pat_src: PatternSource, bindings: &mut SmallVec<[(PatBoundCtx, FxHashSet<Ident>); 1]>, ) { <ins> let is_tuple_struct_pat = matches!(pat.kind, PatKind::TupleStruct(_, _)); </ins> // Visit all direct subpatterns of this pattern. pat.walk(&mut |pat| { debug!("resolve_pattern pat={:?} node={:?}", pat, pat.kind); match pat.kind { <del> PatKind::Ident(bmode, ident, ref sub) => { </del> <ins> // In tuple struct patterns ignore the invalid `ident @ ...`. // It will be handled as an error by the AST lowering. PatKind::Ident(bmode, ident, ref sub) if !(is_tuple_struct_pat && sub.as_ref().filter(|p| p.is_rest()).is_some()) => { </ins> // First try to resolve the identifier as some existing entity, // then fall back to a fresh binding. let has_sub = sub.is_some();


<ins> enum E { A(u8, u8), } fn main() { let e = E::A(2, 3); match e { E::A(x @ ..) => {  //~ ERROR `x @` is not allowed in a tuple x //~ ERROR cannot find value `x` in this scope } }; } </ins>


<ins> error[E0425]: cannot find value `x` in this scope --> $DIR/issue-74539.rs:9:13 | LL |             x |             ^ help: a local variable with a similar name exists: `e` error: `x @` is not allowed in a tuple struct --> $DIR/issue-74539.rs:8:14 | LL |         E::A(x @ ..) => { |              ^^^^^^ this is only allowed in slice patterns | = help: remove this and bind each tuple field independently help: if you don't need to use the contents of x, discard the tuple's remaining fields | LL |         E::A(..) => { |              ^^ error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0425`. </ins>


use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use rustc_trait_selection::autoderef::Autoderef; <ins> use std::slice; </ins> impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Type-check `*oprnd_expr` with `oprnd_expr` type-checked already.


} match expr.kind { <del> hir::ExprKind::Index(ref base_expr, ref index_expr) => { // We need to get the final type in case dereferences were needed for the trait // to apply (#72002). let index_expr_ty = self.typeck_results.borrow().expr_ty_adjusted(index_expr); self.convert_place_op_to_mutable( PlaceOp::Index, expr, base_expr, &[index_expr_ty], ); </del> <ins> hir::ExprKind::Index(ref base_expr, ..) => { self.convert_place_op_to_mutable(PlaceOp::Index, expr, base_expr); </ins> } hir::ExprKind::Unary(hir::UnOp::UnDeref, ref base_expr) => { <del> self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr, &[]); </del> <ins> self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr); </ins> } _ => {} }


op: PlaceOp, expr: &hir::Expr<'_>, base_expr: &hir::Expr<'_>, <del> arg_tys: &[Ty<'tcx>], </del> ) { <del> debug!("convert_place_op_to_mutable({:?}, {:?}, {:?}, {:?})", op, expr, base_expr, arg_tys); </del> <ins> debug!("convert_place_op_to_mutable({:?}, {:?}, {:?})", op, expr, base_expr); </ins> if !self.typeck_results.borrow().is_method_call(expr) { debug!("convert_place_op_to_mutable - builtin, nothing to do"); return;


.expect("place op takes something that is not a ref") .ty; <ins> let arg_ty = match op { PlaceOp::Deref => None, PlaceOp::Index => { // We would need to recover the `T` used when we resolve `<_ as Index<T>>::index` // in try_index_step. This is the subst at index 1. // // Note: we should *not* use `expr_ty` of index_expr here because autoderef // during coercions can cause type of index_expr to differ from `T` (#72002). // We also could not use `expr_ty_adjusted` of index_expr because reborrowing // during coercions can also cause type of index_expr to differ from `T`, // which can potentially cause regionck failure (#74933). Some(self.typeck_results.borrow().node_substs(expr.hir_id).type_at(1)) } }; let arg_tys = match arg_ty { None => &[], Some(ref ty) => slice::from_ref(ty), }; </ins> let method = self.try_mutable_overloaded_place_op(expr.span, base_ty, arg_tys, op); let method = match method { Some(ok) => self.register_infer_ok_obligations(ok),


<ins> // check-pass // // rust-lang/rust#74933: Lifetime error when indexing with borrowed index use std::ops::{Index, IndexMut}; struct S(V); struct K<'a>(&'a ()); struct V; impl<'a> Index<&'a K<'a>> for S { type Output = V; fn index(&self, _: &'a K<'a>) -> &V { &self.0 } } impl<'a> IndexMut<&'a K<'a>> for S { fn index_mut(&mut self, _: &'a K<'a>) -> &mut V { &mut self.0 } } impl V { fn foo(&mut self) {} } fn test(s: &mut S, k: &K<'_>) { s[k] = V; s[k].foo(); } fn main() { let mut s = S(V); let k = K(&()); test(&mut s, &k); } </ins>


) = binding.kind { let def_id = (&*self).parent(ctor_def_id).expect("no parent for a constructor"); <del> if let Some(fields) = self.field_names.get(&def_id) { let first_field = fields.first().expect("empty field list in the map"); return Some(fields.iter().fold(first_field.span, |acc, field| acc.to(field.span))); } </del> <ins> let fields = self.field_names.get(&def_id)?; let first_field = fields.first()?; // Handle `struct Foo()` return Some(fields.iter().fold(first_field.span, |acc, field| acc.to(field.span))); </ins> } None }


<ins> // Regression test for issue #75062 // Tests that we don't ICE on a privacy error for a fieldless tuple struct. mod foo { struct Bar(); } fn main() { foo::Bar(); //~ ERROR tuple struct } </ins>


<ins> error[E0603]: tuple struct `Bar` is private --> $DIR/issue-75062-fieldless-tuple-struct.rs:9:10 | LL |     foo::Bar(); |          ^^^ private tuple struct | note: the tuple struct `Bar` is defined here --> $DIR/issue-75062-fieldless-tuple-struct.rs:5:5 | LL |     struct Bar(); |     ^^^^^^^^^^^^^ error: aborting due to previous error For more information about this error, try `rustc --explain E0603`. </ins>


TyKind::Slice(ref ty) => Slice(box ty.clean(cx)), TyKind::Array(ref ty, ref length) => { let def_id = cx.tcx.hir().local_def_id(length.hir_id); <del> let length = match cx.tcx.const_eval_poly(def_id.to_def_id()) { Ok(length) => { print_const(cx, ty::Const::from_value(cx.tcx, length, cx.tcx.types.usize)) } Err(_) => cx .sess() .source_map() .span_to_snippet(cx.tcx.def_span(def_id)) .unwrap_or_else(|_| "_".to_string()), }; </del> <ins> // NOTE(min_const_generics): We can't use `const_eval_poly` for constants // as we currently do not supply the parent generics to anonymous constants // but do allow `ConstKind::Param`. // // `const_eval_poly` tries to to first substitute generic parameters which // results in an ICE while manually constructing the constant and using `eval` // does nothing for `ConstKind::Param`. let ct = ty::Const::from_anon_const(cx.tcx, def_id); let param_env = cx.tcx.param_env(def_id); let length = print_const(cx, ct.eval(cx.tcx, param_env)); </ins> Array(box ty.clean(cx), length) } TyKind::Tup(ref tys) => Tuple(tys.clean(cx)),


<ins> // ignore-tidy-linelength #![feature(min_const_generics)] #![crate_name = "foo"] // @has foo/type.CellIndex.html '//pre[@class="rust typedef"]' 'type CellIndex<const D: usize> = [i64; D];' pub type CellIndex<const D: usize> = [i64; D]; </ins>


use crate::{ cell::{Cell, UnsafeCell}, fmt, <ins> marker::PhantomData, </ins> mem::{self, MaybeUninit}, ops::{Deref, Drop}, panic::{RefUnwindSafe, UnwindSafe},


once: Once, // Whether or not the value is initialized is tracked by `state_and_queue`. value: UnsafeCell<MaybeUninit<T>>, <ins> /// `PhantomData` to make sure dropck understands we're dropping T in our Drop impl. /// /// ```compile_fail,E0597 /// #![feature(once_cell)] /// /// use std::lazy::SyncOnceCell; /// /// struct A<'a>(&'a str); /// /// impl<'a> Drop for A<'a> { ///     fn drop(&mut self) {} /// } /// /// let cell = SyncOnceCell::new(); /// { ///     let s = String::new(); ///     let _ = cell.set(A(&s)); /// } /// ``` _marker: PhantomData<T>, </ins> } // Why do we need `T: Send`?


/// Creates a new empty cell. #[unstable(feature = "once_cell", issue = "74465")] pub const fn new() -> SyncOnceCell<T> { <del> SyncOnceCell { once: Once::new(), value: UnsafeCell::new(MaybeUninit::uninit()) } </del> <ins> SyncOnceCell { once: Once::new(), value: UnsafeCell::new(MaybeUninit::uninit()), _marker: PhantomData, } </ins> } /// Gets the reference to the underlying value.


self.index = index; } <del> pub fn look_ahead(&self, n: usize) -> Option<&TokenTree> { self.stream.0[self.index..].get(n).map(|(tree, _)| tree) </del> <ins> pub fn look_ahead(&self, n: usize) -> Option<TokenTree> { self.stream.0[self.index..].get(n).map(|(tree, _)| tree.clone()) </ins> } }


} } <del> impl FromInternal<( TreeAndJoint, Option<&'_ tokenstream::TokenTree>, &'_ ParseSess, &'_ mut Vec<Self>, )> for TokenTree<Group, Punct, Ident, Literal> </del> <ins> impl FromInternal<(TreeAndJoint, &'_ ParseSess, &'_ mut Vec<Self>)> for TokenTree<Group, Punct, Ident, Literal> </ins> { fn from_internal( <del> ((tree, is_joint), look_ahead, sess, stack): ( TreeAndJoint, Option<&tokenstream::TokenTree>, &ParseSess, &mut Vec<Self>, ), </del> <ins> ((tree, is_joint), sess, stack): (TreeAndJoint, &ParseSess, &mut Vec<Self>), </ins> ) -> Self { use rustc_ast::token::*; <del> let joint = is_joint == Joint && matches!(look_ahead, Some(tokenstream::TokenTree::Token(t)) if t.is_op()); </del> <ins> let joint = is_joint == Joint; </ins> let Token { kind, span } = match tree { tokenstream::TokenTree::Delimited(span, delim, tts) => { let delimiter = Delimiter::from_internal(delim);


loop { let tree = iter.stack.pop().or_else(|| { let next = iter.cursor.next_with_joint()?; <del> let lookahead = iter.cursor.look_ahead(0); Some(TokenTree::from_internal((next, lookahead, self.sess, &mut iter.stack))) </del> <ins> Some(TokenTree::from_internal((next, self.sess, &mut iter.stack))) </ins> })?; // A hack used to pass AST fragments to attribute and derive macros // as a single nonterminal token instead of a token stream.


} _ => { let tt = TokenTree::Token(self.token.take()); <del> let is_joint = self.bump(); </del> <ins> let mut is_joint = self.bump(); if !self.token.is_op() { is_joint = NonJoint; } </ins> Ok((tt, is_joint)) } }


} let frame = &self.token_cursor.frame; <del> match frame.tree_cursor.look_ahead(dist - 1) { </del> <ins> looker(&match frame.tree_cursor.look_ahead(dist - 1) { </ins> Some(tree) => match tree { <del> TokenTree::Token(token) => looker(token), </del> <ins> TokenTree::Token(token) => token, </ins> TokenTree::Delimited(dspan, delim, _) => { <del> looker(&Token::new(token::OpenDelim(delim.clone()), dspan.open)) </del> <ins> Token::new(token::OpenDelim(delim), dspan.open) </ins> } }, <del> None => looker(&Token::new(token::CloseDelim(frame.delim), frame.span.close)), } </del> <ins> None => Token::new(token::CloseDelim(frame.delim), frame.span.close), }) </ins> } /// Returns whether any of the given keywords are `dist` tokens ahead of the current one.


#![cfg_attr(bootstrap, feature(doc_spotlight))] #![cfg_attr(not(bootstrap), feature(doc_notable_trait))] #![feature(duration_consts_2)] <del> #![feature(duration_saturating_ops)] </del> #![feature(extended_key_value_attributes)] #![feature(extern_types)] #![feature(fundamental)]


/// # Examples /// /// ``` <del> /// #![feature(duration_saturating_ops)] </del> /// #![feature(duration_constants)] /// use std::time::Duration; /// /// assert_eq!(Duration::new(0, 0).saturating_add(Duration::new(0, 1)), Duration::new(0, 1)); /// assert_eq!(Duration::new(1, 0).saturating_add(Duration::new(u64::MAX, 0)), Duration::MAX); /// ``` <del> #[unstable(feature = "duration_saturating_ops", issue = "76416")] </del> <ins> #[stable(feature = "duration_saturating_ops", since = "1.53.0")] </ins> #[inline] #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] pub const fn saturating_add(self, rhs: Duration) -> Duration {


/// # Examples /// /// ``` <del> /// #![feature(duration_saturating_ops)] </del> /// #![feature(duration_zero)] /// use std::time::Duration; /// /// assert_eq!(Duration::new(0, 1).saturating_sub(Duration::new(0, 0)), Duration::new(0, 1)); /// assert_eq!(Duration::new(0, 0).saturating_sub(Duration::new(0, 1)), Duration::ZERO); /// ``` <del> #[unstable(feature = "duration_saturating_ops", issue = "76416")] </del> <ins> #[stable(feature = "duration_saturating_ops", since = "1.53.0")] </ins> #[inline] #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] pub const fn saturating_sub(self, rhs: Duration) -> Duration {


/// # Examples /// /// ``` <del> /// #![feature(duration_saturating_ops)] </del> /// #![feature(duration_constants)] /// use std::time::Duration; /// /// assert_eq!(Duration::new(0, 500_000_001).saturating_mul(2), Duration::new(1, 2)); /// assert_eq!(Duration::new(u64::MAX - 1, 0).saturating_mul(2), Duration::MAX); /// ``` <del> #[unstable(feature = "duration_saturating_ops", issue = "76416")] </del> <ins> #[stable(feature = "duration_saturating_ops", since = "1.53.0")] </ins> #[inline] #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] pub const fn saturating_mul(self, rhs: u32) -> Duration {


#![feature(div_duration)] #![feature(duration_consts_2)] #![feature(duration_constants)] <del> #![feature(duration_saturating_ops)] </del> #![feature(duration_zero)] #![feature(exact_size_is_empty)] #![feature(extern_types)]


_marker: PhantomData<&'a mut T>, } <ins> unsafe impl<'a, T> Sync for DormantMutRef<'a, T> where &'a mut T: Sync {} unsafe impl<'a, T> Send for DormantMutRef<'a, T> where &'a mut T: Send {} </ins> impl<'a, T> DormantMutRef<'a, T> { /// Capture a unique borrow, and immediately reborrow it. For the compiler, /// the lifetime of the new reference is the same as the lifetime of the


} #[test] <ins> #[allow(dead_code)] fn test_sync() { fn map<T: Sync>(v: &BTreeMap<T, T>) -> impl Sync + '_ { v } fn into_iter<T: Sync>(v: BTreeMap<T, T>) -> impl Sync { v.into_iter() } fn into_keys<T: Sync + Ord>(v: BTreeMap<T, T>) -> impl Sync { v.into_keys() } fn into_values<T: Sync + Ord>(v: BTreeMap<T, T>) -> impl Sync { v.into_values() } fn drain_filter<T: Sync + Ord>(v: &mut BTreeMap<T, T>) -> impl Sync + '_ { v.drain_filter(|_, _| false) } fn iter<T: Sync>(v: &BTreeMap<T, T>) -> impl Sync + '_ { v.iter() } fn iter_mut<T: Sync>(v: &mut BTreeMap<T, T>) -> impl Sync + '_ { v.iter_mut() } fn keys<T: Sync>(v: &BTreeMap<T, T>) -> impl Sync + '_ { v.keys() } fn values<T: Sync>(v: &BTreeMap<T, T>) -> impl Sync + '_ { v.values() } fn values_mut<T: Sync>(v: &mut BTreeMap<T, T>) -> impl Sync + '_ { v.values_mut() } fn range<T: Sync + Ord>(v: &BTreeMap<T, T>) -> impl Sync + '_ { v.range(..) } fn range_mut<T: Sync + Ord>(v: &mut BTreeMap<T, T>) -> impl Sync + '_ { v.range_mut(..) } fn entry<T: Sync + Ord + Default>(v: &mut BTreeMap<T, T>) -> impl Sync + '_ { v.entry(Default::default()) } fn occupied_entry<T: Sync + Ord + Default>(v: &mut BTreeMap<T, T>) -> impl Sync + '_ { match v.entry(Default::default()) { Occupied(entry) => entry, _ => unreachable!(), } } fn vacant_entry<T: Sync + Ord + Default>(v: &mut BTreeMap<T, T>) -> impl Sync + '_ { match v.entry(Default::default()) { Vacant(entry) => entry, _ => unreachable!(), } } } #[test] #[allow(dead_code)] fn test_send() { fn map<T: Send>(v: BTreeMap<T, T>) -> impl Send { v } fn into_iter<T: Send>(v: BTreeMap<T, T>) -> impl Send { v.into_iter() } fn into_keys<T: Send + Ord>(v: BTreeMap<T, T>) -> impl Send { v.into_keys() } fn into_values<T: Send + Ord>(v: BTreeMap<T, T>) -> impl Send { v.into_values() } fn drain_filter<T: Send + Ord>(v: &mut BTreeMap<T, T>) -> impl Send + '_ { v.drain_filter(|_, _| false) } fn iter<T: Send + Sync>(v: &BTreeMap<T, T>) -> impl Send + '_ { v.iter() } fn iter_mut<T: Send + Sync>(v: &mut BTreeMap<T, T>) -> impl Send + '_ { v.iter_mut() } fn keys<T: Send + Sync>(v: &BTreeMap<T, T>) -> impl Send + '_ { v.keys() } fn values<T: Send + Sync>(v: &BTreeMap<T, T>) -> impl Send + '_ { v.values() } fn values_mut<T: Send + Sync>(v: &mut BTreeMap<T, T>) -> impl Send + '_ { v.values_mut() } fn range<T: Send + Sync + Ord>(v: &BTreeMap<T, T>) -> impl Send + '_ { v.range(..) } fn range_mut<T: Send + Sync + Ord>(v: &mut BTreeMap<T, T>) -> impl Send + '_ { v.range_mut(..) } fn entry<T: Send + Ord + Default>(v: &mut BTreeMap<T, T>) -> impl Send + '_ { v.entry(Default::default()) } fn occupied_entry<T: Send + Ord + Default>(v: &mut BTreeMap<T, T>) -> impl Send + '_ { match v.entry(Default::default()) { Occupied(entry) => entry, _ => unreachable!(), } } fn vacant_entry<T: Send + Ord + Default>(v: &mut BTreeMap<T, T>) -> impl Send + '_ { match v.entry(Default::default()) { Vacant(entry) => entry, _ => unreachable!(), } } } #[test] </ins> fn test_occupied_entry_key() { let mut a = BTreeMap::new(); let key = "hello there";


<del> //! Note: most of the tests relevant to this file can be found (at the time of writing) in //! src/tests/ui/pattern/usefulness. </del> <ins> //! Note: tests specific to this file can be found in: //!     - ui/pattern/usefulness //!     - ui/or-patterns //!     - ui/consts/const_in_pattern //!     - ui/rfc-2008-non-exhaustive //!     - probably many others //! I (Nadrieril) prefer to put new tests in `ui/pattern/usefulness` unless there's a specific //! reason not to, for example if they depend on a particular feature like or_patterns. </ins> //! //! This file includes the logic for exhaustiveness and usefulness checking for //! pattern-matching. Specifically, given a list of patterns for a type, we can


#[derive(Clone, Debug)] crate enum Usefulness<'tcx> { <del> /// Carries a list of unreachable subpatterns. Used only in the presence of or-patterns. Useful(Vec<Span>), </del> <ins> /// Carries, for each column in the matrix, a set of sub-branches that have been found to be /// unreachable. Used only in the presence of or-patterns, otherwise it stays empty. Useful(Vec<FxHashSet<Span>>), </ins> /// Carries a list of witnesses of non-exhaustiveness. UsefulWithWitness(Vec<Witness<'tcx>>), NotUseful,


}; UsefulWithWitness(new_witnesses) } <ins> Useful(mut unreachables) => { if !unreachables.is_empty() { // When we apply a constructor, there are `arity` columns of the matrix that // corresponded to its arguments. All the unreachables found in these columns // will, after `apply`, come from the first column. So we take the union of all // the corresponding sets and put them in the first column. // Note that `arity` may be 0, in which case we just push a new empty set. let len = unreachables.len(); let arity = ctor_wild_subpatterns.len(); let mut unioned = FxHashSet::default(); for set in unreachables.drain((len - arity)..) { unioned.extend(set) } unreachables.push(unioned); } Useful(unreachables) } </ins> x => x, } }


// If the first pattern is an or-pattern, expand it. if let Some(vs) = v.expand_or_pat() { <del> // We need to push the already-seen patterns into the matrix in order to detect redundant // branches like `Some(_) | Some(0)`. We also keep track of the unreachable subpatterns. let mut matrix = matrix.clone(); // `Vec` of all the unreachable branches of the current or-pattern. let mut unreachable_branches = Vec::new(); // Subpatterns that are unreachable from all branches. E.g. in the following case, the last // `true` is unreachable only from one branch, so it is overall reachable. </del> <ins> // We expand the or pattern, trying each of its branches in turn and keeping careful track // of possible unreachable sub-branches. // // If two branches have detected some unreachable sub-branches, we need to be careful. If // they were detected in columns that are not the current one, we want to keep only the // sub-branches that were unreachable in _all_ branches. Eg. in the following, the last // `true` is unreachable in the second branch of the first or-pattern, but not otherwise. // Therefore we don't want to lint that it is unreachable. </ins> // // ``` // match (true, true) {


//     (false | true, false | true) => {} // } // ``` <del> let mut unreachable_subpats = FxHashSet::default(); // Whether any branch at all is useful. </del> <ins> // If however the sub-branches come from the current column, they come from the inside of // the current or-pattern, and we want to keep them all. Eg. in the following, we _do_ want // to lint that the last `false` is unreachable. // ``` // match None { //     Some(false) => {} //     None | Some(true | false) => {} // } // ``` let mut matrix = matrix.clone(); // We keep track of sub-branches separately depending on whether they come from this column // or from others. let mut unreachables_this_column: FxHashSet<Span> = FxHashSet::default(); let mut unreachables_other_columns: Vec<FxHashSet<Span>> = Vec::default(); // Whether at least one branch is reachable. </ins> let mut any_is_useful = false; for v in vs { let res = is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false); match res { <del> Useful(pats) => { if !any_is_useful { any_is_useful = true; // Initialize with the first set of unreachable subpatterns encountered. unreachable_subpats = pats.into_iter().collect(); } else { // Keep the patterns unreachable from both this and previous branches. unreachable_subpats = pats.into_iter().filter(|p| unreachable_subpats.contains(p)).collect(); </del> <ins> Useful(unreachables) => { if let Some((this_column, other_columns)) = unreachables.split_last() { // We keep the union of unreachables found in the first column. unreachables_this_column.extend(this_column); // We keep the intersection of unreachables found in other columns. if unreachables_other_columns.is_empty() { unreachables_other_columns = other_columns.to_vec(); } else { unreachables_other_columns = unreachables_other_columns .into_iter() .zip(other_columns) .map(|(x, y)| x.intersection(&y).copied().collect()) .collect(); } </ins> } <ins> any_is_useful = true; </ins> } <del> NotUseful => unreachable_branches.push(v.head().span), UsefulWithWitness(_) => { bug!("Encountered or-pat in `v` during exhaustiveness checking") </del> <ins> NotUseful => { unreachables_this_column.insert(v.head().span); </ins> } <ins> UsefulWithWitness(_) => bug!( "encountered or-pat in the expansion of `_` during exhaustiveness checking" ), </ins> } <del> // If pattern has a guard don't add it to the matrix </del> <ins> // If pattern has a guard don't add it to the matrix. </ins> if !is_under_guard { <ins> // We push the already-seen patterns into the matrix in order to detect redundant // branches like `Some(_) | Some(0)`. </ins> matrix.push(v); } } <del> if any_is_useful { // Collect all the unreachable patterns. unreachable_branches.extend(unreachable_subpats); return Useful(unreachable_branches); </del> <ins> return if any_is_useful { let mut unreachables = if unreachables_other_columns.is_empty() { let n_columns = v.len(); (0..n_columns - 1).map(|_| FxHashSet::default()).collect() } else { unreachables_other_columns }; unreachables.push(unreachables_this_column); Useful(unreachables) </ins> } else { <del> return NotUseful; } </del> <ins> NotUseful }; </ins> } // FIXME(Nadrieril): Hack to work around type normalization issues (see #72476).


hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {} } } <del> Useful(unreachable_subpatterns) => { for span in unreachable_subpatterns { </del> <ins> Useful(unreachables) => { let mut unreachables: Vec<_> = unreachables.into_iter().flatten().collect(); // Emit lints in the order in which they occur in the file. unreachables.sort_unstable(); for span in unreachables { </ins> unreachable_pattern(cx.tcx, span, id, None); } }


(false | true, false | true) => {} } match (true, true) { <del> (true, false) => {} (false, true) => {} </del> <ins> (true, true) => {} (false, false) => {} </ins> (false | true, false | true) => {} } <ins> // https://github.com/rust-lang/rust/issues/76836 match None { Some(false) => {} None | Some(true | false) => {} //~ ERROR unreachable } </ins> // A subpattern that is unreachable in all branches is overall unreachable. match (true, true) { (false, true) => {}


|              ^ error: unreachable pattern <del> --> $DIR/exhaustiveness-unreachable-pattern.rs:89:15 </del> <ins> --> $DIR/exhaustiveness-unreachable-pattern.rs:88:19 | LL |                 | false) => {} |                   ^^^^^ error: unreachable pattern --> $DIR/exhaustiveness-unreachable-pattern.rs:96:15 </ins> | LL |             | true) => {} |               ^^^^ error: unreachable pattern <del> --> $DIR/exhaustiveness-unreachable-pattern.rs:95:15 </del> <ins> --> $DIR/exhaustiveness-unreachable-pattern.rs:102:15 </ins> | LL |             | true, |               ^^^^ <del> error: aborting due to 18 previous errors </del> <ins> error: aborting due to 19 previous errors </ins>


} /// Attribute macro applied to a static to register it as a global allocator. <ins> /// /// See also [`std::alloc::GlobalAlloc`](../std/alloc/trait.GlobalAlloc.html). </ins> #[stable(feature = "global_allocator", since = "1.28.0")] #[allow_internal_unstable(rustc_attrs)] #[rustc_builtin_macro]


// and about 2 * (len1 + len2) comparisons in the worst case // while `extend` takes O(len2 * log(len1)) operations // and about 1 * len2 * log_2(len1) comparisons in the worst case, <del> // assuming len1 >= len2. </del> <ins> // assuming len1 >= len2. For larger heaps, the crossover point // no longer follows this reasoning and was determined empirically. </ins> #[inline] fn better_to_rebuild(len1: usize, len2: usize) -> bool { <del> 2 * (len1 + len2) < len2 * log2_fast(len1) </del> <ins> let tot_len = len1 + len2; if tot_len <= 2048 { 2 * tot_len < len2 * log2_fast(len1) } else { 2 * tot_len < len2 * 11 } </ins> } if better_to_rebuild(self.len(), other.len()) {


} } hir::InlineAsmOperand::InOut { expr, .. } => { <del> succ = self.write_place(expr, succ, ACC_READ | ACC_WRITE); </del> <ins> succ = self.write_place(expr, succ, ACC_READ | ACC_WRITE | ACC_USE); </ins> } hir::InlineAsmOperand::SplitInOut { out_expr, .. } => { if let Some(expr) = out_expr {


<ins> // Ensure inout asm! operands are marked as used by the liveness pass // only-x86_64 // check-pass #![feature(asm)] #![allow(dead_code)] #![warn(unused_assignments)] #![warn(unused_variables)] // Test the single inout case unsafe fn f1(mut src: *const u8) { asm!("/*{0}*/", inout(reg) src); //~ WARN value assigned to `src` is never read } unsafe fn f2(mut src: *const u8) -> *const u8 { asm!("/*{0}*/", inout(reg) src); src } // Test the split inout case unsafe fn f3(mut src: *const u8) { asm!("/*{0}*/", inout(reg) src => src); //~ WARN value assigned to `src` is never read } unsafe fn f4(mut src: *const u8) -> *const u8 { asm!("/*{0}*/", inout(reg) src => src); src } // Tests the use of field projections struct S { field: *mut u8, } unsafe fn f5(src: &mut S) { asm!("/*{0}*/", inout(reg) src.field); } unsafe fn f6(src: &mut S) { asm!("/*{0}*/", inout(reg) src.field => src.field); } fn main() {} </ins>


<ins> warning: value assigned to `src` is never read --> $DIR/liveness-asm.rs:13:32 | LL |     asm!("/*{0}*/", inout(reg) src); |                                ^^^ | note: the lint level is defined here --> $DIR/liveness-asm.rs:8:9 | LL | #![warn(unused_assignments)] |         ^^^^^^^^^^^^^^^^^^ = help: maybe it is overwritten before being read? warning: value assigned to `src` is never read --> $DIR/liveness-asm.rs:23:39 | LL |     asm!("/*{0}*/", inout(reg) src => src); |                                       ^^^ | = help: maybe it is overwritten before being read? warning: 2 warnings emitted </ins>


let func_ty = func.ty(body, tcx); if let ty::FnDef(callee, substs) = *func_ty.kind() { <del> let (callee, call_substs) = if let Ok(Some(instance)) = Instance::resolve(tcx, param_env, callee, substs) { (instance.def_id(), instance.substs) } else { (callee, substs) }; </del> <ins> let normalized_substs = tcx.normalize_erasing_regions(param_env, substs); let (callee, call_substs) = if let Ok(Some(instance)) = Instance::resolve(tcx, param_env, callee, normalized_substs) { (instance.def_id(), instance.substs) } else { (callee, normalized_substs) }; </ins> // FIXME(#57965): Make this work across function boundaries


impl<T> Drop for InPlaceDrop<T> { #[inline] fn drop(&mut self) { <del> if mem::needs_drop::<T>() { unsafe { ptr::drop_in_place(slice::from_raw_parts_mut(self.inner, self.len())); } </del> <ins> unsafe { ptr::drop_in_place(slice::from_raw_parts_mut(self.inner, self.len())); </ins> } } }


} fn drop_remaining(&mut self) { <del> if mem::needs_drop::<T>() { unsafe { ptr::drop_in_place(self.as_mut_slice()); } </del> <ins> unsafe { ptr::drop_in_place(self.as_mut_slice()); </ins> } self.ptr = self.end; }


<del> Subproject commit af078ecc0b069ec594982f92d4c6c58af99efbb5 </del> <ins> Subproject commit 710fc18ddcb6c7677b3c96359abb35da37f2a488 </ins>


code_offset: usize, margin: Margin, ) { <ins> // Tabs are assumed to have been replaced by spaces in calling code. assert!(!source_string.contains('t')); </ins> let line_len = source_string.len(); // Create the source line we will highlight. let left = margin.left(line_len);


} let source_string = match file.get_line(line.line_index - 1) { <del> Some(s) => s, </del> <ins> Some(s) => replace_tabs(&*s), </ins> None => return Vec::new(), };


let file = annotated_file.file.clone(); let line = &annotated_file.lines[line_idx]; if let Some(source_string) = file.get_line(line.line_index - 1) { <del> let leading_whitespace = source_string.chars().take_while(|c| c.is_whitespace()).count(); </del> <ins> let leading_whitespace = source_string .chars() .take_while(|c| c.is_whitespace()) .map(|c| { match c { // Tabs are displayed as 4 spaces 't' => 4, _ => 1, } }) .sum(); </ins> if source_string.chars().any(|c| !c.is_whitespace()) { whitespace_margin = min(whitespace_margin, leading_whitespace); }


self.draw_line( &mut buffer, <del> &unannotated_line, </del> <ins> &replace_tabs(&unannotated_line), </ins> annotated_file.lines[line_idx + 1].line_index - 1, last_buffer_line_num, width_offset,


); // print the suggestion draw_col_separator(&mut buffer, row_num, max_line_num_len + 1); <del> buffer.append(row_num, line, Style::NoStyle); </del> <ins> buffer.append(row_num, &replace_tabs(line), Style::NoStyle); </ins> row_num += 1; }


} } <ins> fn replace_tabs(str: &str) -> String { str.replace('t', "    ") } </ins> fn draw_col_separator(buffer: &mut StyledBuffer, line: usize, col: usize) { buffer.puts(line, col, "| ", Style::LineNumber); }


StyledBuffer { text: vec![], styles: vec![] } } <del> fn replace_tabs(&mut self) { for (line_pos, line) in self.text.iter_mut().enumerate() { let mut tab_pos = vec![]; for (pos, c) in line.iter().enumerate() { if *c == 't' { tab_pos.push(pos); } } // start with the tabs at the end of the line to replace them with 4 space chars for pos in tab_pos.iter().rev() { assert_eq!(line.remove(*pos), 't'); // fix the position of the style to match up after replacing the tabs let s = self.styles[line_pos].remove(*pos); for _ in 0..4 { line.insert(*pos, ' '); self.styles[line_pos].insert(*pos, s); } } } } </del> <ins> pub fn render(&self) -> Vec<Vec<StyledString>> { // Tabs are assumed to have been replaced by spaces in calling code. assert!(self.text.iter().all(|r| !r.contains(&'t'))); </ins> <del> pub fn render(&mut self) -> Vec<Vec<StyledString>> { </del> let mut output: Vec<Vec<StyledString>> = vec![]; let mut styled_vec: Vec<StyledString> = vec![]; <del> // before we render, replace tabs with spaces self.replace_tabs(); </del> for (row, row_style) in self.text.iter().zip(&self.styles) { let mut current_style = Style::NoStyle; let mut current_text = String::new();


<ins> // Test for #78438: ensure underline alignment with many tabs on the left, long line on the right // ignore-tidy-linelength // ignore-tidy-tab fn main() { let money = 42i32; match money { v @ 1 | 2 | 3 => panic!("You gave me too little money {}", v), // Long text here: TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT //~^ ERROR variable `v` is not bound in all patterns v => println!("Enough money {}", v), } } </ins>


<ins> error[E0408]: variable `v` is not bound in all patterns --> $DIR/tabs-trimming.rs:9:16 | LL | ...   v @ 1 | 2 | 3 => panic!("You gave me too little money {}", v), // Long text here: TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT... |       -       ^   ^ pattern doesn't bind `v` |       |       | |       |       pattern doesn't bind `v` |       variable not in all patterns error: aborting due to previous error For more information about this error, try `rustc --explain E0408`. </ins>


return None; } <ins> // when the second place is a projection of the first one, it's not safe to calculate their discriminant values sequentially. // for example, this should not be optimized: // // ```rust // enum E<'a> { Empty, Some(&'a E<'a>), } // let Some(Some(_)) = e; // ``` // // ```mir // bb0: { //   _2 = discriminant(*_1) //   switchInt(_2) -> [...] // } // bb1: { //   _3 = discriminant(*(((*_1) as Some).0: &E)) //   switchInt(_3) -> [...] // } // ``` let discr_place = discr_info.place_of_adt_discr_read; let this_discr_place = this_bb_discr_info.place_of_adt_discr_read; if discr_place.local == this_discr_place.local && this_discr_place.projection.starts_with(discr_place.projection) { trace!("NO: one target is the projection of another"); return None; } </ins> // if we reach this point, the optimization applies, and we should be able to optimize this case // store the info that is needed to apply the optimization


<ins> // run-pass // compile-flags: -Z mir-opt-level=2 -C opt-level=0 // example from #78496 pub enum E<'a> { Empty, Some(&'a E<'a>), } fn f(e: &E) -> u32 { if let E::Some(E::Some(_)) = e { 1 } else { 2 } } fn main() { assert_eq!(f(&E::Empty), 2); } </ins>


<ins> // check-pass #![feature(type_alias_impl_trait)] pub trait Trait { type A; fn f() -> Self::A; } pub trait Tr2<'a, 'b> {} pub struct A<T>(T); pub trait Tr { type B; } impl<'a, 'b, T: Tr<B = dyn Tr2<'a, 'b>>> Trait for A<T> { type A = impl core::fmt::Debug; fn f() -> Self::A {} } fn main() {} </ins>


pub mod simplify; mod simplify_branches; mod simplify_comparison_integral; <del> mod simplify_try; </del> mod sroa; mod uninhabited_enum_branching; mod unreachable_prop;


&o1(simplify_branches::SimplifyConstCondition::new("after-const-prop")), &early_otherwise_branch::EarlyOtherwiseBranch, &simplify_comparison_integral::SimplifyComparisonIntegral, <del> &simplify_try::SimplifyArmIdentity, &simplify_try::SimplifyBranchSame, </del> &dead_store_elimination::DeadStoreElimination, &dest_prop::DestinationPropagation, &o1(simplify_branches::SimplifyConstCondition::new("final")),


<del> //! The general point of the optimizations provided here is to simplify something like: //! //! ```rust //! # fn foo<T, E>(x: Result<T, E>) -> Result<T, E> { //! match x { //!     Ok(x) => Ok(x), //!     Err(x) => Err(x) //! } //! # } //! ``` //! //! into just `x`. use crate::{simplify, MirPass}; use itertools::Itertools as _; use rustc_index::{bit_set::BitSet, vec::IndexVec}; use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::{self, List, Ty, TyCtxt}; use rustc_target::abi::VariantIdx; use std::iter::{once, Enumerate, Peekable}; use std::slice::Iter; /// Simplifies arms of form `Variant(x) => Variant(x)` to just a move. /// /// This is done by transforming basic blocks where the statements match: /// /// ```ignore (MIR) /// _LOCAL_TMP = ((_LOCAL_1 as Variant ).FIELD: TY ); /// _TMP_2 = _LOCAL_TMP; /// ((_LOCAL_0 as Variant).FIELD: TY) = move _TMP_2; /// discriminant(_LOCAL_0) = VAR_IDX; /// ``` /// /// into: /// /// ```ignore (MIR) /// _LOCAL_0 = move _LOCAL_1 /// ``` pub struct SimplifyArmIdentity; #[derive(Debug)] struct ArmIdentityInfo<'tcx> { /// Storage location for the variant's field local_temp_0: Local, /// Storage location holding the variant being read from local_1: Local, /// The variant field being read from vf_s0: VarField<'tcx>, /// Index of the statement which loads the variant being read get_variant_field_stmt: usize, /// Tracks each assignment to a temporary of the variant's field field_tmp_assignments: Vec<(Local, Local)>, /// Storage location holding the variant's field that was read from local_tmp_s1: Local, /// Storage location holding the enum that we are writing to local_0: Local, /// The variant field being written to vf_s1: VarField<'tcx>, /// Storage location that the discriminant is being written to set_discr_local: Local, /// The variant being written set_discr_var_idx: VariantIdx, /// Index of the statement that should be overwritten as a move stmt_to_overwrite: usize, /// SourceInfo for the new move source_info: SourceInfo, /// Indices of matching Storage{Live,Dead} statements encountered. /// (StorageLive index,, StorageDead index, Local) storage_stmts: Vec<(usize, usize, Local)>, /// The statements that should be removed (turned into nops) stmts_to_remove: Vec<usize>, /// Indices of debug variables that need to be adjusted to point to // `{local_0}.{dbg_projection}`. dbg_info_to_adjust: Vec<usize>, /// The projection used to rewrite debug info. dbg_projection: &'tcx List<PlaceElem<'tcx>>, } fn get_arm_identity_info<'a, 'tcx>( stmts: &'a [Statement<'tcx>], locals_count: usize, debug_info: &'a [VarDebugInfo<'tcx>], ) -> Option<ArmIdentityInfo<'tcx>> { // This can't possibly match unless there are at least 3 statements in the block // so fail fast on tiny blocks. if stmts.len() < 3 { return None; } let mut tmp_assigns = Vec::new(); let mut nop_stmts = Vec::new(); let mut storage_stmts = Vec::new(); let mut storage_live_stmts = Vec::new(); let mut storage_dead_stmts = Vec::new(); type StmtIter<'a, 'tcx> = Peekable<Enumerate<Iter<'a, Statement<'tcx>>>>; fn is_storage_stmt(stmt: &Statement<'_>) -> bool { matches!(stmt.kind, StatementKind::StorageLive(_) | StatementKind::StorageDead(_)) } /// Eats consecutive Statements which match `test`, performing the specified `action` for each. /// The iterator `stmt_iter` is not advanced if none were matched. fn try_eat<'a, 'tcx>( stmt_iter: &mut StmtIter<'a, 'tcx>, test: impl Fn(&'a Statement<'tcx>) -> bool, mut action: impl FnMut(usize, &'a Statement<'tcx>), ) { while stmt_iter.peek().map_or(false, |(_, stmt)| test(stmt)) { let (idx, stmt) = stmt_iter.next().unwrap(); action(idx, stmt); } } /// Eats consecutive `StorageLive` and `StorageDead` Statements. /// The iterator `stmt_iter` is not advanced if none were found. fn try_eat_storage_stmts( stmt_iter: &mut StmtIter<'_, '_>, storage_live_stmts: &mut Vec<(usize, Local)>, storage_dead_stmts: &mut Vec<(usize, Local)>, ) { try_eat(stmt_iter, is_storage_stmt, |idx, stmt| { if let StatementKind::StorageLive(l) = stmt.kind { storage_live_stmts.push((idx, l)); } else if let StatementKind::StorageDead(l) = stmt.kind { storage_dead_stmts.push((idx, l)); } }) } fn is_tmp_storage_stmt(stmt: &Statement<'_>) -> bool { use rustc_middle::mir::StatementKind::Assign; if let Assign(box (place, Rvalue::Use(Operand::Copy(p) | Operand::Move(p)))) = &stmt.kind { place.as_local().is_some() && p.as_local().is_some() } else { false } } /// Eats consecutive `Assign` Statements. // The iterator `stmt_iter` is not advanced if none were found. fn try_eat_assign_tmp_stmts( stmt_iter: &mut StmtIter<'_, '_>, tmp_assigns: &mut Vec<(Local, Local)>, nop_stmts: &mut Vec<usize>, ) { try_eat(stmt_iter, is_tmp_storage_stmt, |idx, stmt| { use rustc_middle::mir::StatementKind::Assign; if let Assign(box (place, Rvalue::Use(Operand::Copy(p) | Operand::Move(p)))) = &stmt.kind { tmp_assigns.push((place.as_local().unwrap(), p.as_local().unwrap())); nop_stmts.push(idx); } }) } fn find_storage_live_dead_stmts_for_local( local: Local, stmts: &[Statement<'_>], ) -> Option<(usize, usize)> { trace!("looking for {:?}", local); let mut storage_live_stmt = None; let mut storage_dead_stmt = None; for (idx, stmt) in stmts.iter().enumerate() { if stmt.kind == StatementKind::StorageLive(local) { storage_live_stmt = Some(idx); } else if stmt.kind == StatementKind::StorageDead(local) { storage_dead_stmt = Some(idx); } } Some((storage_live_stmt?, storage_dead_stmt.unwrap_or(usize::MAX))) } // Try to match the expected MIR structure with the basic block we're processing. // We want to see something that looks like: // ``` // (StorageLive(_) | StorageDead(_));* // _LOCAL_INTO = ((_LOCAL_FROM as Variant).FIELD: TY); // (StorageLive(_) | StorageDead(_));* // (tmp_n+1 = tmp_n);* // (StorageLive(_) | StorageDead(_));* // (tmp_n+1 = tmp_n);* // ((LOCAL_FROM as Variant).FIELD: TY) = move tmp; // discriminant(LOCAL_FROM) = VariantIdx; // (StorageLive(_) | StorageDead(_));* // ``` let mut stmt_iter = stmts.iter().enumerate().peekable(); try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts); let (get_variant_field_stmt, stmt) = stmt_iter.next()?; let (local_tmp_s0, local_1, vf_s0, dbg_projection) = match_get_variant_field(stmt)?; try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts); try_eat_assign_tmp_stmts(&mut stmt_iter, &mut tmp_assigns, &mut nop_stmts); try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts); try_eat_assign_tmp_stmts(&mut stmt_iter, &mut tmp_assigns, &mut nop_stmts); let (idx, stmt) = stmt_iter.next()?; let (local_tmp_s1, local_0, vf_s1) = match_set_variant_field(stmt)?; nop_stmts.push(idx); let (idx, stmt) = stmt_iter.next()?; let (set_discr_local, set_discr_var_idx) = match_set_discr(stmt)?; let discr_stmt_source_info = stmt.source_info; nop_stmts.push(idx); try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts); for (live_idx, live_local) in storage_live_stmts { if let Some(i) = storage_dead_stmts.iter().rposition(|(_, l)| *l == live_local) { let (dead_idx, _) = storage_dead_stmts.swap_remove(i); storage_stmts.push((live_idx, dead_idx, live_local)); if live_local == local_tmp_s0 { nop_stmts.push(get_variant_field_stmt); } } } // We sort primitive usize here so we can use unstable sort nop_stmts.sort_unstable(); // Use one of the statements we're going to discard between the point // where the storage location for the variant field becomes live and // is killed. let (live_idx, dead_idx) = find_storage_live_dead_stmts_for_local(local_tmp_s0, stmts)?; let stmt_to_overwrite = nop_stmts.iter().find(|stmt_idx| live_idx < **stmt_idx && **stmt_idx < dead_idx); let mut tmp_assigned_vars = BitSet::new_empty(locals_count); for (l, r) in &tmp_assigns { tmp_assigned_vars.insert(*l); tmp_assigned_vars.insert(*r); } let dbg_info_to_adjust: Vec<_> = debug_info .iter() .enumerate() .filter_map(|(i, var_info)| { if let VarDebugInfoContents::Place(p) = var_info.value { if tmp_assigned_vars.contains(p.local) { return Some(i); } } None }) .collect(); Some(ArmIdentityInfo { local_temp_0: local_tmp_s0, local_1, vf_s0, get_variant_field_stmt, field_tmp_assignments: tmp_assigns, local_tmp_s1, local_0, vf_s1, set_discr_local, set_discr_var_idx, stmt_to_overwrite: *stmt_to_overwrite?, source_info: discr_stmt_source_info, storage_stmts, stmts_to_remove: nop_stmts, dbg_info_to_adjust, dbg_projection, }) } fn optimization_applies<'tcx>( opt_info: &ArmIdentityInfo<'tcx>, local_decls: &IndexVec<Local, LocalDecl<'tcx>>, local_uses: &IndexVec<Local, usize>, var_debug_info: &[VarDebugInfo<'tcx>], ) -> bool { trace!("testing if optimization applies..."); // FIXME(wesleywiser): possibly relax this restriction? if opt_info.local_0 == opt_info.local_1 { trace!("NO: moving into ourselves"); return false; } else if opt_info.vf_s0 != opt_info.vf_s1 { trace!("NO: the field-and-variant information do not match"); return false; } else if local_decls[opt_info.local_0].ty != local_decls[opt_info.local_1].ty { // FIXME(Centril,oli-obk): possibly relax to same layout? trace!("NO: source and target locals have different types"); return false; } else if (opt_info.local_0, opt_info.vf_s0.var_idx) != (opt_info.set_discr_local, opt_info.set_discr_var_idx) { trace!("NO: the discriminants do not match"); return false; } // Verify the assignment chain consists of the form b = a; c = b; d = c; etc... if opt_info.field_tmp_assignments.is_empty() { trace!("NO: no assignments found"); return false; } let mut last_assigned_to = opt_info.field_tmp_assignments[0].1; let source_local = last_assigned_to; for (l, r) in &opt_info.field_tmp_assignments { if *r != last_assigned_to { trace!("NO: found unexpected assignment {:?} = {:?}", l, r); return false; } last_assigned_to = *l; } // Check that the first and last used locals are only used twice // since they are of the form: // // ``` // _first = ((_x as Variant).n: ty); // _n = _first; // ... // ((_y as Variant).n: ty) = _n; // discriminant(_y) = z; // ``` for (l, r) in &opt_info.field_tmp_assignments { if local_uses[*l] != 2 { warn!("NO: FAILED assignment chain local {:?} was used more than twice", l); return false; } else if local_uses[*r] != 2 { warn!("NO: FAILED assignment chain local {:?} was used more than twice", r); return false; } } // Check that debug info only points to full Locals and not projections. for dbg_idx in &opt_info.dbg_info_to_adjust { let dbg_info = &var_debug_info[*dbg_idx]; if let VarDebugInfoContents::Place(p) = dbg_info.value { if !p.projection.is_empty() { trace!("NO: debug info for {:?} had a projection {:?}", dbg_info.name, p); return false; } } } if source_local != opt_info.local_temp_0 { trace!( "NO: start of assignment chain does not match enum variant temp: {:?} != {:?}", source_local, opt_info.local_temp_0 ); return false; } else if last_assigned_to != opt_info.local_tmp_s1 { trace!( "NO: end of assignment chain does not match written enum temp: {:?} != {:?}", last_assigned_to, opt_info.local_tmp_s1 ); return false; } trace!("SUCCESS: optimization applies!"); true } impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // FIXME(77359): This optimization can result in unsoundness. if !tcx.sess.opts.unstable_opts.unsound_mir_opts { return; } let source = body.source; trace!("running SimplifyArmIdentity on {:?}", source); let local_uses = LocalUseCounter::get_local_uses(body); for bb in body.basic_blocks.as_mut() { if let Some(opt_info) = get_arm_identity_info(&bb.statements, body.local_decls.len(), &body.var_debug_info) { trace!("got opt_info = {:#?}", opt_info); if !optimization_applies( &opt_info, &body.local_decls, &local_uses, &body.var_debug_info, ) { debug!("optimization skipped for {:?}", source); continue; } // Also remove unused Storage{Live,Dead} statements which correspond // to temps used previously. for (live_idx, dead_idx, local) in &opt_info.storage_stmts { // The temporary that we've read the variant field into is scoped to this block, // so we can remove the assignment. if *local == opt_info.local_temp_0 { bb.statements[opt_info.get_variant_field_stmt].make_nop(); } for (left, right) in &opt_info.field_tmp_assignments { if local == left || local == right { bb.statements[*live_idx].make_nop(); bb.statements[*dead_idx].make_nop(); } } } // Right shape; transform for stmt_idx in opt_info.stmts_to_remove { bb.statements[stmt_idx].make_nop(); } let stmt = &mut bb.statements[opt_info.stmt_to_overwrite]; stmt.source_info = opt_info.source_info; stmt.kind = StatementKind::Assign(Box::new(( opt_info.local_0.into(), Rvalue::Use(Operand::Move(opt_info.local_1.into())), ))); bb.statements.retain(|stmt| stmt.kind != StatementKind::Nop); // Fix the debug info to point to the right local for dbg_index in opt_info.dbg_info_to_adjust { let dbg_info = &mut body.var_debug_info[dbg_index]; assert!( matches!(dbg_info.value, VarDebugInfoContents::Place(_)), "value was not a Place" ); if let VarDebugInfoContents::Place(p) = &mut dbg_info.value { assert!(p.projection.is_empty()); p.local = opt_info.local_0; p.projection = opt_info.dbg_projection; } } trace!("block is now {:?}", bb.statements); } } } } struct LocalUseCounter { local_uses: IndexVec<Local, usize>, } impl LocalUseCounter { fn get_local_uses(body: &Body<'_>) -> IndexVec<Local, usize> { let mut counter = LocalUseCounter { local_uses: IndexVec::from_elem(0, &body.local_decls) }; counter.visit_body(body); counter.local_uses } } impl Visitor<'_> for LocalUseCounter { fn visit_local(&mut self, local: Local, context: PlaceContext, _location: Location) { if context.is_storage_marker() || context == PlaceContext::NonUse(NonUseContext::VarDebugInfo) { return; } self.local_uses[local] += 1; } } /// Match on: /// ```ignore (MIR) /// _LOCAL_INTO = ((_LOCAL_FROM as Variant).FIELD: TY); /// ``` fn match_get_variant_field<'tcx>( stmt: &Statement<'tcx>, ) -> Option<(Local, Local, VarField<'tcx>, &'tcx List<PlaceElem<'tcx>>)> { match &stmt.kind { StatementKind::Assign(box ( place_into, Rvalue::Use(Operand::Copy(pf) | Operand::Move(pf)), )) => { let local_into = place_into.as_local()?; let (local_from, vf) = match_variant_field_place(*pf)?; Some((local_into, local_from, vf, pf.projection)) } _ => None, } } /// Match on: /// ```ignore (MIR) /// ((_LOCAL_FROM as Variant).FIELD: TY) = move _LOCAL_INTO; /// ``` fn match_set_variant_field<'tcx>(stmt: &Statement<'tcx>) -> Option<(Local, Local, VarField<'tcx>)> { match &stmt.kind { StatementKind::Assign(box (place_from, Rvalue::Use(Operand::Move(place_into)))) => { let local_into = place_into.as_local()?; let (local_from, vf) = match_variant_field_place(*place_from)?; Some((local_into, local_from, vf)) } _ => None, } } /// Match on: /// ```ignore (MIR) /// discriminant(_LOCAL_TO_SET) = VAR_IDX; /// ``` fn match_set_discr(stmt: &Statement<'_>) -> Option<(Local, VariantIdx)> { match &stmt.kind { StatementKind::SetDiscriminant { place, variant_index } => { Some((place.as_local()?, *variant_index)) } _ => None, } } #[derive(PartialEq, Debug)] struct VarField<'tcx> { field: Field, field_ty: Ty<'tcx>, var_idx: VariantIdx, } /// Match on `((_LOCAL as Variant).FIELD: TY)`. fn match_variant_field_place(place: Place<'_>) -> Option<(Local, VarField<'_>)> { match place.as_ref() { PlaceRef { local, projection: &[ProjectionElem::Downcast(_, var_idx), ProjectionElem::Field(field, ty)], } => Some((local, VarField { field, field_ty: ty, var_idx })), _ => None, } } /// Simplifies `SwitchInt(_) -> [targets]`, /// where all the `targets` have the same form, /// into `goto -> target_first`. pub struct SimplifyBranchSame; impl<'tcx> MirPass<'tcx> for SimplifyBranchSame { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // This optimization is disabled by default for now due to // soundness concerns; see issue #89485 and PR #89489. if !tcx.sess.opts.unstable_opts.unsound_mir_opts { return; } trace!("Running SimplifyBranchSame on {:?}", body.source); let finder = SimplifyBranchSameOptimizationFinder { body, tcx }; let opts = finder.find(); let did_remove_blocks = opts.len() > 0; for opt in opts.iter() { trace!("SUCCESS: Applying optimization {:?}", opt); // Replace `SwitchInt(..) -> [bb_first, ..];` with a `goto -> bb_first;`. body.basic_blocks_mut()[opt.bb_to_opt_terminator].terminator_mut().kind = TerminatorKind::Goto { target: opt.bb_to_goto }; } if did_remove_blocks { // We have dead blocks now, so remove those. simplify::remove_dead_blocks(tcx, body); } } } #[derive(Debug)] struct SimplifyBranchSameOptimization { /// All basic blocks are equal so go to this one bb_to_goto: BasicBlock, /// Basic block where the terminator can be simplified to a goto bb_to_opt_terminator: BasicBlock, } struct SwitchTargetAndValue { target: BasicBlock, // None in case of the `otherwise` case value: Option<u128>, } struct SimplifyBranchSameOptimizationFinder<'a, 'tcx> { body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, } impl<'tcx> SimplifyBranchSameOptimizationFinder<'_, 'tcx> { fn find(&self) -> Vec<SimplifyBranchSameOptimization> { self.body .basic_blocks .iter_enumerated() .filter_map(|(bb_idx, bb)| { let (discr_switched_on, targets_and_values) = match &bb.terminator().kind { TerminatorKind::SwitchInt { targets, discr, .. } => { let targets_and_values: Vec<_> = targets.iter() .map(|(val, target)| SwitchTargetAndValue { target, value: Some(val) }) .chain(once(SwitchTargetAndValue { target: targets.otherwise(), value: None })) .collect(); (discr, targets_and_values) }, _ => return None, }; // find the adt that has its discriminant read // assuming this must be the last statement of the block let adt_matched_on = match &bb.statements.last()?.kind { StatementKind::Assign(box (place, rhs)) if Some(*place) == discr_switched_on.place() => { match rhs { Rvalue::Discriminant(adt_place) if adt_place.ty(self.body, self.tcx).ty.is_enum() => adt_place, _ => { trace!("NO: expected a discriminant read of an enum instead of: {:?}", rhs); return None; } } } other => { trace!("NO: expected an assignment of a discriminant read to a place. Found: {:?}", other); return None }, }; let mut iter_bbs_reachable = targets_and_values .iter() .map(|target_and_value| (target_and_value, &self.body.basic_blocks[target_and_value.target])) .filter(|(_, bb)| { // Reaching `unreachable` is UB so assume it doesn't happen. bb.terminator().kind != TerminatorKind::Unreachable }) .peekable(); let bb_first = iter_bbs_reachable.peek().map_or(&targets_and_values[0], |(idx, _)| *idx); let mut all_successors_equivalent = StatementEquality::TrivialEqual; // All successor basic blocks must be equal or contain statements that are pairwise considered equal. for ((target_and_value_l,bb_l), (target_and_value_r,bb_r)) in iter_bbs_reachable.tuple_windows() { let trivial_checks = bb_l.is_cleanup == bb_r.is_cleanup && bb_l.terminator().kind == bb_r.terminator().kind && bb_l.statements.len() == bb_r.statements.len(); let statement_check = || { bb_l.statements.iter().zip(&bb_r.statements).try_fold(StatementEquality::TrivialEqual, |acc,(l,r)| { let stmt_equality = self.statement_equality(*adt_matched_on, &l, target_and_value_l, &r, target_and_value_r); if matches!(stmt_equality, StatementEquality::NotEqual) { // short circuit None } else { Some(acc.combine(&stmt_equality)) } }) .unwrap_or(StatementEquality::NotEqual) }; if !trivial_checks { all_successors_equivalent = StatementEquality::NotEqual; break; } all_successors_equivalent = all_successors_equivalent.combine(&statement_check()); }; match all_successors_equivalent{ StatementEquality::TrivialEqual => { // statements are trivially equal, so just take first trace!("Statements are trivially equal"); Some(SimplifyBranchSameOptimization { bb_to_goto: bb_first.target, bb_to_opt_terminator: bb_idx, }) } StatementEquality::ConsideredEqual(bb_to_choose) => { trace!("Statements are considered equal"); Some(SimplifyBranchSameOptimization { bb_to_goto: bb_to_choose, bb_to_opt_terminator: bb_idx, }) } StatementEquality::NotEqual => { trace!("NO: not all successors of basic block {:?} were equivalent", bb_idx); None } } }) .collect() } /// Tests if two statements can be considered equal /// /// Statements can be trivially equal if the kinds match. /// But they can also be considered equal in the following case A: /// ```ignore (MIR) /// discriminant(_0) = 0;   // bb1 /// _0 = move _1;           // bb2 /// ``` /// In this case the two statements are equal iff /// - `_0` is an enum where the variant index 0 is fieldless, and /// -  bb1 was targeted by a switch where the discriminant of `_1` was switched on fn statement_equality( &self, adt_matched_on: Place<'tcx>, x: &Statement<'tcx>, x_target_and_value: &SwitchTargetAndValue, y: &Statement<'tcx>, y_target_and_value: &SwitchTargetAndValue, ) -> StatementEquality { let helper = |rhs: &Rvalue<'tcx>, place: &Place<'tcx>, variant_index: VariantIdx, switch_value: u128, side_to_choose| { let place_type = place.ty(self.body, self.tcx).ty; let adt = match *place_type.kind() { ty::Adt(adt, _) if adt.is_enum() => adt, _ => return StatementEquality::NotEqual, }; // We need to make sure that the switch value that targets the bb with // SetDiscriminant is the same as the variant discriminant. let variant_discr = adt.discriminant_for_variant(self.tcx, variant_index).val; if variant_discr != switch_value { trace!( "NO: variant discriminant {} does not equal switch value {}", variant_discr, switch_value ); return StatementEquality::NotEqual; } let variant_is_fieldless = adt.variant(variant_index).fields.is_empty(); if !variant_is_fieldless { trace!("NO: variant {:?} was not fieldless", variant_index); return StatementEquality::NotEqual; } match rhs { Rvalue::Use(operand) if operand.place() == Some(adt_matched_on) => { StatementEquality::ConsideredEqual(side_to_choose) } _ => { trace!( "NO: RHS of assignment was {:?}, but expected it to match the adt being matched on in the switch, which is {:?}", rhs, adt_matched_on ); StatementEquality::NotEqual } } }; match (&x.kind, &y.kind) { // trivial case (x, y) if x == y => StatementEquality::TrivialEqual, // check for case A ( StatementKind::Assign(box (_, rhs)), &StatementKind::SetDiscriminant { ref place, variant_index }, ) if y_target_and_value.value.is_some() => { // choose basic block of x, as that has the assign helper( rhs, place, variant_index, y_target_and_value.value.unwrap(), x_target_and_value.target, ) } ( &StatementKind::SetDiscriminant { ref place, variant_index }, &StatementKind::Assign(box (_, ref rhs)), ) if x_target_and_value.value.is_some() => { // choose basic block of y, as that has the assign helper( rhs, place, variant_index, x_target_and_value.value.unwrap(), y_target_and_value.target, ) } _ => { trace!("NO: statements `{:?}` and `{:?}` not considered equal", x, y); StatementEquality::NotEqual } } } } #[derive(Copy, Clone, Eq, PartialEq)] enum StatementEquality { /// The two statements are trivially equal; same kind TrivialEqual, /// The two statements are considered equal, but may be of different kinds. The BasicBlock field is the basic block to jump to when performing the branch-same optimization. /// For example, `_0 = _1` and `discriminant(_0) = discriminant(0)` are considered equal if 0 is a fieldless variant of an enum. But we don't want to jump to the basic block with the SetDiscriminant, as that is not legal if _1 is not the 0 variant index ConsideredEqual(BasicBlock), /// The two statements are not equal NotEqual, } impl StatementEquality { fn combine(&self, other: &StatementEquality) -> StatementEquality { use StatementEquality::*; match (self, other) { (TrivialEqual, TrivialEqual) => TrivialEqual, (TrivialEqual, ConsideredEqual(b)) | (ConsideredEqual(b), TrivialEqual) => { ConsideredEqual(*b) } (ConsideredEqual(b1), ConsideredEqual(b2)) => { if b1 == b2 { ConsideredEqual(*b1) } else { NotEqual } } (_, NotEqual) | (NotEqual, _) => NotEqual, } } } </del>


<del> - // MIR for `encode` before SimplifyBranchSame + // MIR for `encode` after SimplifyBranchSame fn encode(_1: Type) -> Type { debug v => _1;                       // in scope 0 at $DIR/76803_regression.rs:+0:15: +0:16 let mut _0: Type;                    // return place in scope 0 at $DIR/76803_regression.rs:+0:27: +0:31 let mut _2: isize;                   // in scope 0 at $DIR/76803_regression.rs:+2:9: +2:16 bb0: { _2 = discriminant(_1);           // scope 0 at $DIR/76803_regression.rs:+1:11: +1:12 switchInt(move _2) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/76803_regression.rs:+1:5: +1:12 } bb1: { _0 = move _1;                    // scope 0 at $DIR/76803_regression.rs:+3:14: +3:15 goto -> bb3;                     // scope 0 at $DIR/76803_regression.rs:+3:14: +3:15 } bb2: { Deinit(_0);                      // scope 0 at $DIR/76803_regression.rs:+2:20: +2:27 discriminant(_0) = 1;            // scope 0 at $DIR/76803_regression.rs:+2:20: +2:27 goto -> bb3;                     // scope 0 at $DIR/76803_regression.rs:+2:20: +2:27 } bb3: { return;                          // scope 0 at $DIR/76803_regression.rs:+5:2: +5:2 } } </del>


<del> // compile-flags: -Z mir-opt-level=1 // EMIT_MIR 76803_regression.encode.SimplifyBranchSame.diff #[derive(Debug, Eq, PartialEq)] pub enum Type { A, B, } pub fn encode(v: Type) -> Type { match v { Type::A => Type::B, _ => v, } } fn main() { assert_eq!(Type::B, encode(Type::A)); } </del>


<del> - // MIR for `main` before SimplifyArmIdentity + // MIR for `main` after SimplifyArmIdentity fn main() -> () { let mut _0: ();                      // return place in scope 0 at $DIR/issue_73223.rs:+0:11: +0:11 let _1: i32;                         // in scope 0 at $DIR/issue_73223.rs:+1:9: +1:14 let mut _2: std::option::Option<i32>; // in scope 0 at $DIR/issue_73223.rs:+1:23: +1:30 let mut _3: isize;                   // in scope 0 at $DIR/issue_73223.rs:+2:9: +2:16 let _4: i32;                         // in scope 0 at $DIR/issue_73223.rs:+2:14: +2:15 let mut _6: i32;                     // in scope 0 at $DIR/issue_73223.rs:+6:22: +6:27 let mut _7: &i32;                    // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL let mut _8: &i32;                    // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL let mut _11: bool;                   // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL let mut _12: bool;                   // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL let mut _13: i32;                    // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL let mut _14: i32;                    // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL let _16: !;                          // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL let mut _17: core::panicking::AssertKind; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL let mut _18: &i32;                   // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL let _19: &i32;                       // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL let mut _20: &i32;                   // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL let _21: &i32;                       // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL let mut _22: std::option::Option<std::fmt::Arguments<'_>>; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL let mut _24: &i32;                   // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL let mut _25: &i32;                   // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL scope 1 { debug split => _1;               // in scope 1 at $DIR/issue_73223.rs:+1:9: +1:14 let _5: std::option::Option<i32>; // in scope 1 at $DIR/issue_73223.rs:+6:9: +6:14 scope 3 { debug _prev => _5;           // in scope 3 at $DIR/issue_73223.rs:+6:9: +6:14 let _9: &i32;                // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL let _10: &i32;               // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL let mut _23: &i32;           // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL scope 4 { debug left_val => _9;    // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL debug right_val => _10;  // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL let _15: core::panicking::AssertKind; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL scope 5 { debug kind => _15;   // in scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL } } } } scope 2 { debug v => _4;                   // in scope 2 at $DIR/issue_73223.rs:+2:14: +2:15 } bb0: { StorageLive(_1);                 // scope 0 at $DIR/issue_73223.rs:+1:9: +1:14 StorageLive(_2);                 // scope 0 at $DIR/issue_73223.rs:+1:23: +1:30 Deinit(_2);                      // scope 0 at $DIR/issue_73223.rs:+1:23: +1:30 ((_2 as Some).0: i32) = const 1_i32; // scope 0 at $DIR/issue_73223.rs:+1:23: +1:30 discriminant(_2) = 1;            // scope 0 at $DIR/issue_73223.rs:+1:23: +1:30 _3 = const 1_isize;              // scope 0 at $DIR/issue_73223.rs:+1:23: +1:30 goto -> bb3;                     // scope 0 at $DIR/issue_73223.rs:+1:17: +1:30 } bb1: { StorageDead(_2);                 // scope 0 at $DIR/issue_73223.rs:+4:6: +4:7 StorageDead(_1);                 // scope 0 at $DIR/issue_73223.rs:+8:1: +8:2 return;                          // scope 0 at $DIR/issue_73223.rs:+8:2: +8:2 } bb2: { unreachable;                     // scope 0 at $DIR/issue_73223.rs:+1:23: +1:30 } bb3: { StorageLive(_4);                 // scope 0 at $DIR/issue_73223.rs:+2:14: +2:15 _4 = ((_2 as Some).0: i32);      // scope 0 at $DIR/issue_73223.rs:+2:14: +2:15 _1 = _4;                         // scope 2 at $DIR/issue_73223.rs:+2:20: +2:21 StorageDead(_4);                 // scope 0 at $DIR/issue_73223.rs:+2:20: +2:21 StorageDead(_2);                 // scope 0 at $DIR/issue_73223.rs:+4:6: +4:7 StorageLive(_5);                 // scope 1 at $DIR/issue_73223.rs:+6:9: +6:14 StorageLive(_6);                 // scope 1 at $DIR/issue_73223.rs:+6:22: +6:27 _6 = _1;                         // scope 1 at $DIR/issue_73223.rs:+6:22: +6:27 Deinit(_5);                      // scope 1 at $DIR/issue_73223.rs:+6:17: +6:28 ((_5 as Some).0: i32) = move _6; // scope 1 at $DIR/issue_73223.rs:+6:17: +6:28 discriminant(_5) = 1;            // scope 1 at $DIR/issue_73223.rs:+6:17: +6:28 StorageDead(_6);                 // scope 1 at $DIR/issue_73223.rs:+6:27: +6:28 StorageLive(_24);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL StorageLive(_25);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL StorageLive(_7);                 // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL _7 = &_1;                        // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL StorageLive(_8);                 // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL _23 = const _;                   // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL // mir::Constant // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL // + literal: Const { ty: &i32, val: Unevaluated(main, [], Some(promoted[0])) } _8 = _23;                        // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL Deinit(_24);                     // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL Deinit(_25);                     // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL _24 = move _7;                   // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL _25 = move _8;                   // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL StorageDead(_8);                 // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL StorageDead(_7);                 // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL StorageLive(_9);                 // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL _9 = _24;                        // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL StorageLive(_10);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL _10 = _25;                       // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL StorageLive(_11);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL StorageLive(_12);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL StorageLive(_13);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL _13 = (*_9);                     // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL StorageLive(_14);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL _14 = const 1_i32;               // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL _12 = Eq(move _13, const 1_i32); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL StorageDead(_14);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL StorageDead(_13);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL _11 = Not(move _12);             // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL StorageDead(_12);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL switchInt(move _11) -> [0: bb5, otherwise: bb4]; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL } bb4: { StorageLive(_15);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL Deinit(_15);                     // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL discriminant(_15) = 0;           // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL StorageLive(_16);                // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL StorageLive(_17);                // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL _17 = const core::panicking::AssertKind::Eq; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL // mir::Constant // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL // + literal: Const { ty: core::panicking::AssertKind, val: Value(Scalar(0x00)) } StorageLive(_18);                // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL StorageLive(_19);                // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL _19 = _9;                        // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL _18 = _19;                       // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL StorageLive(_20);                // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL StorageLive(_21);                // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL _21 = _10;                       // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL _20 = _21;                       // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL StorageLive(_22);                // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL Deinit(_22);                     // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL discriminant(_22) = 0;           // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL _16 = core::panicking::assert_failed::<i32, i32>(const core::panicking::AssertKind::Eq, move _18, move _20, move _22); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL // mir::Constant // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL // + literal: Const { ty: for<'a, 'b, 'c> fn(core::panicking::AssertKind, &'a i32, &'b i32, Option<Arguments<'c>>) -> ! {core::panicking::assert_failed::<i32, i32>}, val: Value(<ZST>) } // mir::Constant // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL // + literal: Const { ty: core::panicking::AssertKind, val: Value(Scalar(0x00)) } } bb5: { StorageDead(_11);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL StorageDead(_10);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL StorageDead(_9);                 // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL StorageDead(_24);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL StorageDead(_25);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL StorageDead(_5);                 // scope 1 at $DIR/issue_73223.rs:+8:1: +8:2 StorageDead(_1);                 // scope 0 at $DIR/issue_73223.rs:+8:1: +8:2 return;                          // scope 0 at $DIR/issue_73223.rs:+8:2: +8:2 } } </del>


<del> fn main() { let split = match Some(1) { Some(v) => v, None => return, }; let _prev = Some(split); assert_eq!(split, 1); } // EMIT_MIR issue_73223.main.SimplifyArmIdentity.diff </del>


} _ => { self.tcx.sess.emit_err(YieldExprOutsideOfGenerator { span: expr.span }); <ins> // Avoid expressions without types during writeback (#78653). self.check_expr(value); </ins> self.tcx.mk_unit() } }


<ins> #![feature(generators)] fn main() { yield || for i in 0 { } //~^ ERROR yield expression outside of generator literal //~| ERROR `{integer}` is not an iterator } </ins>


<ins> error[E0627]: yield expression outside of generator literal --> $DIR/yield-outside-generator-issue-78653.rs:4:5 | LL |     yield || for i in 0 { } |     ^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: `{integer}` is not an iterator --> $DIR/yield-outside-generator-issue-78653.rs:4:23 | LL |     yield || for i in 0 { } |                       ^ `{integer}` is not an iterator | = help: the trait `Iterator` is not implemented for `{integer}` = note: if you want to iterate between `start` until a value `end`, use the exclusive range syntax `start..end` or the inclusive range syntax `start..=end` = note: required because of the requirements on the impl of `IntoIterator` for `{integer}` = note: required by `into_iter` error: aborting due to 2 previous errors Some errors have detailed explanations: E0277, E0627. For more information about an error, try `rustc --explain E0277`. </ins>


const WHILE_PARSING_OR_MSG: &str = "while parsing this or-pattern starting here"; /// Whether or not an or-pattern should be gated when occurring in the current context. <del> #[derive(PartialEq)] </del> <ins> #[derive(PartialEq, Clone, Copy)] </ins> pub(super) enum GateOr { Yes, No,


) -> PResult<'a, P<Pat>> { // Parse the first pattern (`p_0`). let first_pat = self.parse_pat(expected)?; <del> self.maybe_recover_unexpected_comma(first_pat.span, rc)?; </del> <ins> self.maybe_recover_unexpected_comma(first_pat.span, rc, gate_or)?; </ins> // If the next token is not a `|`, // this is not an or-pattern and we should exit here.


err.span_label(lo, WHILE_PARSING_OR_MSG); err })?; <del> self.maybe_recover_unexpected_comma(pat.span, rc)?; </del> <ins> self.maybe_recover_unexpected_comma(pat.span, rc, gate_or)?; </ins> pats.push(pat); } let or_pattern_span = lo.to(self.prev_token.span);


/// Some special error handling for the "top-level" patterns in a match arm, /// `for` loop, `let`, &c. (in contrast to subpatterns within such). <del> fn maybe_recover_unexpected_comma(&mut self, lo: Span, rc: RecoverComma) -> PResult<'a, ()> { </del> <ins> fn maybe_recover_unexpected_comma( &mut self, lo: Span, rc: RecoverComma, gate_or: GateOr, ) -> PResult<'a, ()> { </ins> if rc == RecoverComma::No || self.token != token::Comma { return Ok(()); }


let seq_span = lo.to(self.prev_token.span); let mut err = self.struct_span_err(comma_span, "unexpected `,` in pattern"); if let Ok(seq_snippet) = self.span_to_snippet(seq_span) { <ins> const MSG: &str = "try adding parentheses to match on a tuple..."; let or_suggestion = gate_or == GateOr::No || !self.sess.gated_spans.is_ungated(sym::or_patterns); </ins> err.span_suggestion( seq_span, <del> "try adding parentheses to match on a tuple...", </del> <ins> if or_suggestion { MSG } else { MSG.trim_end_matches('.') }, </ins> format!("({})", seq_snippet), Applicability::MachineApplicable, <del> ) .span_suggestion( seq_span, "...or a vertical bar to match on multiple alternatives", seq_snippet.replace(",", " |"), Applicability::MachineApplicable, </del> ); <ins> if or_suggestion { err.span_suggestion( seq_span, "...or a vertical bar to match on multiple alternatives", seq_snippet.replace(",", " |"), Applicability::MachineApplicable, ); } </ins> } Err(err) }


--> $DIR/issue-48492-tuple-destructure-missing-parens.rs:67:10 | LL |     for x, _barr_body in women.iter().map(|woman| woman.allosomes.clone()) { <del> |          ^ | help: try adding parentheses to match on a tuple... | LL |     for (x, _barr_body) in women.iter().map(|woman| woman.allosomes.clone()) { |         ^^^^^^^^^^^^^^^ help: ...or a vertical bar to match on multiple alternatives | LL |     for x | _barr_body in women.iter().map(|woman| woman.allosomes.clone()) { |         ^^^^^^^^^^^^^^ </del> <ins> |         -^----------- help: try adding parentheses to match on a tuple: `(x, _barr_body)` </ins> error: unexpected `,` in pattern --> $DIR/issue-48492-tuple-destructure-missing-parens.rs:75:10 | LL |     for x, y @ Allosome::Y(_) in men.iter().map(|man| man.allosomes.clone()) { <del> |          ^ | help: try adding parentheses to match on a tuple... | LL |     for (x, y @ Allosome::Y(_)) in men.iter().map(|man| man.allosomes.clone()) { |         ^^^^^^^^^^^^^^^^^^^^^^^ help: ...or a vertical bar to match on multiple alternatives | LL |     for x | y @ Allosome::Y(_) in men.iter().map(|man| man.allosomes.clone()) { |         ^^^^^^^^^^^^^^^^^^^^^^ </del> <ins> |         -^------------------- help: try adding parentheses to match on a tuple: `(x, y @ Allosome::Y(_))` </ins> error: unexpected `,` in pattern --> $DIR/issue-48492-tuple-destructure-missing-parens.rs:84:14 | LL |     let women, men: (Vec<Genome>, Vec<Genome>) = genomes.iter().cloned() <del> |              ^ | help: try adding parentheses to match on a tuple... | LL |     let (women, men): (Vec<Genome>, Vec<Genome>) = genomes.iter().cloned() |         ^^^^^^^^^^^^ help: ...or a vertical bar to match on multiple alternatives | LL |     let women | men: (Vec<Genome>, Vec<Genome>) = genomes.iter().cloned() |         ^^^^^^^^^^^ </del> <ins> |         -----^---- help: try adding parentheses to match on a tuple: `(women, men)` </ins> error: aborting due to 6 previous errors


Ok(true) } ty::Float(_) | ty::Int(_) | ty::Uint(_) => { <del> let value = self.ecx.read_scalar(value)?; </del> <ins> let value = try_validation!( self.ecx.read_scalar(value), self.path, err_unsup!(ReadPointerAsBytes) => { "read of part of a pointer" }, ); </ins> // NOTE: Keep this in sync with the array optimization for int/float // types below! if self.ctfe_mode.is_some() {


<ins> // ignore-32bit // This test gives a different error on 32-bit architectures. union Transmute<T: Copy, U: Copy> { t: T, u: U, } trait Bar { fn bar(&self) -> u32; } struct Foo { foo: u32, bar: bool, } impl Bar for Foo { fn bar(&self) -> u32 { self.foo } } #[derive(Copy, Clone)] struct Fat<'a>(&'a Foo, &'static VTable); struct VTable { size: Foo, } const FOO: &dyn Bar = &Foo { foo: 128, bar: false, }; const G: Fat = unsafe { Transmute { t: FOO }.u }; //~^ ERROR it is undefined behavior to use this value fn main() {} </ins>


<ins> error[E0080]: it is undefined behavior to use this value --> $DIR/issue-79690.rs:29:1 | LL | const G: Fat = unsafe { Transmute { t: FOO }.u }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered read of part of a pointer at .1.<deref>.size.foo | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error: aborting due to previous error For more information about this error, try `rustc --explain E0080`. </ins>


use super::{ CheckInAllocMsg, GlobalAlloc, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, <del> ValueVisitor, </del> <ins> ScalarMaybeUninit, ValueVisitor, </ins> }; macro_rules! throw_validation_failure {


value: OpTy<'tcx, M::PointerTag>, kind: &str, ) -> InterpResult<'tcx> { <del> let value = self.ecx.read_immediate(value)?; </del> <ins> let value = try_validation!( self.ecx.read_immediate(value), self.path, err_unsup!(ReadPointerAsBytes) => { "part of a pointer" } expected { "a proper pointer or integer value" }, ); </ins> // Handle wide pointers. // Check metadata early, for better diagnostics let place = try_validation!(


Ok(()) } <ins> fn read_scalar( &self, op: OpTy<'tcx, M::PointerTag>, ) -> InterpResult<'tcx, ScalarMaybeUninit<M::PointerTag>> { Ok(try_validation!( self.ecx.read_scalar(op), self.path, err_unsup!(ReadPointerAsBytes) => { "(potentially part of) a pointer" } expected { "plain (non-pointer) bytes" }, )) } </ins> /// Check if this is a value of primitive type, and if yes check the validity of the value /// at that type.  Return `true` if the type is indeed primitive. fn try_visit_primitive(


let ty = value.layout.ty; match ty.kind() { ty::Bool => { <del> let value = self.ecx.read_scalar(value)?; </del> <ins> let value = self.read_scalar(value)?; </ins> try_validation!( value.to_bool(), self.path,


Ok(true) } ty::Char => { <del> let value = self.ecx.read_scalar(value)?; </del> <ins> let value = self.read_scalar(value)?; </ins> try_validation!( value.to_char(), self.path,


Ok(true) } ty::Float(_) | ty::Int(_) | ty::Uint(_) => { <del> let value = try_validation!( self.ecx.read_scalar(value), self.path, err_unsup!(ReadPointerAsBytes) => { "read of part of a pointer" }, ); </del> <ins> let value = self.read_scalar(value)?; </ins> // NOTE: Keep this in sync with the array optimization for int/float // types below! if self.ctfe_mode.is_some() {


// actually enforce the strict rules for raw pointers (mostly because // that lets us re-use `ref_to_mplace`). let place = try_validation!( <del> self.ecx.ref_to_mplace(self.ecx.read_immediate(value)?), </del> <ins> self.ecx.read_immediate(value).and_then(|i| self.ecx.ref_to_mplace(i)), </ins> self.path, err_ub!(InvalidUninitBytes(None)) => { "uninitialized raw pointer" }, <ins> err_unsup!(ReadPointerAsBytes) => { "part of a pointer" } expected { "a proper pointer or integer value" }, </ins> ); if place.layout.is_unsized() { self.check_wide_ptr_meta(place.meta, place.layout)?;


Ok(true) } ty::FnPtr(_sig) => { <del> let value = self.ecx.read_scalar(value)?; </del> <ins> let value = try_validation!( self.ecx.read_immediate(value), self.path, err_unsup!(ReadPointerAsBytes) => { "part of a pointer" } expected { "a proper pointer or integer value" }, ); </ins> let _fn = try_validation!( <del> value.check_init().and_then(|ptr| self.ecx.memory.get_fn(ptr)), </del> <ins> value.to_scalar().and_then(|ptr| self.ecx.memory.get_fn(ptr)), </ins> self.path, err_ub!(DanglingIntPointer(..)) | err_ub!(InvalidFunctionPointer(..)) |


op: OpTy<'tcx, M::PointerTag>, scalar_layout: &Scalar, ) -> InterpResult<'tcx> { <del> let value = self.ecx.read_scalar(op)?; </del> <ins> let value = self.read_scalar(op)?; </ins> let valid_range = &scalar_layout.valid_range; let (lo, hi) = valid_range.clone().into_inner(); // Determine the allowed range


--> $DIR/issue-79690.rs:29:1 | LL | const G: Fat = unsafe { Transmute { t: FOO }.u }; <del> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered read of part of a pointer at .1.<deref>.size.foo </del> <ins> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered (potentially part of) a pointer at .1.<deref>.size.foo, but expected plain (non-pointer) bytes </ins> | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.


CodeBlockKind::Fenced(ref lang) => { LangString::parse_without_check(&lang, self.check_error_codes, false) } <del> CodeBlockKind::Indented => LangString::all_false(), </del> <ins> CodeBlockKind::Indented => Default::default(), </ins> }; if !parse_result.rust { return Some(Event::Start(Tag::CodeBlock(kind)));


let block_info = match kind { CodeBlockKind::Fenced(ref lang) => { if lang.is_empty() { <del> LangString::all_false() </del> <ins> Default::default() </ins> } else { LangString::parse( lang,


) } } <del> CodeBlockKind::Indented => LangString::all_false(), </del> <ins> CodeBlockKind::Indented => Default::default(), </ins> }; if !block_info.rust { continue;


Some(Vec<String>), } <del> impl LangString { fn all_false() -> LangString { LangString { </del> <ins> impl Default for LangString { fn default() -> Self { Self { </ins> original: String::new(), should_panic: false, no_run: false, ignore: Ignore::None, <del> rust: true, // NB This used to be `notrust = false` </del> <ins> rust: true, </ins> test_harness: false, compile_fail: false, error_codes: Vec::new(),


edition: None, } } <ins> } impl LangString { </ins> fn parse_without_check( string: &str, allow_error_code_check: ErrorCodes,


let allow_error_code_check = allow_error_code_check.as_bool(); let mut seen_rust_tags = false; let mut seen_other_tags = false; <del> let mut data = LangString::all_false(); </del> <ins> let mut data = LangString::default(); </ins> let mut ignores = vec![]; data.original = string.to_owned();


CodeBlockKind::Fenced(syntax) => { let syntax = syntax.as_ref(); let lang_string = if syntax.is_empty() { <del> LangString::all_false() </del> <ins> Default::default() </ins> } else { LangString::parse(&*syntax, ErrorCodes::Yes, false, Some(extra_info)) };


assert_eq!(LangString::parse(s, ErrorCodes::Yes, true, None), lg) } <del> t(LangString::all_false()); t(LangString { original: "rust".into(), ..LangString::all_false() }); t(LangString { original: "sh".into(), rust: false, ..LangString::all_false() }); t(LangString { original: "ignore".into(), ignore: Ignore::All, ..LangString::all_false() }); </del> <ins> t(Default::default()); t(LangString { original: "rust".into(), ..Default::default() }); t(LangString { original: "sh".into(), rust: false, ..Default::default() }); t(LangString { original: "ignore".into(), ignore: Ignore::All, ..Default::default() }); </ins> t(LangString { original: "ignore-foo".into(), ignore: Ignore::Some(vec!["foo".to_string()]), <del> ..LangString::all_false() }); t(LangString { original: "should_panic".into(), should_panic: true, ..LangString::all_false() }); t(LangString { original: "no_run".into(), no_run: true, ..LangString::all_false() }); t(LangString { original: "test_harness".into(), test_harness: true, ..LangString::all_false() </del> <ins> ..Default::default() </ins> }); <ins> t(LangString { original: "should_panic".into(), should_panic: true, ..Default::default() }); t(LangString { original: "no_run".into(), no_run: true, ..Default::default() }); t(LangString { original: "test_harness".into(), test_harness: true, ..Default::default() }); </ins> t(LangString { original: "compile_fail".into(), no_run: true, compile_fail: true, <del> ..LangString::all_false() }); t(LangString { original: "allow_fail".into(), allow_fail: true, ..LangString::all_false() }); t(LangString { original: "{.no_run .example}".into(), no_run: true, ..LangString::all_false() </del> <ins> ..Default::default() </ins> }); <ins> t(LangString { original: "allow_fail".into(), allow_fail: true, ..Default::default() }); t(LangString { original: "{.no_run .example}".into(), no_run: true, ..Default::default() }); </ins> t(LangString { original: "{.sh .should_panic}".into(), should_panic: true, rust: false, <del> ..LangString::all_false() </del> <ins> ..Default::default() </ins> }); <del> t(LangString { original: "{.example .rust}".into(), ..LangString::all_false() }); </del> <ins> t(LangString { original: "{.example .rust}".into(), ..Default::default() }); </ins> t(LangString { original: "{.test_harness .rust}".into(), test_harness: true, <del> ..LangString::all_false() </del> <ins> ..Default::default() </ins> }); t(LangString { original: "text, no_run".into(), no_run: true, rust: false, <del> ..LangString::all_false() </del> <ins> ..Default::default() </ins> }); t(LangString { original: "text,no_run".into(), no_run: true, rust: false, <del> ..LangString::all_false() </del> <ins> ..Default::default() </ins> }); t(LangString { original: "edition2015".into(), edition: Some(Edition::Edition2015), <del> ..LangString::all_false() </del> <ins> ..Default::default() </ins> }); t(LangString { original: "edition2018".into(), edition: Some(Edition::Edition2018), <del> ..LangString::all_false() </del> <ins> ..Default::default() </ins> }); }


use core::mem::*; <ins> #[cfg(panic = "unwind")] </ins> use std::rc::Rc; #[test]


#[test] fn uninit_write_slice_cloned_no_drop() { <del> let rc = Rc::new(()); </del> <ins> #[derive(Clone)] struct Bomb; impl Drop for Bomb { fn drop(&mut self) { panic!("dropped a bomb! kaboom") } } </ins> let mut dst = [MaybeUninit::uninit()]; <del> let src = [rc.clone()]; </del> <ins> let src = [Bomb]; </ins> MaybeUninit::write_slice_cloned(&mut dst, &src); <del> drop(src); assert_eq!(Rc::strong_count(&rc), 2); </del> <ins> forget(src); </ins> }


use rustc_ast::NodeId; use rustc_ast::{mut_visit, visit}; use rustc_ast::{Attribute, HasAttrs, HasTokens}; <ins> use rustc_errors::PResult; </ins> use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_expand::config::StripUnconfigured; use rustc_expand::configure;


// the location of `#[cfg]` and `#[cfg_attr]` in the token stream. The tokenization // process is lossless, so this process is invisible to proc-macros. <del> let parse_annotatable_with: fn(&mut Parser<'_>) -> _ = match annotatable { Annotatable::Item(_) => { |parser| Annotatable::Item(parser.parse_item(ForceCollect::Yes).unwrap().unwrap()) } Annotatable::TraitItem(_) => |parser| { Annotatable::TraitItem( parser.parse_trait_item(ForceCollect::Yes).unwrap().unwrap().unwrap(), ) }, Annotatable::ImplItem(_) => |parser| { Annotatable::ImplItem( parser.parse_impl_item(ForceCollect::Yes).unwrap().unwrap().unwrap(), ) }, Annotatable::ForeignItem(_) => |parser| { Annotatable::ForeignItem( parser.parse_foreign_item(ForceCollect::Yes).unwrap().unwrap().unwrap(), ) }, Annotatable::Stmt(_) => |parser| { Annotatable::Stmt(P(parser.parse_stmt(ForceCollect::Yes).unwrap().unwrap())) }, Annotatable::Expr(_) => { |parser| Annotatable::Expr(parser.parse_expr_force_collect().unwrap()) } _ => unreachable!(), }; </del> <ins> let parse_annotatable_with: for<'a> fn(&mut Parser<'a>) -> PResult<'a, _> = match annotatable { Annotatable::Item(_) => { |parser| Ok(Annotatable::Item(parser.parse_item(ForceCollect::Yes)?.unwrap())) } Annotatable::TraitItem(_) => |parser| { Ok(Annotatable::TraitItem( parser.parse_trait_item(ForceCollect::Yes)?.unwrap().unwrap(), )) }, Annotatable::ImplItem(_) => |parser| { Ok(Annotatable::ImplItem( parser.parse_impl_item(ForceCollect::Yes)?.unwrap().unwrap(), )) }, Annotatable::ForeignItem(_) => |parser| { Ok(Annotatable::ForeignItem( parser.parse_foreign_item(ForceCollect::Yes)?.unwrap().unwrap(), )) }, Annotatable::Stmt(_) => |parser| { Ok(Annotatable::Stmt(P(parser.parse_stmt(ForceCollect::Yes)?.unwrap()))) }, Annotatable::Expr(_) => { |parser| Ok(Annotatable::Expr(parser.parse_expr_force_collect()?)) } _ => unreachable!(), }; </ins> // 'Flatten' all nonterminals (i.e. `TokenKind::Interpolated`) // to `None`-delimited groups containing the corresponding tokens. This


let mut parser = rustc_parse::stream_to_parser(&self.cfg.sess.parse_sess, orig_tokens, None); parser.capture_cfg = true; <del> annotatable = parse_annotatable_with(&mut parser); </del> <ins> match parse_annotatable_with(&mut parser) { Ok(a) => annotatable = a, Err(mut err) => { err.emit(); return Some(annotatable); } } </ins> // Now that we have our re-parsed `AttrTokenStream`, recursively configuring // our attribute target will correctly the tokens as well.


panic!("Unexpected last token {:?}", last_token) } } <del> assert!(stack.is_empty(), "Stack should be empty: final_buf={:?} stack={:?}", final_buf, stack); </del> AttrTokenStream::new(final_buf.inner) }


<ins> macro_rules! values { ($($token:ident($value:literal) $(as $inner:ty)? => $attr:meta,)*) => { #[derive(Debug)] pub enum TokenKind { $( #[$attr] $token $($inner)? = $value, )* } }; } //~^^^^^ ERROR expected one of `(`, `,`, `=`, `{`, or `}`, found `(String)` //~| ERROR macro expansion ignores token `(String)` and any following values!(STRING(1) as (String) => cfg(test),); //~^ ERROR expected one of `!` or `::`, found `<eof>` fn main() {} </ins>


<ins> error: expected one of `(`, `,`, `=`, `{`, or `}`, found `(String)` --> $DIR/syntax-error-recovery.rs:7:26 | LL |                 $token $($inner)? = $value, |                          ^^^^^^ expected one of `(`, `,`, `=`, `{`, or `}` ... LL | values!(STRING(1) as (String) => cfg(test),); | -------------------------------------------- in this macro invocation | = note: this error originates in the macro `values` (in Nightly builds, run with -Z macro-backtrace for more info) error: macro expansion ignores token `(String)` and any following --> $DIR/syntax-error-recovery.rs:7:26 | LL |                 $token $($inner)? = $value, |                          ^^^^^^ ... LL | values!(STRING(1) as (String) => cfg(test),); | -------------------------------------------- caused by the macro expansion here | = note: the usage of `values!` is likely invalid in item context error: expected one of `!` or `::`, found `<eof>` --> $DIR/syntax-error-recovery.rs:15:9 | LL | values!(STRING(1) as (String) => cfg(test),); |         ^^^^^^ expected one of `!` or `::` error: aborting due to 3 previous errors </ins>


Some(Node::Item(it)) => item_scope_tag(&it), Some(Node::TraitItem(it)) => trait_item_scope_tag(&it), Some(Node::ImplItem(it)) => impl_item_scope_tag(&it), <ins> Some(Node::ForeignItem(it)) => foreign_item_scope_tag(&it), </ins> _ => unreachable!(), }; let (prefix, span) = match *region {


} } <ins> fn foreign_item_scope_tag(item: &hir::ForeignItem<'_>) -> &'static str { match item.kind { hir::ForeignItemKind::Fn(..) => "method body", hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => "associated item", } } </ins> fn explain_span(tcx: TyCtxt<'tcx>, heading: &str, span: Span) -> (String, Option<Span>) { let lo = tcx.sess.source_map().lookup_char_pos(span.lo()); (format!("the {} at {}:{}", heading, lo.line, lo.col.to_usize() + 1), Some(span))


let fcx = FnCtxt::new(&inh, param_env, id); if !inh.tcx.features().trivial_bounds { // As predicates are cached rather than obligations, this <del> // needsto be called first so that they are checked with an </del> <ins> // needs to be called first so that they are checked with an </ins> // empty `param_env`. check_false_global_bounds(&fcx, span, id); }


<ins> // Regression test for #80468. #![crate_type = "lib"] pub trait Trait {} #[repr(transparent)] pub struct Wrapper<T: Trait>(T); #[repr(transparent)] pub struct Ref<'a>(&'a u8); impl Trait for Ref {} //~ ERROR:  implicit elided lifetime not allowed here extern "C" { pub fn repro(_: Wrapper<Ref>); //~ ERROR: mismatched types } </ins>


<ins> error[E0726]: implicit elided lifetime not allowed here --> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:13:16 | LL | impl Trait for Ref {} |                ^^^- help: indicate the anonymous lifetime: `<'_>` error[E0308]: mismatched types --> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:16:21 | LL |     pub fn repro(_: Wrapper<Ref>); |                     ^^^^^^^^^^^^ lifetime mismatch | = note: expected trait `Trait` found trait `Trait` note: the anonymous lifetime #1 defined on the method body at 16:5... --> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:16:5 | LL |     pub fn repro(_: Wrapper<Ref>); |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: ...does not necessarily outlive the static lifetime error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0308`. </ins>


// We have a valid span in almost all cases, but we don't have one when linting a crate // name provided via the command line. if !ident.span.is_dummy() { <ins> let sc_ident = Ident::from_str_and_span(&sc, ident.span); let (message, suggestion) = if sc_ident.is_reserved() { // We shouldn't suggest a reserved identifier to fix non-snake-case identifiers. // Instead, recommend renaming the identifier entirely or, if permitted, // escaping it to create a raw identifier. if sc_ident.name.can_be_raw() { ("rename the identifier or convert it to a snake case raw identifier", sc_ident.to_string()) } else { err.note(&format!("`{}` cannot be used as a raw identifier", sc)); ("rename the identifier", String::new()) } } else { ("convert the identifier to snake case", sc) }; </ins> err.span_suggestion( ident.span, <del> "convert the identifier to snake case", sc, </del> <ins> message, suggestion, </ins> Applicability::MaybeIncorrect, ); } else {


<ins> #![warn(unused)] #![allow(dead_code)] #![deny(non_snake_case)] mod Impl {} //~^ ERROR module `Impl` should have a snake case name fn While() {} //~^ ERROR function `While` should have a snake case name fn main() { let Mod: usize = 0; //~^ ERROR variable `Mod` should have a snake case name //~^^ WARN unused variable: `Mod` let Super: usize = 0; //~^ ERROR variable `Super` should have a snake case name //~^^ WARN unused variable: `Super` } </ins>


<ins> warning: unused variable: `Mod` --> $DIR/lint-non-snake-case-identifiers-suggestion-reserved.rs:12:9 | LL |     let Mod: usize = 0; |         ^^^ help: if this is intentional, prefix it with an underscore: `_Mod` | note: the lint level is defined here --> $DIR/lint-non-snake-case-identifiers-suggestion-reserved.rs:1:9 | LL | #![warn(unused)] |         ^^^^^^ = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` warning: unused variable: `Super` --> $DIR/lint-non-snake-case-identifiers-suggestion-reserved.rs:16:9 | LL |     let Super: usize = 0; |         ^^^^^ help: if this is intentional, prefix it with an underscore: `_Super` error: module `Impl` should have a snake case name --> $DIR/lint-non-snake-case-identifiers-suggestion-reserved.rs:5:5 | LL | mod Impl {} |     ^^^^ | note: the lint level is defined here --> $DIR/lint-non-snake-case-identifiers-suggestion-reserved.rs:3:9 | LL | #![deny(non_snake_case)] |         ^^^^^^^^^^^^^^ help: rename the identifier or convert it to a snake case raw identifier | LL | mod r#impl {} |     ^^^^^^ error: function `While` should have a snake case name --> $DIR/lint-non-snake-case-identifiers-suggestion-reserved.rs:8:4 | LL | fn While() {} |    ^^^^^ | help: rename the identifier or convert it to a snake case raw identifier | LL | fn r#while() {} |    ^^^^^^^ error: variable `Mod` should have a snake case name --> $DIR/lint-non-snake-case-identifiers-suggestion-reserved.rs:12:9 | LL |     let Mod: usize = 0; |         ^^^ | help: rename the identifier or convert it to a snake case raw identifier | LL |     let r#mod: usize = 0; |         ^^^^^ error: variable `Super` should have a snake case name --> $DIR/lint-non-snake-case-identifiers-suggestion-reserved.rs:16:9 | LL |     let Super: usize = 0; |         ^^^^^ help: rename the identifier | = note: `super` cannot be used as a raw identifier error: aborting due to 4 previous errors; 2 warnings emitted </ins>


<ins> // Regression test for #75883. pub struct UI {} impl UI { pub fn run() -> Result<_> { //~^ ERROR: this enum takes 2 type arguments but only 1 type argument was supplied //~| ERROR: the type placeholder `_` is not allowed within types on item signatures let mut ui = UI {}; ui.interact(); unimplemented!(); } pub fn interact(&mut self) -> Result<_> { //~^ ERROR: this enum takes 2 type arguments but only 1 type argument was supplied //~| ERROR: the type placeholder `_` is not allowed within types on item signatures unimplemented!(); } } fn main() {} </ins>


<ins> error[E0107]: this enum takes 2 type arguments but only 1 type argument was supplied --> $DIR/issue-75883.rs:6:21 | LL |     pub fn run() -> Result<_> { |                     ^^^^^^ - supplied 1 type argument |                     | |                     expected 2 type arguments | note: enum defined here, with 2 type parameters: `T`, `E` --> $SRC_DIR/core/src/result.rs:LL:COL | LL | pub enum Result<T, E> { |          ^^^^^^ -  - help: add missing type argument | LL |     pub fn run() -> Result<_, E> { |                             ^^^ error[E0107]: this enum takes 2 type arguments but only 1 type argument was supplied --> $DIR/issue-75883.rs:15:35 | LL |     pub fn interact(&mut self) -> Result<_> { |                                   ^^^^^^ - supplied 1 type argument |                                   | |                                   expected 2 type arguments | note: enum defined here, with 2 type parameters: `T`, `E` --> $SRC_DIR/core/src/result.rs:LL:COL | LL | pub enum Result<T, E> { |          ^^^^^^ -  - help: add missing type argument | LL |     pub fn interact(&mut self) -> Result<_, E> { |                                           ^^^ error[E0121]: the type placeholder `_` is not allowed within types on item signatures --> $DIR/issue-75883.rs:15:42 | LL |     pub fn interact(&mut self) -> Result<_> { |                                          ^ not allowed in type signatures error[E0121]: the type placeholder `_` is not allowed within types on item signatures --> $DIR/issue-75883.rs:6:28 | LL |     pub fn run() -> Result<_> { |                            ^ not allowed in type signatures error: aborting due to 4 previous errors Some errors have detailed explanations: E0107, E0121. For more information about an error, try `rustc --explain E0107`. </ins>


<ins> // Regression test for #80779. pub struct T<'a>(&'a str); pub fn f<'a>(val: T<'a>) -> _ { //~^ ERROR: the type placeholder `_` is not allowed within types on item signatures g(val) } pub fn g(_: T<'static>) -> _ {} //~^ ERROR: the type placeholder `_` is not allowed within types on item signatures fn main() {} </ins>