diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 4ce1d4882ef2c..97385b2eaab89 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -1,7 +1,6 @@ //! Functions dealing with attributes and meta items. use std::fmt::Debug; -use std::iter; use std::sync::atomic::{AtomicU32, Ordering}; use rustc_index::bit_set::GrowableBitSet; @@ -16,7 +15,9 @@ use crate::ast::{ }; use crate::ptr::P; use crate::token::{self, CommentKind, Delimiter, Token}; -use crate::tokenstream::{DelimSpan, LazyAttrTokenStream, Spacing, TokenStream, TokenTree}; +use crate::tokenstream::{ + DelimSpan, LazyAttrTokenStream, Spacing, TokenStream, TokenStreamIter, TokenTree, +}; use crate::util::comments; use crate::util::literal::escape_string_symbol; @@ -365,12 +366,9 @@ impl MetaItem { } } - fn from_tokens<'a, I>(tokens: &mut iter::Peekable) -> Option - where - I: Iterator, - { + fn from_tokens(iter: &mut TokenStreamIter<'_>) -> Option { // FIXME: Share code with `parse_path`. - let tt = tokens.next().map(|tt| TokenTree::uninterpolate(tt)); + let tt = iter.next().map(|tt| TokenTree::uninterpolate(tt)); let path = match tt.as_deref() { Some(&TokenTree::Token( Token { kind: ref kind @ (token::Ident(..) | token::PathSep), span }, @@ -378,9 +376,9 @@ impl MetaItem { )) => 'arm: { let mut segments = if let &token::Ident(name, _) = kind { if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) = - tokens.peek() + iter.peek() { - tokens.next(); + iter.next(); thin_vec![PathSegment::from_ident(Ident::new(name, span))] } else { break 'arm Path::from_ident(Ident::new(name, span)); @@ -390,16 +388,16 @@ impl MetaItem { }; loop { if let Some(&TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) = - tokens.next().map(|tt| TokenTree::uninterpolate(tt)).as_deref() + iter.next().map(|tt| TokenTree::uninterpolate(tt)).as_deref() { segments.push(PathSegment::from_ident(Ident::new(name, span))); } else { return None; } if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) = - tokens.peek() + iter.peek() { - tokens.next(); + iter.next(); } else { break; } @@ -420,8 +418,8 @@ impl MetaItem { } _ => return None, }; - let list_closing_paren_pos = tokens.peek().map(|tt| tt.span().hi()); - let kind = MetaItemKind::from_tokens(tokens)?; + let list_closing_paren_pos = iter.peek().map(|tt| tt.span().hi()); + let kind = MetaItemKind::from_tokens(iter)?; let hi = match &kind { MetaItemKind::NameValue(lit) => lit.span.hi(), MetaItemKind::List(..) => list_closing_paren_pos.unwrap_or(path.span.hi()), @@ -438,12 +436,12 @@ impl MetaItem { impl MetaItemKind { // public because it can be called in the hir pub fn list_from_tokens(tokens: TokenStream) -> Option> { - let mut tokens = tokens.trees().peekable(); + let mut iter = tokens.iter(); let mut result = ThinVec::new(); - while tokens.peek().is_some() { - let item = MetaItemInner::from_tokens(&mut tokens)?; + while iter.peek().is_some() { + let item = MetaItemInner::from_tokens(&mut iter)?; result.push(item); - match tokens.next() { + match iter.next() { None | Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) => {} _ => return None, } @@ -451,12 +449,10 @@ impl MetaItemKind { Some(result) } - fn name_value_from_tokens<'a>( - tokens: &mut impl Iterator, - ) -> Option { - match tokens.next() { + fn name_value_from_tokens(iter: &mut TokenStreamIter<'_>) -> Option { + match iter.next() { Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) => { - MetaItemKind::name_value_from_tokens(&mut inner_tokens.trees()) + MetaItemKind::name_value_from_tokens(&mut inner_tokens.iter()) } Some(TokenTree::Token(token, _)) => { MetaItemLit::from_token(token).map(MetaItemKind::NameValue) @@ -465,19 +461,17 @@ impl MetaItemKind { } } - fn from_tokens<'a>( - tokens: &mut iter::Peekable>, - ) -> Option { - match tokens.peek() { + fn from_tokens(iter: &mut TokenStreamIter<'_>) -> Option { + match iter.peek() { Some(TokenTree::Delimited(.., Delimiter::Parenthesis, inner_tokens)) => { let inner_tokens = inner_tokens.clone(); - tokens.next(); + iter.next(); MetaItemKind::list_from_tokens(inner_tokens).map(MetaItemKind::List) } Some(TokenTree::Delimited(..)) => None, Some(TokenTree::Token(Token { kind: token::Eq, .. }, _)) => { - tokens.next(); - MetaItemKind::name_value_from_tokens(tokens) + iter.next(); + MetaItemKind::name_value_from_tokens(iter) } _ => Some(MetaItemKind::Word), } @@ -593,22 +587,19 @@ impl MetaItemInner { self.meta_item().is_some() } - fn from_tokens<'a, I>(tokens: &mut iter::Peekable) -> Option - where - I: Iterator, - { - match tokens.peek() { + fn from_tokens(iter: &mut TokenStreamIter<'_>) -> Option { + match iter.peek() { Some(TokenTree::Token(token, _)) if let Some(lit) = MetaItemLit::from_token(token) => { - tokens.next(); + iter.next(); return Some(MetaItemInner::Lit(lit)); } Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) => { - tokens.next(); - return MetaItemInner::from_tokens(&mut inner_tokens.trees().peekable()); + iter.next(); + return MetaItemInner::from_tokens(&mut inner_tokens.iter()); } _ => {} } - MetaItem::from_tokens(tokens).map(MetaItemInner::MetaItem) + MetaItem::from_tokens(iter).map(MetaItemInner::MetaItem) } } diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index ab82f18133e78..f639e785bc4f4 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -903,7 +903,8 @@ impl Token { self.is_non_raw_ident_where(|id| id.name == kw) } - /// Returns `true` if the token is a given keyword, `kw` or if `case` is `Insensitive` and this token is an identifier equal to `kw` ignoring the case. + /// Returns `true` if the token is a given keyword, `kw` or if `case` is `Insensitive` and this + /// token is an identifier equal to `kw` ignoring the case. pub fn is_keyword_case(&self, kw: Symbol, case: Case) -> bool { self.is_keyword(kw) || (case == Case::Insensitive @@ -916,6 +917,11 @@ impl Token { self.is_non_raw_ident_where(Ident::is_path_segment_keyword) } + /// Don't use this unless you're doing something very loose and heuristic-y. + pub fn is_any_keyword(&self) -> bool { + self.is_non_raw_ident_where(Ident::is_any_keyword) + } + /// Returns true for reserved identifiers used internally for elided lifetimes, /// unnamed method parameters, crate root module, error recovery etc. pub fn is_special_ident(&self) -> bool { diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index c6b6addc946e8..e7b393d869d2b 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -99,7 +99,7 @@ where CTX: crate::HashStableContext, { fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { - for sub_tt in self.trees() { + for sub_tt in self.iter() { sub_tt.hash_stable(hcx, hasher); } } @@ -406,7 +406,7 @@ impl Eq for TokenStream {} impl PartialEq for TokenStream { fn eq(&self, other: &TokenStream) -> bool { - self.trees().eq(other.trees()) + self.iter().eq(other.iter()) } } @@ -423,24 +423,24 @@ impl TokenStream { self.0.len() } - pub fn trees(&self) -> RefTokenTreeCursor<'_> { - RefTokenTreeCursor::new(self) + pub fn get(&self, index: usize) -> Option<&TokenTree> { + self.0.get(index) } - pub fn into_trees(self) -> TokenTreeCursor { - TokenTreeCursor::new(self) + pub fn iter(&self) -> TokenStreamIter<'_> { + TokenStreamIter::new(self) } /// Compares two `TokenStream`s, checking equality without regarding span information. pub fn eq_unspanned(&self, other: &TokenStream) -> bool { - let mut t1 = self.trees(); - let mut t2 = other.trees(); - for (t1, t2) in iter::zip(&mut t1, &mut t2) { - if !t1.eq_unspanned(t2) { + let mut iter1 = self.iter(); + let mut iter2 = other.iter(); + for (tt1, tt2) in iter::zip(&mut iter1, &mut iter2) { + if !tt1.eq_unspanned(tt2) { return false; } } - t1.next().is_none() && t2.next().is_none() + iter1.next().is_none() && iter2.next().is_none() } /// Create a token stream containing a single token with alone spacing. The @@ -509,7 +509,7 @@ impl TokenStream { #[must_use] pub fn flattened(&self) -> TokenStream { fn can_skip(stream: &TokenStream) -> bool { - stream.trees().all(|tree| match tree { + stream.iter().all(|tree| match tree { TokenTree::Token(token, _) => !matches!( token.kind, token::NtIdent(..) | token::NtLifetime(..) | token::Interpolated(..) @@ -522,7 +522,7 @@ impl TokenStream { return self.clone(); } - self.trees().map(|tree| TokenStream::flatten_token_tree(tree)).collect() + self.iter().map(|tree| TokenStream::flatten_token_tree(tree)).collect() } // If `vec` is not empty, try to glue `tt` onto its last token. The return @@ -665,25 +665,26 @@ impl TokenStream { } } -/// By-reference iterator over a [`TokenStream`], that produces `&TokenTree` -/// items. #[derive(Clone)] -pub struct RefTokenTreeCursor<'t> { +pub struct TokenStreamIter<'t> { stream: &'t TokenStream, index: usize, } -impl<'t> RefTokenTreeCursor<'t> { +impl<'t> TokenStreamIter<'t> { fn new(stream: &'t TokenStream) -> Self { - RefTokenTreeCursor { stream, index: 0 } + TokenStreamIter { stream, index: 0 } } - pub fn look_ahead(&self, n: usize) -> Option<&TokenTree> { - self.stream.0.get(self.index + n) + // Peeking could be done via `Peekable`, but most iterators need peeking, + // and this is simple and avoids the need to use `peekable` and `Peekable` + // at all the use sites. + pub fn peek(&self) -> Option<&'t TokenTree> { + self.stream.0.get(self.index) } } -impl<'t> Iterator for RefTokenTreeCursor<'t> { +impl<'t> Iterator for TokenStreamIter<'t> { type Item = &'t TokenTree; fn next(&mut self) -> Option<&'t TokenTree> { @@ -694,39 +695,6 @@ impl<'t> Iterator for RefTokenTreeCursor<'t> { } } -/// Owning by-value iterator over a [`TokenStream`], that produces `&TokenTree` -/// items. -/// -/// Doesn't impl `Iterator` because Rust doesn't permit an owning iterator to -/// return `&T` from `next`; the need for an explicit lifetime in the `Item` -/// associated type gets in the way. Instead, use `next_ref` (which doesn't -/// involve associated types) for getting individual elements, or -/// `RefTokenTreeCursor` if you really want an `Iterator`, e.g. in a `for` -/// loop. -#[derive(Clone, Debug)] -pub struct TokenTreeCursor { - pub stream: TokenStream, - index: usize, -} - -impl TokenTreeCursor { - fn new(stream: TokenStream) -> Self { - TokenTreeCursor { stream, index: 0 } - } - - #[inline] - pub fn next_ref(&mut self) -> Option<&TokenTree> { - self.stream.0.get(self.index).map(|tree| { - self.index += 1; - tree - }) - } - - pub fn look_ahead(&self, n: usize) -> Option<&TokenTree> { - self.stream.0.get(self.index + n) - } -} - #[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)] pub struct DelimSpan { pub open: Span, diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 70b72e88d7f49..24c1c0f221ee3 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -725,7 +725,7 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere // E.g. we have seen cases where a proc macro can handle `a :: b` but not // `a::b`. See #117433 for some examples. fn print_tts(&mut self, tts: &TokenStream, convert_dollar_crate: bool) { - let mut iter = tts.trees().peekable(); + let mut iter = tts.iter().peekable(); while let Some(tt) = iter.next() { let spacing = self.print_tt(tt, convert_dollar_crate); if let Some(next) = iter.peek() { diff --git a/compiler/rustc_builtin_macros/src/concat_idents.rs b/compiler/rustc_builtin_macros/src/concat_idents.rs index 208b499eb7afc..a721f5b84c557 100644 --- a/compiler/rustc_builtin_macros/src/concat_idents.rs +++ b/compiler/rustc_builtin_macros/src/concat_idents.rs @@ -18,7 +18,7 @@ pub(crate) fn expand_concat_idents<'cx>( } let mut res_str = String::new(); - for (i, e) in tts.trees().enumerate() { + for (i, e) in tts.iter().enumerate() { if i & 1 == 1 { match e { TokenTree::Token(Token { kind: token::Comma, .. }, _) => {} diff --git a/compiler/rustc_builtin_macros/src/trace_macros.rs b/compiler/rustc_builtin_macros/src/trace_macros.rs index 670ddc0415f4f..8264c17b4d171 100644 --- a/compiler/rustc_builtin_macros/src/trace_macros.rs +++ b/compiler/rustc_builtin_macros/src/trace_macros.rs @@ -9,9 +9,9 @@ pub(crate) fn expand_trace_macros( sp: Span, tt: TokenStream, ) -> MacroExpanderResult<'static> { - let mut cursor = tt.trees(); + let mut iter = tt.iter(); let mut err = false; - let value = match &cursor.next() { + let value = match iter.next() { Some(TokenTree::Token(token, _)) if token.is_keyword(kw::True) => true, Some(TokenTree::Token(token, _)) if token.is_keyword(kw::False) => false, _ => { @@ -19,7 +19,7 @@ pub(crate) fn expand_trace_macros( false } }; - err |= cursor.next().is_some(); + err |= iter.next().is_some(); if err { cx.dcx().emit_err(errors::TraceMacros { span: sp }); } else { diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index ed49dfe1761f2..3dc39fc131af4 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -26,7 +26,7 @@ use std::fmt::Write as _; use std::fs::{self, File}; use std::io::{self, IsTerminal, Read, Write}; use std::panic::{self, PanicHookInfo, catch_unwind}; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::process::{self, Command, Stdio}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, OnceLock}; @@ -460,7 +460,7 @@ fn run_compiler( }) } -fn dump_feature_usage_metrics(tcxt: TyCtxt<'_>, metrics_dir: &PathBuf) { +fn dump_feature_usage_metrics(tcxt: TyCtxt<'_>, metrics_dir: &Path) { let output_filenames = tcxt.output_filenames(()); let mut metrics_file_name = std::ffi::OsString::from("unstable_feature_usage_metrics-"); let mut metrics_path = output_filenames.with_directory_and_extension(metrics_dir, "json"); diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs index da6e620a24fc5..1ccb070f83a4b 100644 --- a/compiler/rustc_expand/src/mbe/metavar_expr.rs +++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs @@ -1,5 +1,5 @@ use rustc_ast::token::{self, Delimiter, IdentIsRaw, Lit, Token, TokenKind}; -use rustc_ast::tokenstream::{RefTokenTreeCursor, TokenStream, TokenTree}; +use rustc_ast::tokenstream::{TokenStream, TokenStreamIter, TokenTree}; use rustc_ast::{LitIntType, LitKind}; use rustc_ast_pretty::pprust; use rustc_errors::{Applicability, PResult}; @@ -38,14 +38,14 @@ impl MetaVarExpr { outer_span: Span, psess: &'psess ParseSess, ) -> PResult<'psess, MetaVarExpr> { - let mut tts = input.trees(); - let ident = parse_ident(&mut tts, psess, outer_span)?; - let Some(TokenTree::Delimited(.., Delimiter::Parenthesis, args)) = tts.next() else { + let mut iter = input.iter(); + let ident = parse_ident(&mut iter, psess, outer_span)?; + let Some(TokenTree::Delimited(.., Delimiter::Parenthesis, args)) = iter.next() else { let msg = "meta-variable expression parameter must be wrapped in parentheses"; return Err(psess.dcx().struct_span_err(ident.span, msg)); }; - check_trailing_token(&mut tts, psess)?; - let mut iter = args.trees(); + check_trailing_token(&mut iter, psess)?; + let mut iter = args.iter(); let rslt = match ident.as_str() { "concat" => { let mut result = Vec::new(); @@ -73,7 +73,7 @@ impl MetaVarExpr { } }; result.push(element); - if iter.look_ahead(0).is_none() { + if iter.peek().is_none() { break; } if !try_eat_comma(&mut iter) { @@ -142,7 +142,7 @@ pub(crate) enum MetaVarExprConcatElem { // Checks if there are any remaining tokens. For example, `${ignore(ident ... a b c ...)}` fn check_trailing_token<'psess>( - iter: &mut RefTokenTreeCursor<'_>, + iter: &mut TokenStreamIter<'_>, psess: &'psess ParseSess, ) -> PResult<'psess, ()> { if let Some(tt) = iter.next() { @@ -158,14 +158,14 @@ fn check_trailing_token<'psess>( /// Parse a meta-variable `count` expression: `count(ident[, depth])` fn parse_count<'psess>( - iter: &mut RefTokenTreeCursor<'_>, + iter: &mut TokenStreamIter<'_>, psess: &'psess ParseSess, span: Span, ) -> PResult<'psess, MetaVarExpr> { eat_dollar(iter, psess, span)?; let ident = parse_ident(iter, psess, span)?; let depth = if try_eat_comma(iter) { - if iter.look_ahead(0).is_none() { + if iter.peek().is_none() { return Err(psess.dcx().struct_span_err( span, "`count` followed by a comma must have an associated index indicating its depth", @@ -180,7 +180,7 @@ fn parse_count<'psess>( /// Parses the depth used by index(depth) and len(depth). fn parse_depth<'psess>( - iter: &mut RefTokenTreeCursor<'_>, + iter: &mut TokenStreamIter<'_>, psess: &'psess ParseSess, span: Span, ) -> PResult<'psess, usize> { @@ -203,7 +203,7 @@ fn parse_depth<'psess>( /// Parses an generic ident fn parse_ident<'psess>( - iter: &mut RefTokenTreeCursor<'_>, + iter: &mut TokenStreamIter<'_>, psess: &'psess ParseSess, fallback_span: Span, ) -> PResult<'psess, Ident> { @@ -235,7 +235,7 @@ fn parse_ident_from_token<'psess>( } fn parse_token<'psess, 't>( - iter: &mut RefTokenTreeCursor<'t>, + iter: &mut TokenStreamIter<'t>, psess: &'psess ParseSess, fallback_span: Span, ) -> PResult<'psess, &'t Token> { @@ -250,8 +250,8 @@ fn parse_token<'psess, 't>( /// Tries to move the iterator forward returning `true` if there is a comma. If not, then the /// iterator is not modified and the result is `false`. -fn try_eat_comma(iter: &mut RefTokenTreeCursor<'_>) -> bool { - if let Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) = iter.look_ahead(0) { +fn try_eat_comma(iter: &mut TokenStreamIter<'_>) -> bool { + if let Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) = iter.peek() { let _ = iter.next(); return true; } @@ -260,8 +260,8 @@ fn try_eat_comma(iter: &mut RefTokenTreeCursor<'_>) -> bool { /// Tries to move the iterator forward returning `true` if there is a dollar sign. If not, then the /// iterator is not modified and the result is `false`. -fn try_eat_dollar(iter: &mut RefTokenTreeCursor<'_>) -> bool { - if let Some(TokenTree::Token(Token { kind: token::Dollar, .. }, _)) = iter.look_ahead(0) { +fn try_eat_dollar(iter: &mut TokenStreamIter<'_>) -> bool { + if let Some(TokenTree::Token(Token { kind: token::Dollar, .. }, _)) = iter.peek() { let _ = iter.next(); return true; } @@ -270,12 +270,11 @@ fn try_eat_dollar(iter: &mut RefTokenTreeCursor<'_>) -> bool { /// Expects that the next item is a dollar sign. fn eat_dollar<'psess>( - iter: &mut RefTokenTreeCursor<'_>, + iter: &mut TokenStreamIter<'_>, psess: &'psess ParseSess, span: Span, ) -> PResult<'psess, ()> { - if let Some(TokenTree::Token(Token { kind: token::Dollar, .. }, _)) = iter.look_ahead(0) { - let _ = iter.next(); + if try_eat_dollar(iter) { return Ok(()); } Err(psess.dcx().struct_span_err( diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index 1addfabea234a..a27d47892e461 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -1,4 +1,5 @@ use rustc_ast::token::{self, Delimiter, IdentIsRaw, NonterminalKind, Token}; +use rustc_ast::tokenstream::TokenStreamIter; use rustc_ast::{NodeId, tokenstream}; use rustc_ast_pretty::pprust; use rustc_feature::Features; @@ -48,25 +49,25 @@ pub(super) fn parse( // For each token tree in `input`, parse the token into a `self::TokenTree`, consuming // additional trees if need be. - let mut trees = input.trees().peekable(); - while let Some(tree) = trees.next() { + let mut iter = input.iter(); + while let Some(tree) = iter.next() { // Given the parsed tree, if there is a metavar and we are expecting matchers, actually // parse out the matcher (i.e., in `$id:ident` this would parse the `:` and `ident`). - let tree = parse_tree(tree, &mut trees, parsing_patterns, sess, node_id, features, edition); + let tree = parse_tree(tree, &mut iter, parsing_patterns, sess, node_id, features, edition); match tree { TokenTree::MetaVar(start_sp, ident) if parsing_patterns => { // Not consuming the next token immediately, as it may not be a colon - let span = match trees.peek() { + let span = match iter.peek() { Some(&tokenstream::TokenTree::Token( Token { kind: token::Colon, span: colon_span }, _, )) => { // Consume the colon first - trees.next(); + iter.next(); // It's ok to consume the next tree no matter how, // since if it's not a token then it will be an invalid declaration. - match trees.next() { + match iter.next() { Some(tokenstream::TokenTree::Token(token, _)) => match token.ident() { Some((fragment, _)) => { let span = token.span.with_lo(start_sp.lo()); @@ -142,14 +143,14 @@ fn maybe_emit_macro_metavar_expr_concat_feature(features: &Features, sess: &Sess /// # Parameters /// /// - `tree`: the tree we wish to convert. -/// - `outer_trees`: an iterator over trees. We may need to read more tokens from it in order to finish +/// - `outer_iter`: an iterator over trees. We may need to read more tokens from it in order to finish /// converting `tree` /// - `parsing_patterns`: same as [parse]. /// - `sess`: the parsing session. Any errors will be emitted to this session. /// - `features`: language features so we can do feature gating. fn parse_tree<'a>( tree: &'a tokenstream::TokenTree, - outer_trees: &mut impl Iterator, + outer_iter: &mut TokenStreamIter<'a>, parsing_patterns: bool, sess: &Session, node_id: NodeId, @@ -162,15 +163,16 @@ fn parse_tree<'a>( &tokenstream::TokenTree::Token(Token { kind: token::Dollar, span: dollar_span }, _) => { // FIXME: Handle `Invisible`-delimited groups in a more systematic way // during parsing. - let mut next = outer_trees.next(); - let mut trees: Box>; - match next { + let mut next = outer_iter.next(); + let mut iter_storage; + let mut iter: &mut TokenStreamIter<'_> = match next { Some(tokenstream::TokenTree::Delimited(.., delim, tts)) if delim.skip() => { - trees = Box::new(tts.trees()); - next = trees.next(); + iter_storage = tts.iter(); + next = iter_storage.next(); + &mut iter_storage } - _ => trees = Box::new(outer_trees), - } + _ => outer_iter, + }; match next { // `tree` is followed by a delimited set of token trees. @@ -229,7 +231,7 @@ fn parse_tree<'a>( let sequence = parse(tts, parsing_patterns, sess, node_id, features, edition); // Get the Kleene operator and optional separator let (separator, kleene) = - parse_sep_and_kleene_op(&mut trees, delim_span.entire(), sess); + parse_sep_and_kleene_op(&mut iter, delim_span.entire(), sess); // Count the number of captured "names" (i.e., named metavars) let num_captures = if parsing_patterns { count_metavar_decls(&sequence) } else { 0 }; @@ -312,11 +314,11 @@ fn kleene_op(token: &Token) -> Option { /// - Ok(Ok((op, span))) if the next token tree is a KleeneOp /// - Ok(Err(tok, span)) if the next token tree is a token but not a KleeneOp /// - Err(span) if the next token tree is not a token -fn parse_kleene_op<'a>( - input: &mut impl Iterator, +fn parse_kleene_op( + iter: &mut TokenStreamIter<'_>, span: Span, ) -> Result, Span> { - match input.next() { + match iter.next() { Some(tokenstream::TokenTree::Token(token, _)) => match kleene_op(token) { Some(op) => Ok(Ok((op, token.span))), None => Ok(Err(token.clone())), @@ -333,22 +335,22 @@ fn parse_kleene_op<'a>( /// itself. Note that here we are parsing the _macro_ itself, rather than trying to match some /// stream of tokens in an invocation of a macro. /// -/// This function will take some input iterator `input` corresponding to `span` and a parsing -/// session `sess`. If the next one (or possibly two) tokens in `input` correspond to a Kleene +/// This function will take some input iterator `iter` corresponding to `span` and a parsing +/// session `sess`. If the next one (or possibly two) tokens in `iter` correspond to a Kleene /// operator and separator, then a tuple with `(separator, KleeneOp)` is returned. Otherwise, an /// error with the appropriate span is emitted to `sess` and a dummy value is returned. -fn parse_sep_and_kleene_op<'a>( - input: &mut impl Iterator, +fn parse_sep_and_kleene_op( + iter: &mut TokenStreamIter<'_>, span: Span, sess: &Session, ) -> (Option, KleeneToken) { // We basically look at two token trees here, denoted as #1 and #2 below - let span = match parse_kleene_op(input, span) { + let span = match parse_kleene_op(iter, span) { // #1 is a `?`, `+`, or `*` KleeneOp Ok(Ok((op, span))) => return (None, KleeneToken::new(op, span)), // #1 is a separator followed by #2, a KleeneOp - Ok(Err(token)) => match parse_kleene_op(input, token.span) { + Ok(Err(token)) => match parse_kleene_op(iter, token.span) { // #2 is the `?` Kleene op, which does not take a separator (error) Ok(Ok((KleeneOp::ZeroOrOne, span))) => { // Error! diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 0adff4eaf9dd9..8577aa110af86 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -111,9 +111,9 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec { let delimiter = pm::Delimiter::from_internal(delim); diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index 0b81f4693712f..d3ff1f7bebe65 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -371,10 +371,9 @@ pub(super) fn explicit_item_bounds_with_filter( associated_type_bounds(tcx, def_id, opaque_ty.bounds, opaque_ty.span, filter); return ty::EarlyBinder::bind(bounds); } - Some(ty::ImplTraitInTraitData::Impl { .. }) => span_bug!( - tcx.def_span(def_id), - "item bounds for RPITIT in impl to be fed on def-id creation" - ), + Some(ty::ImplTraitInTraitData::Impl { .. }) => { + span_bug!(tcx.def_span(def_id), "RPITIT in impl should not have item bounds") + } None => {} } diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index c5fb34740229a..8f84492146f11 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -956,6 +956,15 @@ pub(super) fn const_conditions<'tcx>( bug!("const_conditions invoked for item that is not conditionally const: {def_id:?}"); } + match tcx.opt_rpitit_info(def_id.to_def_id()) { + // RPITITs inherit const conditions of their parent fn + Some( + ty::ImplTraitInTraitData::Impl { fn_def_id } + | ty::ImplTraitInTraitData::Trait { fn_def_id, .. }, + ) => return tcx.const_conditions(fn_def_id), + None => {} + } + let (generics, trait_def_id_and_supertraits, has_parent) = match tcx.hir_node_by_def_id(def_id) { Node::Item(item) => match item.kind { @@ -1059,19 +1068,29 @@ pub(super) fn explicit_implied_const_bounds<'tcx>( bug!("const_conditions invoked for item that is not conditionally const: {def_id:?}"); } - let bounds = match tcx.hir_node_by_def_id(def_id) { - Node::Item(hir::Item { kind: hir::ItemKind::Trait(..), .. }) => { - implied_predicates_with_filter( - tcx, - def_id.to_def_id(), - PredicateFilter::SelfConstIfConst, - ) - } - Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Type(..), .. }) - | Node::OpaqueTy(_) => { + let bounds = match tcx.opt_rpitit_info(def_id.to_def_id()) { + // RPITIT's bounds are the same as opaque type bounds, but with + // a projection self type. + Some(ty::ImplTraitInTraitData::Trait { .. }) => { explicit_item_bounds_with_filter(tcx, def_id, PredicateFilter::ConstIfConst) } - _ => bug!("explicit_implied_const_bounds called on wrong item: {def_id:?}"), + Some(ty::ImplTraitInTraitData::Impl { .. }) => { + span_bug!(tcx.def_span(def_id), "RPITIT in impl should not have item bounds") + } + None => match tcx.hir_node_by_def_id(def_id) { + Node::Item(hir::Item { kind: hir::ItemKind::Trait(..), .. }) => { + implied_predicates_with_filter( + tcx, + def_id.to_def_id(), + PredicateFilter::SelfConstIfConst, + ) + } + Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Type(..), .. }) + | Node::OpaqueTy(_) => { + explicit_item_bounds_with_filter(tcx, def_id, PredicateFilter::ConstIfConst) + } + _ => bug!("explicit_implied_const_bounds called on wrong item: {def_id:?}"), + }, }; bounds.map_bound(|bounds| { diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 06392deb8ff9a..98b28240f4cb9 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -717,12 +717,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { BindingMode(def_br, Mutability::Mut) } else { // `mut` resets the binding mode on edition <= 2021 - *self - .typeck_results - .borrow_mut() - .rust_2024_migration_desugared_pats_mut() - .entry(pat_info.top_info.hir_id) - .or_default() |= pat.span.at_least_rust_2024(); + self.add_rust_2024_migration_desugared_pat( + pat_info.top_info.hir_id, + pat.span, + ident.span, + "requires binding by-value, but the implicit default is by-reference", + ); BindingMode(ByRef::No, Mutability::Mut) } } @@ -730,12 +730,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { BindingMode(ByRef::Yes(_), _) => { if matches!(def_br, ByRef::Yes(_)) { // `ref`/`ref mut` overrides the binding mode on edition <= 2021 - *self - .typeck_results - .borrow_mut() - .rust_2024_migration_desugared_pats_mut() - .entry(pat_info.top_info.hir_id) - .or_default() |= pat.span.at_least_rust_2024(); + self.add_rust_2024_migration_desugared_pat( + pat_info.top_info.hir_id, + pat.span, + ident.span, + "cannot override to bind by-reference when that is the implicit default", + ); } user_bind_annot } @@ -2265,12 +2265,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Reset binding mode on old editions if pat_info.binding_mode != ByRef::No { pat_info.binding_mode = ByRef::No; - *self - .typeck_results - .borrow_mut() - .rust_2024_migration_desugared_pats_mut() - .entry(pat_info.top_info.hir_id) - .or_default() |= pat.span.at_least_rust_2024(); + self.add_rust_2024_migration_desugared_pat( + pat_info.top_info.hir_id, + pat.span, + inner.span, + "cannot implicitly match against multiple layers of reference", + ) } } @@ -2629,4 +2629,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => (false, ty), } } + + /// Record a pattern that's invalid under Rust 2024 match ergonomics, along with a problematic + /// span, so that the pattern migration lint can desugar it during THIR construction. + fn add_rust_2024_migration_desugared_pat( + &self, + pat_id: HirId, + subpat_span: Span, + cutoff_span: Span, + detailed_label: &str, + ) { + // Try to trim the span we're labeling to just the `&` or binding mode that's an issue. + // If the subpattern's span is is from an expansion, the emitted label will not be trimmed. + let source_map = self.tcx.sess.source_map(); + let cutoff_span = source_map + .span_extend_prev_while(cutoff_span, char::is_whitespace) + .unwrap_or(cutoff_span); + // Ensure we use the syntax context and thus edition of `subpat_span`; this will be a hard + // error if the subpattern is of edition >= 2024. + let trimmed_span = subpat_span.until(cutoff_span).with_ctxt(subpat_span.ctxt()); + + // Only provide a detailed label if the problematic subpattern isn't from an expansion. + // In the case that it's from a macro, we'll add a more detailed note in the emitter. + let desc = if subpat_span.from_expansion() { + "default binding mode is reset within expansion" + } else { + detailed_label + }; + + self.typeck_results + .borrow_mut() + .rust_2024_migration_desugared_pats_mut() + .entry(pat_id) + .or_default() + .push((trimmed_span, desc.to_owned())); + } } diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 58465ec1cd9c0..6e823957cc6d6 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1828,7 +1828,7 @@ impl KeywordIdents { fn check_tokens(&mut self, cx: &EarlyContext<'_>, tokens: &TokenStream) { // Check if the preceding token is `$`, because we want to allow `$async`, etc. let mut prev_dollar = false; - for tt in tokens.trees() { + for tt in tokens.iter() { match tt { // Only report non-raw idents. TokenTree::Token(token, _) => { diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index d32666d889511..b31a4c74787fa 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -170,27 +170,11 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind { | PatKind::TupleStruct(qpath, ..) | PatKind::Struct(qpath, ..), .. - }) => { - if let QPath::TypeRelative(qpath_ty, ..) = qpath - && qpath_ty.hir_id == ty.hir_id - { - Some(path.span) - } else { - None - } - } - Node::Expr(Expr { kind: ExprKind::Path(qpath), .. }) => { - if let QPath::TypeRelative(qpath_ty, ..) = qpath - && qpath_ty.hir_id == ty.hir_id - { - Some(path.span) - } else { - None - } - } - // Can't unify these two branches because qpath below is `&&` and above is `&` - // and `A | B` paths don't play well together with adjustments, apparently. - Node::Expr(Expr { kind: ExprKind::Struct(qpath, ..), .. }) => { + }) + | Node::Expr( + Expr { kind: ExprKind::Path(qpath), .. } + | &Expr { kind: ExprKind::Struct(qpath, ..), .. }, + ) => { if let QPath::TypeRelative(qpath_ty, ..) = qpath && qpath_ty.hir_id == ty.hir_id { diff --git a/compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs b/compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs index 23f4f7289067d..ce280fef8b623 100644 --- a/compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs +++ b/compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs @@ -84,7 +84,7 @@ impl Expr2024 { let mut prev_colon = false; let mut prev_identifier = false; let mut prev_dollar = false; - for tt in tokens.trees() { + for tt in tokens.iter() { debug!( "check_tokens: {:?} - colon {prev_dollar} - ident {prev_identifier} - colon {prev_colon}", tt diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 7f3239fa57ae2..98ef7d58a5024 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1348,8 +1348,8 @@ pub struct BasicBlockData<'tcx> { } impl<'tcx> BasicBlockData<'tcx> { - pub fn new(terminator: Option>) -> BasicBlockData<'tcx> { - BasicBlockData { statements: vec![], terminator, is_cleanup: false } + pub fn new(terminator: Option>, is_cleanup: bool) -> BasicBlockData<'tcx> { + BasicBlockData { statements: vec![], terminator, is_cleanup } } /// Accessor for terminator. diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 551c113aa5922..f94f52e4f6180 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -74,9 +74,8 @@ pub struct TypeckResults<'tcx> { pat_binding_modes: ItemLocalMap, /// Top-level patterns whose match ergonomics need to be desugared by the Rust 2021 -> 2024 - /// migration lint. The boolean indicates whether the emitted diagnostic should be a hard error - /// (if any of the incompatible pattern elements are in edition 2024). - rust_2024_migration_desugared_pats: ItemLocalMap, + /// migration lint. Problematic subpatterns are stored in the `Vec` for the lint to highlight. + rust_2024_migration_desugared_pats: ItemLocalMap>, /// Stores the types which were implicitly dereferenced in pattern binding modes /// for later usage in THIR lowering. For example, @@ -419,14 +418,18 @@ impl<'tcx> TypeckResults<'tcx> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_adjustments } } - pub fn rust_2024_migration_desugared_pats(&self) -> LocalTableInContext<'_, bool> { + pub fn rust_2024_migration_desugared_pats( + &self, + ) -> LocalTableInContext<'_, Vec<(Span, String)>> { LocalTableInContext { hir_owner: self.hir_owner, data: &self.rust_2024_migration_desugared_pats, } } - pub fn rust_2024_migration_desugared_pats_mut(&mut self) -> LocalTableInContextMut<'_, bool> { + pub fn rust_2024_migration_desugared_pats_mut( + &mut self, + ) -> LocalTableInContextMut<'_, Vec<(Span, String)>> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.rust_2024_migration_desugared_pats, diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index f647486f62ade..edba247c7b0df 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -285,7 +285,7 @@ mir_build_pointer_pattern = function pointers and raw pointers not derived from mir_build_privately_uninhabited = pattern `{$witness_1}` is currently uninhabited, but this variant contains private fields which may become inhabited in the future -mir_build_rust_2024_incompatible_pat = patterns are not allowed to reset the default binding mode in edition 2024 +mir_build_rust_2024_incompatible_pat = this pattern relies on behavior which may change in edition 2024 mir_build_rustc_box_attribute_error = `#[rustc_box]` attribute used incorrectly .attributes = no other attributes may be applied diff --git a/compiler/rustc_mir_build/src/builder/cfg.rs b/compiler/rustc_mir_build/src/builder/cfg.rs index cca309115ba81..42212f2518b0a 100644 --- a/compiler/rustc_mir_build/src/builder/cfg.rs +++ b/compiler/rustc_mir_build/src/builder/cfg.rs @@ -19,7 +19,7 @@ impl<'tcx> CFG<'tcx> { // it as #[inline(never)] to keep rustc's stack use in check. #[inline(never)] pub(crate) fn start_new_block(&mut self) -> BasicBlock { - self.basic_blocks.push(BasicBlockData::new(None)) + self.basic_blocks.push(BasicBlockData::new(None, false)) } pub(crate) fn start_new_cleanup_block(&mut self) -> BasicBlock { diff --git a/compiler/rustc_mir_build/src/builder/custom/mod.rs b/compiler/rustc_mir_build/src/builder/custom/mod.rs index 34cdc288f0ba1..ca2e1dd7a1a81 100644 --- a/compiler/rustc_mir_build/src/builder/custom/mod.rs +++ b/compiler/rustc_mir_build/src/builder/custom/mod.rs @@ -64,7 +64,7 @@ pub(super) fn build_custom_mir<'tcx>( }; body.local_decls.push(LocalDecl::new(return_ty, return_ty_span)); - body.basic_blocks_mut().push(BasicBlockData::new(None)); + body.basic_blocks_mut().push(BasicBlockData::new(None, false)); body.source_scopes.push(SourceScopeData { span, parent_scope: None, diff --git a/compiler/rustc_mir_build/src/builder/custom/parse.rs b/compiler/rustc_mir_build/src/builder/custom/parse.rs index 538068e1fac8e..91e284604b68c 100644 --- a/compiler/rustc_mir_build/src/builder/custom/parse.rs +++ b/compiler/rustc_mir_build/src/builder/custom/parse.rs @@ -199,10 +199,12 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { match &self.thir[stmt].kind { StmtKind::Let { pattern, initializer: Some(initializer), .. } => { let (var, ..) = self.parse_var(pattern)?; - let mut data = BasicBlockData::new(None); - data.is_cleanup = parse_by_kind!(self, *initializer, _, "basic block declaration", - @variant(mir_basic_block, Normal) => false, - @variant(mir_basic_block, Cleanup) => true, + let data = BasicBlockData::new( + None, + parse_by_kind!(self, *initializer, _, "basic block declaration", + @variant(mir_basic_block, Normal) => false, + @variant(mir_basic_block, Cleanup) => true, + ), ); let block = self.body.basic_blocks_mut().push(data); self.block_map.insert(var, block); @@ -308,8 +310,7 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { ExprKind::Block { block } => &self.thir[*block], ); - let mut data = BasicBlockData::new(None); - data.is_cleanup = is_cleanup; + let mut data = BasicBlockData::new(None, is_cleanup); for stmt_id in &*block.stmts { let stmt = self.statement_as_expr(*stmt_id)?; let span = self.thir[stmt].span; diff --git a/compiler/rustc_mir_build/src/builder/misc.rs b/compiler/rustc_mir_build/src/builder/misc.rs index a53ae05e84f3c..9ea56a9574fde 100644 --- a/compiler/rustc_mir_build/src/builder/misc.rs +++ b/compiler/rustc_mir_build/src/builder/misc.rs @@ -56,10 +56,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { pub(crate) fn consume_by_copy_or_move(&self, place: Place<'tcx>) -> Operand<'tcx> { let tcx = self.tcx; let ty = place.ty(&self.local_decls, tcx).ty; - if !self.infcx.type_is_copy_modulo_regions(self.param_env, ty) { - Operand::Move(place) - } else { + if self.infcx.type_is_copy_modulo_regions(self.param_env, ty) { Operand::Copy(place) + } else { + Operand::Move(place) } } } diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index e77d249616807..be5f8bdffb50b 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -1,7 +1,7 @@ use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, - MultiSpan, SubdiagMessageOp, Subdiagnostic, + MultiSpan, SubdiagMessageOp, Subdiagnostic, pluralize, }; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::{self, Ty}; @@ -1088,18 +1088,20 @@ pub(crate) enum RustcBoxAttrReason { #[derive(LintDiagnostic)] #[diag(mir_build_rust_2024_incompatible_pat)] -pub(crate) struct Rust2024IncompatiblePat { +pub(crate) struct Rust2024IncompatiblePat<'a> { #[subdiagnostic] - pub(crate) sugg: Rust2024IncompatiblePatSugg, + pub(crate) sugg: Rust2024IncompatiblePatSugg<'a>, } -pub(crate) struct Rust2024IncompatiblePatSugg { +pub(crate) struct Rust2024IncompatiblePatSugg<'a> { pub(crate) suggestion: Vec<(Span, String)>, - /// Whether the incompatibility is a hard error because a relevant span is in edition 2024. - pub(crate) is_hard_error: bool, + pub(crate) ref_pattern_count: usize, + pub(crate) binding_mode_count: usize, + /// Labeled spans for subpatterns invalid in Rust 2024. + pub(crate) labels: &'a [(Span, String)], } -impl Subdiagnostic for Rust2024IncompatiblePatSugg { +impl<'a> Subdiagnostic for Rust2024IncompatiblePatSugg<'a> { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, @@ -1111,6 +1113,16 @@ impl Subdiagnostic for Rust2024IncompatiblePatSugg { } else { Applicability::MaybeIncorrect }; - diag.multipart_suggestion("desugar the match ergonomics", self.suggestion, applicability); + let plural_derefs = pluralize!(self.ref_pattern_count); + let and_modes = if self.binding_mode_count > 0 { + format!(" and variable binding mode{}", pluralize!(self.binding_mode_count)) + } else { + String::new() + }; + diag.multipart_suggestion_verbose( + format!("make the implied reference pattern{plural_derefs}{and_modes} explicit"), + self.suggestion, + applicability, + ); } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 2dbc8b7b57385..bdf243c87b6f1 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -6,6 +6,7 @@ mod const_to_pat; use std::cmp::Ordering; use rustc_abi::{FieldIdx, Integer}; +use rustc_errors::MultiSpan; use rustc_errors::codes::*; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; @@ -34,7 +35,7 @@ struct PatCtxt<'a, 'tcx> { typeck_results: &'a ty::TypeckResults<'tcx>, /// Used by the Rust 2024 migration lint. - rust_2024_migration_suggestion: Option, + rust_2024_migration_suggestion: Option>, } pub(super) fn pat_from_hir<'a, 'tcx>( @@ -50,24 +51,36 @@ pub(super) fn pat_from_hir<'a, 'tcx>( rust_2024_migration_suggestion: typeck_results .rust_2024_migration_desugared_pats() .get(pat.hir_id) - .map(|&is_hard_error| Rust2024IncompatiblePatSugg { + .map(|labels| Rust2024IncompatiblePatSugg { suggestion: Vec::new(), - is_hard_error, + ref_pattern_count: 0, + binding_mode_count: 0, + labels: labels.as_slice(), }), }; let result = pcx.lower_pattern(pat); debug!("pat_from_hir({:?}) = {:?}", pat, result); if let Some(sugg) = pcx.rust_2024_migration_suggestion { - if sugg.is_hard_error { + let mut spans = MultiSpan::from_spans(sugg.labels.iter().map(|(span, _)| *span).collect()); + for (span, label) in sugg.labels { + spans.push_span_label(*span, label.clone()); + } + // If a relevant span is from at least edition 2024, this is a hard error. + let is_hard_error = spans.primary_spans().iter().any(|span| span.at_least_rust_2024()); + if is_hard_error { let mut err = - tcx.dcx().struct_span_err(pat.span, fluent::mir_build_rust_2024_incompatible_pat); + tcx.dcx().struct_span_err(spans, fluent::mir_build_rust_2024_incompatible_pat); + if let Some(info) = lint::builtin::RUST_2024_INCOMPATIBLE_PAT.future_incompatible { + // provide the same reference link as the lint + err.note(format!("for more information, see {}", info.reference)); + } err.subdiagnostic(sugg); err.emit(); } else { tcx.emit_node_span_lint( lint::builtin::RUST_2024_INCOMPATIBLE_PAT, pat.hir_id, - pat.span, + spans, Rust2024IncompatiblePat { sugg }, ); } @@ -133,6 +146,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { }) .collect(); s.suggestion.push((pat.span.shrink_to_lo(), suggestion_str)); + s.ref_pattern_count += adjustments.len(); }; adjusted_pat @@ -371,7 +385,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { s.suggestion.push(( pat.span.with_lo(ident.span.lo()).shrink_to_lo(), sugg_str.to_owned(), - )) + )); + s.binding_mode_count += 1; } // A ref x pattern is the same node used for x, and as such it has diff --git a/compiler/rustc_mir_dataflow/src/framework/visitor.rs b/compiler/rustc_mir_dataflow/src/framework/visitor.rs index d18e9fa33f0c4..a03aecee7be12 100644 --- a/compiler/rustc_mir_dataflow/src/framework/visitor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/visitor.rs @@ -35,7 +35,6 @@ where { fn visit_block_start(&mut self, _state: &A::Domain) {} - /// // njn: grep for "before", "primary", etc. /// Called after the "early" effect of the given statement is applied to `state`. fn visit_after_early_statement_effect( &mut self, diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs index 17c8348140a3e..91e1395e76415 100644 --- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs +++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs @@ -129,18 +129,29 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch { let mut patch = MirPatch::new(body); - // create temp to store second discriminant in, `_s` in example above - let second_discriminant_temp = - patch.new_temp(opt_data.child_ty, opt_data.child_source.span); + let (second_discriminant_temp, second_operand) = if opt_data.need_hoist_discriminant { + // create temp to store second discriminant in, `_s` in example above + let second_discriminant_temp = + patch.new_temp(opt_data.child_ty, opt_data.child_source.span); - patch.add_statement(parent_end, StatementKind::StorageLive(second_discriminant_temp)); + patch.add_statement( + parent_end, + StatementKind::StorageLive(second_discriminant_temp), + ); - // create assignment of discriminant - patch.add_assign( - parent_end, - Place::from(second_discriminant_temp), - Rvalue::Discriminant(opt_data.child_place), - ); + // create assignment of discriminant + patch.add_assign( + parent_end, + Place::from(second_discriminant_temp), + Rvalue::Discriminant(opt_data.child_place), + ); + ( + Some(second_discriminant_temp), + Operand::Move(Place::from(second_discriminant_temp)), + ) + } else { + (None, Operand::Copy(opt_data.child_place)) + }; // create temp to store inequality comparison between the two discriminants, `_t` in // example above @@ -149,11 +160,9 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch { let comp_temp = patch.new_temp(comp_res_type, opt_data.child_source.span); patch.add_statement(parent_end, StatementKind::StorageLive(comp_temp)); - // create inequality comparison between the two discriminants - let comp_rvalue = Rvalue::BinaryOp( - nequal, - Box::new((parent_op.clone(), Operand::Move(Place::from(second_discriminant_temp)))), - ); + // create inequality comparison + let comp_rvalue = + Rvalue::BinaryOp(nequal, Box::new((parent_op.clone(), second_operand))); patch.add_statement( parent_end, StatementKind::Assign(Box::new((Place::from(comp_temp), comp_rvalue))), @@ -170,14 +179,17 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch { let eq_targets = SwitchTargets::new(eq_new_targets, parent_targets.otherwise()); // Create `bbEq` in example above - let eq_switch = BasicBlockData::new(Some(Terminator { - source_info: bbs[parent].terminator().source_info, - kind: TerminatorKind::SwitchInt { - // switch on the first discriminant, so we can mark the second one as dead - discr: parent_op, - targets: eq_targets, - }, - })); + let eq_switch = BasicBlockData::new( + Some(Terminator { + source_info: bbs[parent].terminator().source_info, + kind: TerminatorKind::SwitchInt { + // switch on the first discriminant, so we can mark the second one as dead + discr: parent_op, + targets: eq_targets, + }, + }), + bbs[parent].is_cleanup, + ); let eq_bb = patch.new_block(eq_switch); @@ -189,8 +201,13 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch { TerminatorKind::if_(Operand::Move(Place::from(comp_temp)), true_case, false_case), ); - // generate StorageDead for the second_discriminant_temp not in use anymore - patch.add_statement(parent_end, StatementKind::StorageDead(second_discriminant_temp)); + if let Some(second_discriminant_temp) = second_discriminant_temp { + // generate StorageDead for the second_discriminant_temp not in use anymore + patch.add_statement( + parent_end, + StatementKind::StorageDead(second_discriminant_temp), + ); + } // Generate a StorageDead for comp_temp in each of the targets, since we moved it into // the switch @@ -218,6 +235,7 @@ struct OptimizationData<'tcx> { child_place: Place<'tcx>, child_ty: Ty<'tcx>, child_source: SourceInfo, + need_hoist_discriminant: bool, } fn evaluate_candidate<'tcx>( @@ -226,49 +244,21 @@ fn evaluate_candidate<'tcx>( parent: BasicBlock, ) -> Option> { let bbs = &body.basic_blocks; + // NB: If this BB is a cleanup, we may need to figure out what else needs to be handled. + if bbs[parent].is_cleanup { + return None; + } let TerminatorKind::SwitchInt { targets, discr: parent_discr } = &bbs[parent].terminator().kind else { return None; }; let parent_ty = parent_discr.ty(body.local_decls(), tcx); - if !bbs[targets.otherwise()].is_empty_unreachable() { - // Someone could write code like this: - // ```rust - // let Q = val; - // if discriminant(P) == otherwise { - // let ptr = &mut Q as *mut _ as *mut u8; - // // It may be difficult for us to effectively determine whether values are valid. - // // Invalid values can come from all sorts of corners. - // unsafe { *ptr = 10; } - // } - // - // match P { - // A => match Q { - // A => { - // // code - // } - // _ => { - // // don't use Q - // } - // } - // _ => { - // // don't use Q - // } - // }; - // ``` - // - // Hoisting the `discriminant(Q)` out of the `A` arm causes us to compute the discriminant - // of an invalid value, which is UB. - // In order to fix this, **we would either need to show that the discriminant computation of - // `place` is computed in all branches**. - // FIXME(#95162) For the moment, we adopt a conservative approach and - // consider only the `otherwise` branch has no statements and an unreachable terminator. - return None; - } let (_, child) = targets.iter().next()?; - let child_terminator = &bbs[child].terminator(); - let TerminatorKind::SwitchInt { targets: child_targets, discr: child_discr } = - &child_terminator.kind + + let Terminator { + kind: TerminatorKind::SwitchInt { targets: child_targets, discr: child_discr }, + source_info, + } = bbs[child].terminator() else { return None; }; @@ -276,25 +266,115 @@ fn evaluate_candidate<'tcx>( if child_ty != parent_ty { return None; } - let Some(StatementKind::Assign(boxed)) = &bbs[child].statements.first().map(|x| &x.kind) else { + + // We only handle: + // ``` + // bb4: { + // _8 = discriminant((_3.1: Enum1)); + // switchInt(move _8) -> [2: bb7, otherwise: bb1]; + // } + // ``` + // and + // ``` + // bb2: { + // switchInt((_3.1: u64)) -> [1: bb5, otherwise: bb1]; + // } + // ``` + if bbs[child].statements.len() > 1 { return None; + } + + // When thie BB has exactly one statement, this statement should be discriminant. + let need_hoist_discriminant = bbs[child].statements.len() == 1; + let child_place = if need_hoist_discriminant { + if !bbs[targets.otherwise()].is_empty_unreachable() { + // Someone could write code like this: + // ```rust + // let Q = val; + // if discriminant(P) == otherwise { + // let ptr = &mut Q as *mut _ as *mut u8; + // // It may be difficult for us to effectively determine whether values are valid. + // // Invalid values can come from all sorts of corners. + // unsafe { *ptr = 10; } + // } + // + // match P { + // A => match Q { + // A => { + // // code + // } + // _ => { + // // don't use Q + // } + // } + // _ => { + // // don't use Q + // } + // }; + // ``` + // + // Hoisting the `discriminant(Q)` out of the `A` arm causes us to compute the discriminant of an + // invalid value, which is UB. + // In order to fix this, **we would either need to show that the discriminant computation of + // `place` is computed in all branches**. + // FIXME(#95162) For the moment, we adopt a conservative approach and + // consider only the `otherwise` branch has no statements and an unreachable terminator. + return None; + } + // Handle: + // ``` + // bb4: { + // _8 = discriminant((_3.1: Enum1)); + // switchInt(move _8) -> [2: bb7, otherwise: bb1]; + // } + // ``` + let [ + Statement { + kind: StatementKind::Assign(box (_, Rvalue::Discriminant(child_place))), + .. + }, + ] = bbs[child].statements.as_slice() + else { + return None; + }; + *child_place + } else { + // Handle: + // ``` + // bb2: { + // switchInt((_3.1: u64)) -> [1: bb5, otherwise: bb1]; + // } + // ``` + let Operand::Copy(child_place) = child_discr else { + return None; + }; + *child_place }; - let (_, Rvalue::Discriminant(child_place)) = &**boxed else { - return None; + let destination = if need_hoist_discriminant || bbs[targets.otherwise()].is_empty_unreachable() + { + child_targets.otherwise() + } else { + targets.otherwise() }; - let destination = child_targets.otherwise(); // Verify that the optimization is legal for each branch for (value, child) in targets.iter() { - if !verify_candidate_branch(&bbs[child], value, *child_place, destination) { + if !verify_candidate_branch( + &bbs[child], + value, + child_place, + destination, + need_hoist_discriminant, + ) { return None; } } Some(OptimizationData { destination, - child_place: *child_place, + child_place, child_ty, - child_source: child_terminator.source_info, + child_source: *source_info, + need_hoist_discriminant, }) } @@ -303,31 +383,48 @@ fn verify_candidate_branch<'tcx>( value: u128, place: Place<'tcx>, destination: BasicBlock, + need_hoist_discriminant: bool, ) -> bool { - // In order for the optimization to be correct, the branch must... - // ...have exactly one statement - if let [statement] = branch.statements.as_slice() - // ...assign the discriminant of `place` in that statement - && let StatementKind::Assign(boxed) = &statement.kind - && let (discr_place, Rvalue::Discriminant(from_place)) = &**boxed - && *from_place == place - // ...make that assignment to a local - && discr_place.projection.is_empty() - // ...terminate on a `SwitchInt` that invalidates that local - && let TerminatorKind::SwitchInt { discr: switch_op, targets, .. } = - &branch.terminator().kind - && *switch_op == Operand::Move(*discr_place) - // ...fall through to `destination` if the switch misses - && destination == targets.otherwise() - // ...have a branch for value `value` - && let mut iter = targets.iter() - && let Some((target_value, _)) = iter.next() - && target_value == value - // ...and have no more branches - && iter.next().is_none() - { - true + // In order for the optimization to be correct, the terminator must be a `SwitchInt`. + let TerminatorKind::SwitchInt { discr: switch_op, targets } = &branch.terminator().kind else { + return false; + }; + if need_hoist_discriminant { + // If we need hoist discriminant, the branch must have exactly one statement. + let [statement] = branch.statements.as_slice() else { + return false; + }; + // The statement must assign the discriminant of `place`. + let StatementKind::Assign(box (discr_place, Rvalue::Discriminant(from_place))) = + statement.kind + else { + return false; + }; + if from_place != place { + return false; + } + // The assignment must invalidate a local that terminate on a `SwitchInt`. + if !discr_place.projection.is_empty() || *switch_op != Operand::Move(discr_place) { + return false; + } } else { - false + // If we don't need hoist discriminant, the branch must not have any statements. + if !branch.statements.is_empty() { + return false; + } + // The place on `SwitchInt` must be the same. + if *switch_op != Operand::Copy(place) { + return false; + } } + // It must fall through to `destination` if the switch misses. + if destination != targets.otherwise() { + return false; + } + // It must have exactly one branch for value `value` and have no more branches. + let mut iter = targets.iter(); + let (Some((target_value, _)), None) = (iter.next(), iter.next()) else { + return false; + }; + target_value == value } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index f0acbaf56b63e..35699acb318db 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -572,11 +572,13 @@ impl<'tcx> Inliner<'tcx> { let return_block = if let Some(block) = target { // Prepare a new block for code that should execute when call returns. We don't use // target block directly since it might have other predecessors. - let mut data = BasicBlockData::new(Some(Terminator { - source_info: terminator.source_info, - kind: TerminatorKind::Goto { target: block }, - })); - data.is_cleanup = caller_body[block].is_cleanup; + let data = BasicBlockData::new( + Some(Terminator { + source_info: terminator.source_info, + kind: TerminatorKind::Goto { target: block }, + }), + caller_body[block].is_cleanup, + ); Some(caller_body.basic_blocks_mut().push(data)) } else { None diff --git a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs index 139b25be0ab2f..f01bab75c4a1d 100644 --- a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs +++ b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs @@ -96,7 +96,7 @@ impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> { typing_env, stack: Vec::with_capacity(Self::MAX_STACK_LEN), - last_bb: bbs.push(BasicBlockData::new(None)), + last_bb: bbs.push(BasicBlockData::new(None, false)), top_cleanup_bb: match tcx.sess.panic_strategy() { PanicStrategy::Unwind => { // Don't drop input arg because it's just a pointer diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index d1a725e729aa2..8417701ac0cd5 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -22,7 +22,7 @@ use rustc_errors::{ use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::source_map::Spanned; -use rustc_span::symbol::AllKeywords; +use rustc_span::symbol::used_keywords; use rustc_span::{BytePos, DUMMY_SP, Ident, Span, SpanSnippetError, Symbol, kw, sym}; use thin_vec::{ThinVec, thin_vec}; use tracing::{debug, trace}; @@ -811,12 +811,12 @@ impl<'a> Parser<'a> { // so that it gets generated only when the diagnostic needs it. // Also, it is unlikely that this list is generated multiple times because the // parser halts after execution hits this path. - let all_keywords = AllKeywords::new().collect_used(|| prev_ident.span.edition()); + let all_keywords = used_keywords(|| prev_ident.span.edition()); // Otherwise, check the previous token with all the keywords as possible candidates. // This handles code like `Struct Human;` and `While a < b {}`. - // We check the previous token only when the current token is an identifier to avoid false - // positives like suggesting keyword `for` for `extern crate foo {}`. + // We check the previous token only when the current token is an identifier to avoid + // false positives like suggesting keyword `for` for `extern crate foo {}`. if let Some(misspelled_kw) = find_similar_kw(prev_ident, &all_keywords) { err.subdiagnostic(misspelled_kw); // We don't want other suggestions to be added as they are most likely meaningless diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 5a3774642239c..2f34dcb930839 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -8,6 +8,7 @@ use ast::token::IdentIsRaw; use ast::{CoroutineKind, ForLoopKind, GenBlockKind, MatchKind, Pat, Path, PathSegment, Recovered}; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; +use rustc_ast::tokenstream::TokenTree; use rustc_ast::util::case::Case; use rustc_ast::util::classify; use rustc_ast::util::parser::{AssocOp, ExprPrecedence, Fixity, prec_let_scrutinee_needs_par}; @@ -2392,7 +2393,8 @@ impl<'a> Parser<'a> { } if self.token == TokenKind::Semi - && matches!(self.token_cursor.stack.last(), Some((.., Delimiter::Parenthesis))) + && let Some(last) = self.token_cursor.stack.last() + && let Some(TokenTree::Delimited(_, _, Delimiter::Parenthesis, _)) = last.curr() && self.may_recover() { // It is likely that the closure body is a block but where the diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 0d220e74c0e81..4bc8f5913b2b9 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -24,9 +24,7 @@ use rustc_ast::ptr::P; use rustc_ast::token::{ self, Delimiter, IdentIsRaw, InvisibleOrigin, MetaVarKind, Nonterminal, Token, TokenKind, }; -use rustc_ast::tokenstream::{ - AttrsTarget, DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree, TokenTreeCursor, -}; +use rustc_ast::tokenstream::{AttrsTarget, Spacing, TokenStream, TokenTree}; use rustc_ast::util::case::Case; use rustc_ast::{ self as ast, AnonConst, AttrArgs, AttrId, ByRef, Const, CoroutineKind, DUMMY_NODE_ID, @@ -272,21 +270,48 @@ struct CaptureState { seen_attrs: IntervalSet, } -/// Iterator over a `TokenStream` that produces `Token`s. It's a bit odd that +#[derive(Clone, Debug)] +struct TokenTreeCursor { + stream: TokenStream, + /// Points to the current token tree in the stream. In `TokenCursor::curr`, + /// this can be any token tree. In `TokenCursor::stack`, this is always a + /// `TokenTree::Delimited`. + index: usize, +} + +impl TokenTreeCursor { + #[inline] + fn new(stream: TokenStream) -> Self { + TokenTreeCursor { stream, index: 0 } + } + + #[inline] + fn curr(&self) -> Option<&TokenTree> { + self.stream.get(self.index) + } + + #[inline] + fn bump(&mut self) { + self.index += 1; + } +} + +/// A `TokenStream` cursor that produces `Token`s. It's a bit odd that /// we (a) lex tokens into a nice tree structure (`TokenStream`), and then (b) /// use this type to emit them as a linear sequence. But a linear sequence is /// what the parser expects, for the most part. #[derive(Clone, Debug)] struct TokenCursor { - // Cursor for the current (innermost) token stream. The delimiters for this - // token stream are found in `self.stack.last()`; when that is `None` then - // we are in the outermost token stream which never has delimiters. - tree_cursor: TokenTreeCursor, - - // Token streams surrounding the current one. The delimiters for stack[n]'s - // tokens are in `stack[n-1]`. `stack[0]` (when present) has no delimiters - // because it's the outermost token stream which never has delimiters. - stack: Vec<(TokenTreeCursor, DelimSpan, DelimSpacing, Delimiter)>, + // Cursor for the current (innermost) token stream. The index within the + // cursor can point to any token tree in the stream (or one past the end). + // The delimiters for this token stream are found in `self.stack.last()`; + // if that is `None` we are in the outermost token stream which never has + // delimiters. + curr: TokenTreeCursor, + + // Token streams surrounding the current one. The index within each cursor + // always points to a `TokenTree::Delimited`. + stack: Vec, } impl TokenCursor { @@ -301,32 +326,33 @@ impl TokenCursor { // FIXME: we currently don't return `Delimiter::Invisible` open/close delims. To fix // #67062 we will need to, whereupon the `delim != Delimiter::Invisible` conditions // below can be removed. - if let Some(tree) = self.tree_cursor.next_ref() { + if let Some(tree) = self.curr.curr() { match tree { &TokenTree::Token(ref token, spacing) => { debug_assert!(!matches!( token.kind, token::OpenDelim(_) | token::CloseDelim(_) )); - return (token.clone(), spacing); + let res = (token.clone(), spacing); + self.curr.bump(); + return res; } &TokenTree::Delimited(sp, spacing, delim, ref tts) => { - let trees = tts.clone().into_trees(); - self.stack.push(( - mem::replace(&mut self.tree_cursor, trees), - sp, - spacing, - delim, - )); + let trees = TokenTreeCursor::new(tts.clone()); + self.stack.push(mem::replace(&mut self.curr, trees)); if !delim.skip() { return (Token::new(token::OpenDelim(delim), sp.open), spacing.open); } // No open delimiter to return; continue on to the next iteration. } }; - } else if let Some((tree_cursor, span, spacing, delim)) = self.stack.pop() { + } else if let Some(parent) = self.stack.pop() { // We have exhausted this token stream. Move back to its parent token stream. - self.tree_cursor = tree_cursor; + let Some(&TokenTree::Delimited(span, spacing, delim, _)) = parent.curr() else { + panic!("parent should be Delimited") + }; + self.curr = parent; + self.curr.bump(); // move past the `Delimited` if !delim.skip() { return (Token::new(token::CloseDelim(delim), span.close), spacing.close); } @@ -465,7 +491,7 @@ impl<'a> Parser<'a> { capture_cfg: false, restrictions: Restrictions::empty(), expected_tokens: Vec::new(), - token_cursor: TokenCursor { tree_cursor: stream.into_trees(), stack: Vec::new() }, + token_cursor: TokenCursor { curr: TokenTreeCursor::new(stream), stack: Vec::new() }, num_bump_calls: 0, break_last_token: 0, unmatched_angle_bracket_count: 0, @@ -1191,7 +1217,7 @@ impl<'a> Parser<'a> { if dist == 1 { // The index is zero because the tree cursor's index always points // to the next token to be gotten. - match self.token_cursor.tree_cursor.look_ahead(0) { + match self.token_cursor.curr.curr() { Some(tree) => { // Indexing stayed within the current token tree. match tree { @@ -1201,12 +1227,13 @@ impl<'a> Parser<'a> { return looker(&Token::new(token::OpenDelim(delim), dspan.open)); } } - }; + } } None => { // The tree cursor lookahead went (one) past the end of the // current token tree. Try to return a close delimiter. - if let Some(&(_, span, _, delim)) = self.token_cursor.stack.last() + if let Some(last) = self.token_cursor.stack.last() + && let Some(&TokenTree::Delimited(span, _, delim, _)) = last.curr() && !delim.skip() { // We are not in the outermost token stream, so we have @@ -1398,9 +1425,10 @@ impl<'a> Parser<'a> { pub fn parse_token_tree(&mut self) -> TokenTree { match self.token.kind { token::OpenDelim(..) => { - // Grab the tokens within the delimiters. - let stream = self.token_cursor.tree_cursor.stream.clone(); - let (_, span, spacing, delim) = *self.token_cursor.stack.last().unwrap(); + // Clone the `TokenTree::Delimited` that we are currently + // within. That's what we are going to return. + let tree = self.token_cursor.stack.last().unwrap().curr().unwrap().clone(); + debug_assert_matches!(tree, TokenTree::Delimited(..)); // Advance the token cursor through the entire delimited // sequence. After getting the `OpenDelim` we are *within* the @@ -1420,7 +1448,7 @@ impl<'a> Parser<'a> { // Consume close delimiter self.bump(); - TokenTree::Delimited(span, spacing, delim, stream) + tree } token::CloseDelim(_) | token::Eof => unreachable!(), _ => { diff --git a/compiler/rustc_parse/src/parser/tests.rs b/compiler/rustc_parse/src/parser/tests.rs index ca9e78be2017a..3f8d66c2c9580 100644 --- a/compiler/rustc_parse/src/parser/tests.rs +++ b/compiler/rustc_parse/src/parser/tests.rs @@ -2286,7 +2286,7 @@ fn bad_path_expr_1() { fn string_to_tts_macro() { create_default_session_globals_then(|| { let stream = string_to_stream("macro_rules! zip (($a)=>($a))".to_string()); - let tts = &stream.trees().collect::>()[..]; + let tts = &stream.iter().collect::>()[..]; match tts { [ @@ -2298,14 +2298,14 @@ fn string_to_tts_macro() { TokenTree::Token(Token { kind: token::Ident(name_zip, IdentIsRaw::No), .. }, _), TokenTree::Delimited(.., macro_delim, macro_tts), ] if name_macro_rules == &kw::MacroRules && name_zip.as_str() == "zip" => { - let tts = ¯o_tts.trees().collect::>(); + let tts = ¯o_tts.iter().collect::>(); match &tts[..] { [ TokenTree::Delimited(.., first_delim, first_tts), TokenTree::Token(Token { kind: token::FatArrow, .. }, _), TokenTree::Delimited(.., second_delim, second_tts), ] if macro_delim == &Delimiter::Parenthesis => { - let tts = &first_tts.trees().collect::>(); + let tts = &first_tts.iter().collect::>(); match &tts[..] { [ TokenTree::Token(Token { kind: token::Dollar, .. }, _), @@ -2317,7 +2317,7 @@ fn string_to_tts_macro() { } _ => panic!("value 3: {:?} {:?}", first_delim, first_tts), } - let tts = &second_tts.trees().collect::>(); + let tts = &second_tts.iter().collect::>(); match &tts[..] { [ TokenTree::Token(Token { kind: token::Dollar, .. }, _), @@ -2545,7 +2545,7 @@ fn ttdelim_span() { .unwrap(); let ast::ExprKind::MacCall(mac) = &expr.kind else { panic!("not a macro") }; - let span = mac.args.tokens.trees().last().unwrap().span(); + let span = mac.args.tokens.iter().last().unwrap().span(); match psess.source_map().span_to_snippet(span) { Ok(s) => assert_eq!(&s[..], "{ body }"), diff --git a/compiler/rustc_parse/src/parser/tokenstream/tests.rs b/compiler/rustc_parse/src/parser/tokenstream/tests.rs index b13b68c266a0e..037b5b1a9de5d 100644 --- a/compiler/rustc_parse/src/parser/tokenstream/tests.rs +++ b/compiler/rustc_parse/src/parser/tokenstream/tests.rs @@ -23,8 +23,8 @@ fn test_concat() { let mut eq_res = TokenStream::default(); eq_res.push_stream(test_fst); eq_res.push_stream(test_snd); - assert_eq!(test_res.trees().count(), 5); - assert_eq!(eq_res.trees().count(), 5); + assert_eq!(test_res.iter().count(), 5); + assert_eq!(eq_res.iter().count(), 5); assert_eq!(test_res.eq_unspanned(&eq_res), true); }) } @@ -33,7 +33,7 @@ fn test_concat() { fn test_to_from_bijection() { create_default_session_globals_then(|| { let test_start = string_to_ts("foo::bar(baz)"); - let test_end = test_start.trees().cloned().collect(); + let test_end = test_start.iter().cloned().collect(); assert_eq!(test_start, test_end) }) } @@ -105,6 +105,6 @@ fn test_dotdotdot() { stream.push_tree(TokenTree::token_joint(token::Dot, sp(1, 2))); stream.push_tree(TokenTree::token_alone(token::Dot, sp(2, 3))); assert!(stream.eq_unspanned(&string_to_ts("..."))); - assert_eq!(stream.trees().count(), 1); + assert_eq!(stream.iter().count(), 1); }) } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 936c2ca87d69b..4784a4d1953df 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1076,7 +1076,7 @@ impl OutputFilenames { self.with_directory_and_extension(&self.out_directory, extension) } - pub fn with_directory_and_extension(&self, directory: &PathBuf, extension: &str) -> PathBuf { + pub fn with_directory_and_extension(&self, directory: &Path, extension: &str) -> PathBuf { let mut path = directory.join(&self.filestem); path.set_extension(extension); path diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 7d99ca5a31e2d..a7ff0576f92d6 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -20,18 +20,26 @@ mod tests; // The proc macro code for this is in `compiler/rustc_macros/src/symbols.rs`. symbols! { - // If you modify this list, adjust `is_special`, `is_used_keyword`/`is_unused_keyword` - // and `AllKeywords`. + // This list includes things that are definitely keywords (e.g. `if`), + // a few things that are definitely not keywords (e.g. the empty symbol, + // `{{root}}`) and things where there is disagreement between people and/or + // documents (such as the Rust Reference) about whether it is a keyword + // (e.g. `_`). + // + // If you modify this list, adjust any relevant `Symbol::{is,can_be}_*` predicates and + // `used_keywords`. // But this should rarely be necessary if the keywords are kept in alphabetic order. Keywords { // Special reserved identifiers used internally for elided lifetimes, // unnamed method parameters, crate root module, error recovery etc. + // Matching predicates: `is_any_keyword`, `is_special`/`is_reserved` Empty: "", PathRoot: "{{root}}", DollarCrate: "$crate", Underscore: "_", // Keywords that are used in stable Rust. + // Matching predicates: `is_any_keyword`, `is_used_keyword_always`/`is_reserved` As: "as", Break: "break", Const: "const", @@ -69,6 +77,7 @@ symbols! { While: "while", // Keywords that are used in unstable Rust or reserved for future use. + // Matching predicates: `is_any_keyword`, `is_unused_keyword_always`/`is_reserved` Abstract: "abstract", Become: "become", Box: "box", @@ -83,23 +92,29 @@ symbols! { Yield: "yield", // Edition-specific keywords that are used in stable Rust. + // Matching predicates: `is_any_keyword`, `is_used_keyword_conditional`/`is_reserved` (if + // the edition suffices) Async: "async", // >= 2018 Edition only Await: "await", // >= 2018 Edition only Dyn: "dyn", // >= 2018 Edition only // Edition-specific keywords that are used in unstable Rust or reserved for future use. + // Matching predicates: `is_any_keyword`, `is_unused_keyword_conditional`/`is_reserved` (if + // the edition suffices) + Gen: "gen", // >= 2024 Edition only Try: "try", // >= 2018 Edition only - // Special lifetime names + // "Lifetime keywords": regular keywords with a leading `'`. + // Matching predicates: `is_any_keyword` UnderscoreLifetime: "'_", StaticLifetime: "'static", // Weak keywords, have special meaning only in specific contexts. + // Matching predicates: `is_any_keyword` Auto: "auto", Builtin: "builtin", Catch: "catch", Default: "default", - Gen: "gen", MacroRules: "macro_rules", Raw: "raw", Reuse: "reuse", @@ -2589,6 +2604,11 @@ pub mod sym { } impl Symbol { + /// Don't use this unless you're doing something very loose and heuristic-y. + pub fn is_any_keyword(self) -> bool { + self >= kw::As && self <= kw::Yeet + } + fn is_special(self) -> bool { self <= kw::Underscore } @@ -2606,8 +2626,8 @@ impl Symbol { } fn is_unused_keyword_conditional(self, edition: impl Copy + FnOnce() -> Edition) -> bool { - self == kw::Try && edition().at_least_rust_2018() - || self == kw::Gen && edition().at_least_rust_2024() + self == kw::Gen && edition().at_least_rust_2024() + || self == kw::Try && edition().at_least_rust_2018() } pub fn is_reserved(self, edition: impl Copy + FnOnce() -> Edition) -> bool { @@ -2645,6 +2665,11 @@ impl Symbol { } impl Ident { + /// Don't use this unless you're doing something very loose and heuristic-y. + pub fn is_any_keyword(self) -> bool { + self.name.is_any_keyword() + } + /// Returns `true` for reserved identifiers used internally for elided lifetimes, /// unnamed method parameters, crate root module, error recovery etc. pub fn is_special(self) -> bool { @@ -2683,41 +2708,19 @@ impl Ident { } } -/// An iterator over all the keywords in Rust. -#[derive(Copy, Clone)] -pub struct AllKeywords { - curr_idx: u32, - end_idx: u32, -} - -impl AllKeywords { - /// Initialize a new iterator over all the keywords. - /// - /// *Note:* Please update this if a new keyword is added beyond the current - /// range. - pub fn new() -> Self { - AllKeywords { curr_idx: kw::Empty.as_u32(), end_idx: kw::Yeet.as_u32() } - } - - /// Collect all the keywords in a given edition into a vector. - pub fn collect_used(&self, edition: impl Copy + FnOnce() -> Edition) -> Vec { - self.filter(|&keyword| { - keyword.is_used_keyword_always() || keyword.is_used_keyword_conditional(edition) +/// Collect all the keywords in a given edition into a vector. +/// +/// *Note:* Please update this if a new keyword is added beyond the current +/// range. +pub fn used_keywords(edition: impl Copy + FnOnce() -> Edition) -> Vec { + (kw::Empty.as_u32()..kw::Yeet.as_u32()) + .filter_map(|kw| { + let kw = Symbol::new(kw); + if kw.is_used_keyword_always() || kw.is_used_keyword_conditional(edition) { + Some(kw) + } else { + None + } }) .collect() - } -} - -impl Iterator for AllKeywords { - type Item = Symbol; - - fn next(&mut self) -> Option { - if self.curr_idx <= self.end_idx { - let keyword = Symbol::new(self.curr_idx); - self.curr_idx += 1; - Some(keyword) - } else { - None - } - } } diff --git a/library/core/src/cell/lazy.rs b/library/core/src/cell/lazy.rs index 5ac33516684d7..84cbbc71f40ae 100644 --- a/library/core/src/cell/lazy.rs +++ b/library/core/src/cell/lazy.rs @@ -219,7 +219,7 @@ impl T> LazyCell { } impl LazyCell { - /// Returns a reference to the value if initialized, or `None` if not. + /// Returns a mutable reference to the value if initialized, or `None` if not. /// /// # Examples /// @@ -245,7 +245,7 @@ impl LazyCell { } } - /// Returns a mutable reference to the value if initialized, or `None` if not. + /// Returns a reference to the value if initialized, or `None` if not. /// /// # Examples /// diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 0cacd6e4f37ac..460b86163bdf1 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -523,6 +523,11 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, car let mut features = String::new(); + if stage != 0 && builder.config.default_codegen_backend(target).as_deref() == Some("cranelift") + { + features += "compiler-builtins-no-f16-f128 "; + } + if builder.no_std(target) == Some(true) { features += " compiler-builtins-mem"; if !target.starts_with("bpf") { diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs index 7ed01f25c94b2..fbd0dc3ec3021 100644 --- a/src/bootstrap/src/core/build_steps/setup.rs +++ b/src/bootstrap/src/core/build_steps/setup.rs @@ -209,7 +209,7 @@ pub fn setup(config: &Config, profile: Profile) { setup_config_toml(path, profile, config); } -fn setup_config_toml(path: &PathBuf, profile: Profile, config: &Config) { +fn setup_config_toml(path: &Path, profile: Profile, config: &Config) { if profile == Profile::None { return; } diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 22d361ff091cb..435216ef534c8 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -1942,7 +1942,7 @@ impl Config { ); let channel = config - .read_file_by_commit(&PathBuf::from("src/ci/channel"), commit) + .read_file_by_commit(Path::new("src/ci/channel"), commit) .trim() .to_owned(); @@ -2383,12 +2383,10 @@ impl Config { /// Return the version it would have used for the given commit. pub(crate) fn artifact_version_part(&self, commit: &str) -> String { let (channel, version) = if self.rust_info.is_managed_git_subrepository() { - let channel = self - .read_file_by_commit(&PathBuf::from("src/ci/channel"), commit) - .trim() - .to_owned(); + let channel = + self.read_file_by_commit(Path::new("src/ci/channel"), commit).trim().to_owned(); let version = - self.read_file_by_commit(&PathBuf::from("src/version"), commit).trim().to_owned(); + self.read_file_by_commit(Path::new("src/version"), commit).trim().to_owned(); (channel, version) } else { let channel = fs::read_to_string(self.src.join("src/ci/channel")); diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 9e52f6884a4bc..ffec5dc5c58e2 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2604,7 +2604,7 @@ fn filter_tokens_from_list( ) -> Vec { let mut tokens = Vec::with_capacity(args_tokens.len()); let mut skip_next_comma = false; - for token in args_tokens.trees() { + for token in args_tokens.iter() { match token { TokenTree::Token(Token { kind: TokenKind::Comma, .. }, _) if skip_next_comma => { skip_next_comma = false; diff --git a/src/librustdoc/clean/render_macro_matchers.rs b/src/librustdoc/clean/render_macro_matchers.rs index d39ecf83ac01c..3cc5f8d615a4e 100644 --- a/src/librustdoc/clean/render_macro_matchers.rs +++ b/src/librustdoc/clean/render_macro_matchers.rs @@ -131,7 +131,7 @@ fn print_tts(printer: &mut Printer<'_>, tts: &TokenStream) { use State::*; let mut state = Start; - for tt in tts.trees() { + for tt in tts.iter() { let (needs_space, next_state) = match &tt { TokenTree::Token(tt, _) => match (state, &tt.kind) { (Dollar, token::Ident(..)) => (false, DollarIdent), diff --git a/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs b/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs index b4ed8a68a325a..fd27e30a67f3b 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs @@ -15,7 +15,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute) { } if let AttrArgs::Delimited(args) = &normal_attr.item.args - && let mut tt_iter = args.tokens.trees() + && let mut tt_iter = args.tokens.iter() && let Some(TokenTree::Token( Token { kind: TokenKind::Ident(sym::expected, _), diff --git a/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs b/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs index c8f814137289c..7d86bd3e540a1 100644 --- a/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs +++ b/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs @@ -82,11 +82,11 @@ fn is_macro_export(attr: &Attribute) -> bool { fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option { let mut prev_is_dollar = false; - let mut cursor = tts.trees(); - while let Some(curr) = cursor.next() { + let mut iter = tts.iter(); + while let Some(curr) = iter.next() { if !prev_is_dollar && let Some(span) = is_crate_keyword(curr) - && let Some(next) = cursor.look_ahead(0) + && let Some(next) = iter.peek() && is_token(next, &TokenKind::PathSep) { return Some(span); diff --git a/src/tools/compiletest/src/debuggers.rs b/src/tools/compiletest/src/debuggers.rs index b605bc813f195..20e3c8dfb9ee7 100644 --- a/src/tools/compiletest/src/debuggers.rs +++ b/src/tools/compiletest/src/debuggers.rs @@ -1,6 +1,6 @@ use std::env; use std::ffi::OsString; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::process::Command; use std::sync::Arc; @@ -141,7 +141,7 @@ pub(crate) fn extract_cdb_version(full_version_line: &str) -> Option<[u16; 4]> { pub(crate) fn analyze_gdb( gdb: Option, target: &str, - android_cross_path: &PathBuf, + android_cross_path: &Path, ) -> (Option, Option) { #[cfg(not(windows))] const GDB_FALLBACK: &str = "gdb"; diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index a5a166af33b6d..d3b4631a21285 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -598,10 +598,9 @@ pub fn collect_and_make_tests(config: Arc) -> Vec { let mut collector = TestCollector { tests: vec![], found_path_stems: HashSet::new(), poisoned: false }; - collect_tests_from_dir(&cx, &mut collector, &cx.config.src_base, &PathBuf::new()) - .unwrap_or_else(|reason| { - panic!("Could not read tests from {}: {reason}", cx.config.src_base.display()) - }); + collect_tests_from_dir(&cx, &mut collector, &cx.config.src_base, Path::new("")).unwrap_or_else( + |reason| panic!("Could not read tests from {}: {reason}", cx.config.src_base.display()), + ); let TestCollector { tests, found_path_stems, poisoned } = collector; diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 8af4325e7b106..cb31b03dd2af5 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2560,7 +2560,7 @@ impl<'test> TestCx<'test> { }) } - fn delete_file(&self, file: &PathBuf) { + fn delete_file(&self, file: &Path) { if !file.exists() { // Deleting a nonexistent file would error. return; diff --git a/src/tools/rustc-perf-wrapper/src/main.rs b/src/tools/rustc-perf-wrapper/src/main.rs index 951d36b788b53..0b4c894e29d9b 100644 --- a/src/tools/rustc-perf-wrapper/src/main.rs +++ b/src/tools/rustc-perf-wrapper/src/main.rs @@ -163,7 +163,7 @@ fn apply_shared_opts(cmd: &mut Command, opts: &SharedOpts) { } } -fn execute_benchmark(cmd: &mut Command, compiler: &PathBuf) { +fn execute_benchmark(cmd: &mut Command, compiler: &Path) { cmd.arg(compiler); println!("Running `rustc-perf` using `{}`", compiler.display()); diff --git a/src/tools/rustfmt/src/macros.rs b/src/tools/rustfmt/src/macros.rs index 4083d9398f6bc..ea8ca38cb7725 100644 --- a/src/tools/rustfmt/src/macros.rs +++ b/src/tools/rustfmt/src/macros.rs @@ -13,7 +13,7 @@ use std::collections::HashMap; use std::panic::{AssertUnwindSafe, catch_unwind}; use rustc_ast::token::{BinOpToken, Delimiter, Token, TokenKind}; -use rustc_ast::tokenstream::{RefTokenTreeCursor, TokenStream, TokenTree}; +use rustc_ast::tokenstream::{TokenStream, TokenStreamIter, TokenTree}; use rustc_ast::{ast, ptr}; use rustc_ast_pretty::pprust; use rustc_span::{ @@ -443,7 +443,7 @@ pub(crate) fn rewrite_macro_def( } let ts = def.body.tokens.clone(); - let mut parser = MacroParser::new(ts.trees()); + let mut parser = MacroParser::new(ts.iter()); let parsed_def = match parser.parse() { Some(def) => def, None => return snippet, @@ -794,7 +794,7 @@ impl MacroArgParser { self.buf.clear(); } - fn add_meta_variable(&mut self, iter: &mut RefTokenTreeCursor<'_>) -> Option<()> { + fn add_meta_variable(&mut self, iter: &mut TokenStreamIter<'_>) -> Option<()> { match iter.next() { Some(&TokenTree::Token( Token { @@ -826,7 +826,7 @@ impl MacroArgParser { &mut self, inner: Vec, delim: Delimiter, - iter: &mut RefTokenTreeCursor<'_>, + iter: &mut TokenStreamIter<'_>, ) -> Option<()> { let mut buffer = String::new(); let mut first = true; @@ -926,7 +926,7 @@ impl MacroArgParser { /// Returns a collection of parsed macro def's arguments. fn parse(mut self, tokens: TokenStream) -> Option> { - let mut iter = tokens.trees(); + let mut iter = tokens.iter(); while let Some(tok) = iter.next() { match tok { @@ -1063,7 +1063,7 @@ fn format_macro_args( } fn span_for_token_stream(token_stream: &TokenStream) -> Option { - token_stream.trees().next().map(|tt| tt.span()) + token_stream.iter().next().map(|tt| tt.span()) } // We should insert a space if the next token is a: @@ -1179,18 +1179,18 @@ pub(crate) fn macro_style(mac: &ast::MacCall, context: &RewriteContext<'_>) -> D // A very simple parser that just parses a macros 2.0 definition into its branches. // Currently we do not attempt to parse any further than that. struct MacroParser<'a> { - toks: RefTokenTreeCursor<'a>, + iter: TokenStreamIter<'a>, } impl<'a> MacroParser<'a> { - const fn new(toks: RefTokenTreeCursor<'a>) -> Self { - Self { toks } + const fn new(iter: TokenStreamIter<'a>) -> Self { + Self { iter } } // (`(` ... `)` `=>` `{` ... `}`)* fn parse(&mut self) -> Option { let mut branches = vec![]; - while self.toks.look_ahead(1).is_some() { + while self.iter.peek().is_some() { branches.push(self.parse_branch()?); } @@ -1199,13 +1199,13 @@ impl<'a> MacroParser<'a> { // `(` ... `)` `=>` `{` ... `}` fn parse_branch(&mut self) -> Option { - let tok = self.toks.next()?; + let tok = self.iter.next()?; let (lo, args_paren_kind) = match tok { TokenTree::Token(..) => return None, &TokenTree::Delimited(delimited_span, _, d, _) => (delimited_span.open.lo(), d), }; let args = TokenStream::new(vec![tok.clone()]); - match self.toks.next()? { + match self.iter.next()? { TokenTree::Token( Token { kind: TokenKind::FatArrow, @@ -1215,7 +1215,7 @@ impl<'a> MacroParser<'a> { ) => {} _ => return None, } - let (mut hi, body, whole_body) = match self.toks.next()? { + let (mut hi, body, whole_body) = match self.iter.next()? { TokenTree::Token(..) => return None, TokenTree::Delimited(delimited_span, ..) => { let data = delimited_span.entire().data(); @@ -1237,10 +1237,10 @@ impl<'a> MacroParser<'a> { span, }, _, - )) = self.toks.look_ahead(0) + )) = self.iter.peek() { hi = span.hi(); - self.toks.next(); + self.iter.next(); } Some(MacroBranch { span: mk_sp(lo, hi), diff --git a/src/tools/rustfmt/src/parse/macros/mod.rs b/src/tools/rustfmt/src/parse/macros/mod.rs index 7271e73db8dd4..680a35f7e03ad 100644 --- a/src/tools/rustfmt/src/parse/macros/mod.rs +++ b/src/tools/rustfmt/src/parse/macros/mod.rs @@ -4,8 +4,7 @@ use rustc_ast::{ast, ptr}; use rustc_parse::MACRO_ARGUMENTS; use rustc_parse::parser::{ForceCollect, Parser, Recovery}; use rustc_session::parse::ParseSess; -use rustc_span::Symbol; -use rustc_span::symbol::{self, kw}; +use rustc_span::symbol; use crate::macros::MacroArg; use crate::rewrite::RewriteContext; @@ -82,18 +81,18 @@ pub(crate) struct ParsedMacroArgs { } fn check_keyword<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { - for &keyword in RUST_KW.iter() { - if parser.token.is_keyword(keyword) - && parser.look_ahead(1, |t| *t == TokenKind::Eof || *t == TokenKind::Comma) - { - parser.bump(); - return Some(MacroArg::Keyword( - symbol::Ident::with_dummy_span(keyword), - parser.prev_token.span, - )); - } + if parser.token.is_any_keyword() + && parser.look_ahead(1, |t| *t == TokenKind::Eof || *t == TokenKind::Comma) + { + let keyword = parser.token.ident().unwrap().0.name; + parser.bump(); + Some(MacroArg::Keyword( + symbol::Ident::with_dummy_span(keyword), + parser.prev_token.span, + )) + } else { + None } - None } pub(crate) fn parse_macro_args( @@ -169,65 +168,3 @@ pub(crate) fn parse_expr( let mut parser = build_parser(context, tokens); parser.parse_expr().ok() } - -const RUST_KW: [Symbol; 59] = [ - kw::PathRoot, - kw::DollarCrate, - kw::Underscore, - kw::As, - kw::Box, - kw::Break, - kw::Const, - kw::Continue, - kw::Crate, - kw::Else, - kw::Enum, - kw::Extern, - kw::False, - kw::Fn, - kw::For, - kw::If, - kw::Impl, - kw::In, - kw::Let, - kw::Loop, - kw::Match, - kw::Mod, - kw::Move, - kw::Mut, - kw::Pub, - kw::Ref, - kw::Return, - kw::SelfLower, - kw::SelfUpper, - kw::Static, - kw::Struct, - kw::Super, - kw::Trait, - kw::True, - kw::Type, - kw::Unsafe, - kw::Use, - kw::Where, - kw::While, - kw::Abstract, - kw::Become, - kw::Do, - kw::Final, - kw::Macro, - kw::Override, - kw::Priv, - kw::Typeof, - kw::Unsized, - kw::Virtual, - kw::Yield, - kw::Dyn, - kw::Async, - kw::Try, - kw::UnderscoreLifetime, - kw::StaticLifetime, - kw::Auto, - kw::Catch, - kw::Default, - kw::Union, -]; diff --git a/tests/assembly/asm/aarch64-el2vmsa.rs b/tests/assembly/asm/aarch64-el2vmsa.rs index c217f008c070a..3652d58d85a07 100644 --- a/tests/assembly/asm/aarch64-el2vmsa.rs +++ b/tests/assembly/asm/aarch64-el2vmsa.rs @@ -1,18 +1,14 @@ +//@ add-core-stubs //@ assembly-output: emit-asm //@ compile-flags: --target aarch64-unknown-linux-gnu //@ needs-llvm-components: aarch64 -#![feature(no_core, lang_items, rustc_attrs)] +#![feature(no_core)] #![crate_type = "rlib"] #![no_core] -#[rustc_builtin_macro] -macro_rules! asm { - () => {}; -} - -#[lang = "sized"] -trait Sized {} +extern crate minicore; +use minicore::*; // CHECK-LABEL: ttbr0_el2: #[no_mangle] diff --git a/tests/assembly/asm/aarch64-modifiers.rs b/tests/assembly/asm/aarch64-modifiers.rs index a4a41dd96c169..a3956d21a0677 100644 --- a/tests/assembly/asm/aarch64-modifiers.rs +++ b/tests/assembly/asm/aarch64-modifiers.rs @@ -1,29 +1,17 @@ +//@ add-core-stubs //@ assembly-output: emit-asm //@ compile-flags: -O -C panic=abort //@ compile-flags: --target aarch64-unknown-linux-gnu //@ compile-flags: -Zmerge-functions=disabled //@ needs-llvm-components: aarch64 -#![feature(no_core, lang_items, rustc_attrs)] +#![feature(no_core)] #![crate_type = "rlib"] #![no_core] #![allow(asm_sub_register)] -#[rustc_builtin_macro] -macro_rules! asm { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! stringify { - () => {}; -} - -#[lang = "sized"] -trait Sized {} -#[lang = "copy"] -trait Copy {} - -impl Copy for i32 {} +extern crate minicore; +use minicore::*; macro_rules! check { ($func:ident $reg:ident $code:literal) => { diff --git a/tests/assembly/asm/aarch64-types.rs b/tests/assembly/asm/aarch64-types.rs index 22e60cd8159b0..439385b14b05b 100644 --- a/tests/assembly/asm/aarch64-types.rs +++ b/tests/assembly/asm/aarch64-types.rs @@ -1,3 +1,4 @@ +//@ add-core-stubs //@ revisions: aarch64 arm64ec //@ assembly-output: emit-asm //@ [aarch64] compile-flags: --target aarch64-unknown-linux-gnu @@ -6,33 +7,15 @@ //@ [arm64ec] needs-llvm-components: aarch64 //@ compile-flags: -Zmerge-functions=disabled -#![feature(no_core, lang_items, rustc_attrs, repr_simd, f16, f128)] +#![feature(no_core, repr_simd, f16, f128)] #![crate_type = "rlib"] #![no_core] #![allow(asm_sub_register, non_camel_case_types)] // FIXME(f16_f128): Only needed for FIXME in check! and check_reg! -#![feature(auto_traits)] +#![feature(auto_traits, lang_items)] -#[rustc_builtin_macro] -macro_rules! asm { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! concat { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! stringify { - () => {}; -} - -#[lang = "sized"] -trait Sized {} -#[lang = "copy"] -trait Copy {} - -// Do we really need to use no_core for this?!? -impl Copy for [T; N] {} +extern crate minicore; +use minicore::*; type ptr = *mut u8; @@ -65,15 +48,6 @@ pub struct f32x4([f32; 4]); #[repr(simd)] pub struct f64x2([f64; 2]); -impl Copy for i8 {} -impl Copy for i16 {} -impl Copy for f16 {} -impl Copy for i32 {} -impl Copy for f32 {} -impl Copy for i64 {} -impl Copy for f64 {} -impl Copy for f128 {} -impl Copy for ptr {} impl Copy for i8x8 {} impl Copy for i16x4 {} impl Copy for i32x2 {} diff --git a/tests/assembly/asm/arm-modifiers.rs b/tests/assembly/asm/arm-modifiers.rs index 7d8d7e8387037..562b6bed74c35 100644 --- a/tests/assembly/asm/arm-modifiers.rs +++ b/tests/assembly/asm/arm-modifiers.rs @@ -1,3 +1,4 @@ +//@ add-core-stubs //@ assembly-output: emit-asm //@ compile-flags: -O -C panic=abort //@ compile-flags: --target armv7-unknown-linux-gnueabihf @@ -5,38 +6,17 @@ //@ compile-flags: -Zmerge-functions=disabled //@ needs-llvm-components: arm -#![feature(no_core, lang_items, rustc_attrs, repr_simd)] +#![feature(no_core, repr_simd)] #![crate_type = "rlib"] #![no_core] #![allow(asm_sub_register, non_camel_case_types)] -#[rustc_builtin_macro] -macro_rules! asm { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! concat { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! stringify { - () => {}; -} - -#[lang = "sized"] -trait Sized {} -#[lang = "copy"] -trait Copy {} - -// Do we really need to use no_core for this?!? -impl Copy for [T; N] {} +extern crate minicore; +use minicore::*; #[repr(simd)] pub struct f32x4([f32; 4]); -impl Copy for i32 {} -impl Copy for f32 {} -impl Copy for f64 {} impl Copy for f32x4 {} macro_rules! check { diff --git a/tests/assembly/asm/arm-types.rs b/tests/assembly/asm/arm-types.rs index 9cebb588aaf86..fb93f474c20eb 100644 --- a/tests/assembly/asm/arm-types.rs +++ b/tests/assembly/asm/arm-types.rs @@ -1,3 +1,4 @@ +//@ add-core-stubs //@ revisions: base d32 neon //@ assembly-output: emit-asm //@ compile-flags: --target armv7-unknown-linux-gnueabihf @@ -8,31 +9,13 @@ //@[neon] filecheck-flags: --check-prefix d32 //@ needs-llvm-components: arm -#![feature(no_core, lang_items, rustc_attrs, repr_simd, f16)] +#![feature(no_core, repr_simd, f16)] #![crate_type = "rlib"] #![no_core] #![allow(asm_sub_register, non_camel_case_types)] -#[rustc_builtin_macro] -macro_rules! asm { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! concat { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! stringify { - () => {}; -} - -#[lang = "sized"] -trait Sized {} -#[lang = "copy"] -trait Copy {} - -// Do we really need to use no_core for this?!? -impl Copy for [T; N] {} +extern crate minicore; +use minicore::*; type ptr = *mut u8; @@ -61,14 +44,6 @@ pub struct f16x8([f16; 8]); #[repr(simd)] pub struct f32x4([f32; 4]); -impl Copy for i8 {} -impl Copy for i16 {} -impl Copy for i32 {} -impl Copy for f16 {} -impl Copy for f32 {} -impl Copy for i64 {} -impl Copy for f64 {} -impl Copy for ptr {} impl Copy for i8x8 {} impl Copy for i16x4 {} impl Copy for i32x2 {} diff --git a/tests/assembly/asm/avr-modifiers.rs b/tests/assembly/asm/avr-modifiers.rs index e94375f959692..585fdd7b72534 100644 --- a/tests/assembly/asm/avr-modifiers.rs +++ b/tests/assembly/asm/avr-modifiers.rs @@ -1,34 +1,18 @@ +//@ add-core-stubs //@ assembly-output: emit-asm //@ compile-flags: --target avr-unknown-gnu-atmega328 //@ needs-llvm-components: avr -#![feature(no_core, lang_items, rustc_attrs, asm_experimental_arch)] +#![feature(no_core, asm_experimental_arch)] #![crate_type = "rlib"] #![no_core] #![allow(non_camel_case_types)] -#[rustc_builtin_macro] -macro_rules! asm { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! concat { - () => {}; -} - -#[lang = "sized"] -trait Sized {} -#[lang = "copy"] -trait Copy {} +extern crate minicore; +use minicore::*; type ptr = *const u64; -impl Copy for i8 {} -impl Copy for i16 {} -impl Copy for i32 {} -impl Copy for i64 {} -impl Copy for ptr {} - macro_rules! check { ($func:ident $hi:literal $lo:literal $reg:tt) => { #[no_mangle] diff --git a/tests/assembly/asm/avr-types.rs b/tests/assembly/asm/avr-types.rs index 88b16895e8dc0..25cf3ec3b4bde 100644 --- a/tests/assembly/asm/avr-types.rs +++ b/tests/assembly/asm/avr-types.rs @@ -1,34 +1,18 @@ +//@ add-core-stubs //@ assembly-output: emit-asm //@ compile-flags: --target avr-unknown-gnu-atmega328 //@ needs-llvm-components: avr -#![feature(no_core, lang_items, rustc_attrs, asm_experimental_arch)] +#![feature(no_core, asm_experimental_arch)] #![crate_type = "rlib"] #![no_core] #![allow(non_camel_case_types)] -#[rustc_builtin_macro] -macro_rules! asm { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! concat { - () => {}; -} - -#[lang = "sized"] -trait Sized {} -#[lang = "copy"] -trait Copy {} +extern crate minicore; +use minicore::*; type ptr = *const u64; -impl Copy for i8 {} -impl Copy for i16 {} -impl Copy for i32 {} -impl Copy for i64 {} -impl Copy for ptr {} - macro_rules! check { ($func:ident $ty:ident $class:ident) => { #[no_mangle] diff --git a/tests/assembly/asm/bpf-types.rs b/tests/assembly/asm/bpf-types.rs index 0a9ec7dd52ba7..07ea7bd5ce055 100644 --- a/tests/assembly/asm/bpf-types.rs +++ b/tests/assembly/asm/bpf-types.rs @@ -1,38 +1,18 @@ +//@ add-core-stubs //@ assembly-output: emit-asm //@ compile-flags: --target bpfel-unknown-none -C target_feature=+alu32 //@ needs-llvm-components: bpf -#![feature(no_core, lang_items, rustc_attrs, repr_simd, asm_experimental_arch)] +#![feature(no_core, asm_experimental_arch)] #![crate_type = "rlib"] #![no_core] #![allow(asm_sub_register, non_camel_case_types)] -#[rustc_builtin_macro] -macro_rules! asm { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! concat { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! stringify { - () => {}; -} - -#[lang = "sized"] -trait Sized {} -#[lang = "copy"] -trait Copy {} +extern crate minicore; +use minicore::*; type ptr = *const u64; -impl Copy for i8 {} -impl Copy for i16 {} -impl Copy for i32 {} -impl Copy for i64 {} -impl Copy for ptr {} - macro_rules! check { ($func:ident $ty:ident $class:ident) => { #[no_mangle] diff --git a/tests/assembly/asm/hexagon-types.rs b/tests/assembly/asm/hexagon-types.rs index 9389fcf9cbac9..ce80fa75b359b 100644 --- a/tests/assembly/asm/hexagon-types.rs +++ b/tests/assembly/asm/hexagon-types.rs @@ -1,38 +1,19 @@ +//@ add-core-stubs //@ assembly-output: emit-asm //@ compile-flags: --target hexagon-unknown-linux-musl //@ compile-flags: -Zmerge-functions=disabled //@ needs-llvm-components: hexagon -#![feature(no_core, lang_items, rustc_attrs, repr_simd, asm_experimental_arch)] +#![feature(no_core, asm_experimental_arch)] #![crate_type = "rlib"] #![no_core] #![allow(asm_sub_register, non_camel_case_types)] -#[rustc_builtin_macro] -macro_rules! asm { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! concat { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! stringify { - () => {}; -} - -#[lang = "sized"] -trait Sized {} -#[lang = "copy"] -trait Copy {} +extern crate minicore; +use minicore::*; type ptr = *const i32; -impl Copy for i8 {} -impl Copy for i16 {} -impl Copy for i32 {} -impl Copy for f32 {} -impl Copy for ptr {} extern "C" { fn extern_func(); static extern_static: u8; diff --git a/tests/assembly/asm/loongarch-type.rs b/tests/assembly/asm/loongarch-type.rs index c51d35876d92d..86d9e03bc9315 100644 --- a/tests/assembly/asm/loongarch-type.rs +++ b/tests/assembly/asm/loongarch-type.rs @@ -1,40 +1,19 @@ +//@ add-core-stubs //@ assembly-output: emit-asm //@ compile-flags: --target loongarch64-unknown-linux-gnu //@ compile-flags: -Zmerge-functions=disabled //@ needs-llvm-components: loongarch -#![feature(no_core, lang_items, rustc_attrs)] +#![feature(no_core)] #![crate_type = "rlib"] #![no_core] #![allow(asm_sub_register, non_camel_case_types)] -#[rustc_builtin_macro] -macro_rules! asm { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! concat { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! stringify { - () => {}; -} - -#[lang = "sized"] -trait Sized {} -#[lang = "copy"] -trait Copy {} +extern crate minicore; +use minicore::*; type ptr = *const i32; -impl Copy for i8 {} -impl Copy for i16 {} -impl Copy for i32 {} -impl Copy for i64 {} -impl Copy for f32 {} -impl Copy for f64 {} -impl Copy for ptr {} extern "C" { fn extern_func(); static extern_static: u8; diff --git a/tests/assembly/asm/m68k-types.rs b/tests/assembly/asm/m68k-types.rs index b3e86b709c3dc..9e4f6d9a1a9d5 100644 --- a/tests/assembly/asm/m68k-types.rs +++ b/tests/assembly/asm/m68k-types.rs @@ -1,34 +1,18 @@ +//@ add-core-stubs //@ assembly-output: emit-asm //@ compile-flags: --target m68k-unknown-linux-gnu //@ needs-llvm-components: m68k -#![feature(no_core, lang_items, rustc_attrs, asm_experimental_arch)] +#![feature(no_core, asm_experimental_arch)] #![crate_type = "rlib"] #![no_core] #![allow(non_camel_case_types)] -#[rustc_builtin_macro] -macro_rules! asm { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! concat { - () => {}; -} - -#[lang = "sized"] -trait Sized {} -#[lang = "copy"] -trait Copy {} +extern crate minicore; +use minicore::*; type ptr = *const u64; -impl Copy for i8 {} -impl Copy for i16 {} -impl Copy for i32 {} -impl Copy for i64 {} -impl Copy for ptr {} - macro_rules! check { ($func:ident $ty:ident $class:ident $mov:literal) => { #[no_mangle] diff --git a/tests/assembly/asm/mips-types.rs b/tests/assembly/asm/mips-types.rs index f40a28be4a7e1..00e8ce0b874aa 100644 --- a/tests/assembly/asm/mips-types.rs +++ b/tests/assembly/asm/mips-types.rs @@ -1,3 +1,4 @@ +//@ add-core-stubs //@ revisions: mips32 mips64 //@ assembly-output: emit-asm //@[mips32] compile-flags: --target mips-unknown-linux-gnu @@ -6,39 +7,16 @@ //@[mips64] needs-llvm-components: mips //@ compile-flags: -Zmerge-functions=disabled -#![feature(no_core, lang_items, rustc_attrs, repr_simd, asm_experimental_arch)] +#![feature(no_core, asm_experimental_arch)] #![crate_type = "rlib"] #![no_core] #![allow(asm_sub_register, non_camel_case_types)] -#[rustc_builtin_macro] -macro_rules! asm { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! concat { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! stringify { - () => {}; -} - -#[lang = "sized"] -trait Sized {} -#[lang = "copy"] -trait Copy {} +extern crate minicore; +use minicore::*; type ptr = *const i32; -impl Copy for i8 {} -impl Copy for u8 {} -impl Copy for i16 {} -impl Copy for i32 {} -impl Copy for i64 {} -impl Copy for f32 {} -impl Copy for f64 {} -impl Copy for ptr {} extern "C" { fn extern_func(); static extern_static: u8; diff --git a/tests/assembly/asm/msp430-types.rs b/tests/assembly/asm/msp430-types.rs index ae09b8b070da2..442dc77999f54 100644 --- a/tests/assembly/asm/msp430-types.rs +++ b/tests/assembly/asm/msp430-types.rs @@ -1,34 +1,18 @@ +//@ add-core-stubs //@ assembly-output: emit-asm //@ compile-flags: --target msp430-none-elf //@ needs-llvm-components: msp430 -#![feature(no_core, lang_items, rustc_attrs, asm_experimental_arch)] +#![feature(no_core, asm_experimental_arch)] #![crate_type = "rlib"] #![no_core] #![allow(non_camel_case_types)] -#[rustc_builtin_macro] -macro_rules! asm { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! concat { - () => {}; -} - -#[lang = "sized"] -trait Sized {} -#[lang = "copy"] -trait Copy {} +extern crate minicore; +use minicore::*; type ptr = *const i16; -impl Copy for i8 {} -impl Copy for i16 {} -impl Copy for i32 {} -impl Copy for i64 {} -impl Copy for ptr {} - macro_rules! check { ($func:ident $ty:ident $class:ident) => { #[no_mangle] diff --git a/tests/assembly/asm/nvptx-types.rs b/tests/assembly/asm/nvptx-types.rs index 0dd3162b4c02b..7e8ebd03024e4 100644 --- a/tests/assembly/asm/nvptx-types.rs +++ b/tests/assembly/asm/nvptx-types.rs @@ -1,35 +1,17 @@ +//@ add-core-stubs //@ assembly-output: emit-asm //@ compile-flags: --target nvptx64-nvidia-cuda -//@ compile-flags: --crate-type cdylib //@ needs-llvm-components: nvptx -#![feature(no_core, lang_items, rustc_attrs, asm_experimental_arch)] +#![feature(no_core, asm_experimental_arch)] +#![crate_type = "rlib"] #![no_core] -#[rustc_builtin_macro] -macro_rules! asm { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! concat { - () => {}; -} - -#[lang = "sized"] -trait Sized {} -#[lang = "copy"] -trait Copy {} +extern crate minicore; +use minicore::*; type ptr = *mut u8; -impl Copy for i8 {} -impl Copy for i16 {} -impl Copy for i32 {} -impl Copy for f32 {} -impl Copy for i64 {} -impl Copy for f64 {} -impl Copy for ptr {} - // NVPTX does not support static variables #[no_mangle] fn extern_func() {} diff --git a/tests/assembly/asm/powerpc-types.rs b/tests/assembly/asm/powerpc-types.rs index aa35c4d886581..4291e4c02f3be 100644 --- a/tests/assembly/asm/powerpc-types.rs +++ b/tests/assembly/asm/powerpc-types.rs @@ -1,3 +1,4 @@ +//@ add-core-stubs //@ revisions: powerpc powerpc_altivec powerpc_vsx powerpc64 powerpc64_vsx //@ assembly-output: emit-asm //@[powerpc] compile-flags: --target powerpc-unknown-linux-gnu @@ -12,11 +13,14 @@ //@[powerpc64_vsx] needs-llvm-components: powerpc //@ compile-flags: -Zmerge-functions=disabled -#![feature(no_core, lang_items, rustc_attrs, repr_simd, asm_experimental_arch)] +#![feature(no_core, repr_simd, asm_experimental_arch)] #![crate_type = "rlib"] #![no_core] #![allow(asm_sub_register, non_camel_case_types)] +extern crate minicore; +use minicore::*; + #[cfg_attr(altivec, cfg(not(target_feature = "altivec")))] #[cfg_attr(not(altivec), cfg(target_feature = "altivec"))] compile_error!("altivec cfg and target feature mismatch"); @@ -24,26 +28,6 @@ compile_error!("altivec cfg and target feature mismatch"); #[cfg_attr(not(vsx), cfg(target_feature = "vsx"))] compile_error!("vsx cfg and target feature mismatch"); -#[rustc_builtin_macro] -macro_rules! asm { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! concat { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! stringify { - () => {}; -} - -#[lang = "sized"] -trait Sized {} -#[lang = "copy"] -trait Copy {} - -impl Copy for [T; N] {} - type ptr = *const i32; #[repr(simd)] @@ -59,14 +43,6 @@ pub struct f32x4([f32; 4]); #[repr(simd)] pub struct f64x2([f64; 2]); -impl Copy for i8 {} -impl Copy for u8 {} -impl Copy for i16 {} -impl Copy for i32 {} -impl Copy for i64 {} -impl Copy for f32 {} -impl Copy for f64 {} -impl Copy for ptr {} impl Copy for i8x16 {} impl Copy for i16x8 {} impl Copy for i32x4 {} diff --git a/tests/assembly/asm/riscv-types.rs b/tests/assembly/asm/riscv-types.rs index 1f5d7d85b0ad9..724aa154da8c0 100644 --- a/tests/assembly/asm/riscv-types.rs +++ b/tests/assembly/asm/riscv-types.rs @@ -1,3 +1,4 @@ +//@ add-core-stubs //@ revisions: riscv64 riscv32 riscv64-zfhmin riscv32-zfhmin riscv64-zfh riscv32-zfh //@ assembly-output: emit-asm @@ -29,40 +30,16 @@ //@ compile-flags: -C target-feature=+d //@ compile-flags: -Zmerge-functions=disabled -#![feature(no_core, lang_items, rustc_attrs, f16)] +#![feature(no_core, f16)] #![crate_type = "rlib"] #![no_core] #![allow(asm_sub_register)] -#[rustc_builtin_macro] -macro_rules! asm { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! concat { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! stringify { - () => {}; -} - -#[lang = "sized"] -trait Sized {} -#[lang = "copy"] -trait Copy {} +extern crate minicore; +use minicore::*; type ptr = *mut u8; -impl Copy for i8 {} -impl Copy for i16 {} -impl Copy for f16 {} -impl Copy for i32 {} -impl Copy for f32 {} -impl Copy for i64 {} -impl Copy for f64 {} -impl Copy for ptr {} - extern "C" { fn extern_func(); static extern_static: u8; diff --git a/tests/assembly/asm/s390x-types.rs b/tests/assembly/asm/s390x-types.rs index 3da22d6c77b62..e6fe38ecb0df2 100644 --- a/tests/assembly/asm/s390x-types.rs +++ b/tests/assembly/asm/s390x-types.rs @@ -1,3 +1,4 @@ +//@ add-core-stubs //@ revisions: s390x s390x_vector //@ assembly-output: emit-asm //@[s390x] compile-flags: --target s390x-unknown-linux-gnu @@ -6,31 +7,14 @@ //@[s390x_vector] needs-llvm-components: systemz //@ compile-flags: -Zmerge-functions=disabled -#![feature(no_core, lang_items, rustc_attrs, repr_simd, f128)] +#![feature(no_core, repr_simd, f128)] #![cfg_attr(s390x_vector, feature(asm_experimental_reg))] #![crate_type = "rlib"] #![no_core] #![allow(asm_sub_register, non_camel_case_types)] -#[rustc_builtin_macro] -macro_rules! asm { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! concat { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! stringify { - () => {}; -} - -#[lang = "sized"] -trait Sized {} -#[lang = "copy"] -trait Copy {} - -impl Copy for [T; N] {} +extern crate minicore; +use minicore::*; type ptr = *const i32; @@ -47,16 +31,6 @@ pub struct f32x4([f32; 4]); #[repr(simd)] pub struct f64x2([f64; 2]); -impl Copy for i8 {} -impl Copy for u8 {} -impl Copy for i16 {} -impl Copy for i32 {} -impl Copy for i64 {} -impl Copy for i128 {} -impl Copy for f32 {} -impl Copy for f64 {} -impl Copy for f128 {} -impl Copy for ptr {} impl Copy for i8x16 {} impl Copy for i16x8 {} impl Copy for i32x4 {} diff --git a/tests/assembly/asm/sparc-types.rs b/tests/assembly/asm/sparc-types.rs index 2270679e837e3..49cc377cd9527 100644 --- a/tests/assembly/asm/sparc-types.rs +++ b/tests/assembly/asm/sparc-types.rs @@ -1,3 +1,4 @@ +//@ add-core-stubs //@ revisions: sparc sparcv8plus sparc64 //@ assembly-output: emit-asm //@[sparc] compile-flags: --target sparc-unknown-none-elf @@ -8,40 +9,16 @@ //@[sparc64] needs-llvm-components: sparc //@ compile-flags: -Zmerge-functions=disabled -#![feature(no_core, lang_items, rustc_attrs, repr_simd, asm_experimental_arch)] +#![feature(no_core, asm_experimental_arch)] #![crate_type = "rlib"] #![no_core] #![allow(asm_sub_register, non_camel_case_types)] -#[rustc_builtin_macro] -macro_rules! asm { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! concat { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! stringify { - () => {}; -} - -#[lang = "sized"] -trait Sized {} -#[lang = "copy"] -trait Copy {} +extern crate minicore; +use minicore::*; type ptr = *const i32; -impl Copy for i8 {} -impl Copy for u8 {} -impl Copy for i16 {} -impl Copy for i32 {} -impl Copy for i64 {} -impl Copy for f32 {} -impl Copy for f64 {} -impl Copy for ptr {} - extern "C" { fn extern_func(); static extern_static: u8; diff --git a/tests/assembly/asm/wasm-types.rs b/tests/assembly/asm/wasm-types.rs index fe5ce836bc6e5..78e555c53173a 100644 --- a/tests/assembly/asm/wasm-types.rs +++ b/tests/assembly/asm/wasm-types.rs @@ -1,35 +1,17 @@ +//@ add-core-stubs //@ assembly-output: emit-asm //@ compile-flags: --target wasm32-unknown-unknown -//@ compile-flags: --crate-type cdylib //@ needs-llvm-components: webassembly -#![feature(no_core, lang_items, rustc_attrs, asm_experimental_arch)] +#![feature(no_core, asm_experimental_arch)] +#![crate_type = "rlib"] #![no_core] -#[rustc_builtin_macro] -macro_rules! asm { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! concat { - () => {}; -} - -#[lang = "sized"] -trait Sized {} -#[lang = "copy"] -trait Copy {} +extern crate minicore; +use minicore::*; type ptr = *mut u8; -impl Copy for i8 {} -impl Copy for i16 {} -impl Copy for i32 {} -impl Copy for f32 {} -impl Copy for i64 {} -impl Copy for f64 {} -impl Copy for ptr {} - extern "C" { fn extern_func(); static extern_static: u8; diff --git a/tests/assembly/asm/x86-modifiers.rs b/tests/assembly/asm/x86-modifiers.rs index 5a48af9205f75..53e4b92f84acb 100644 --- a/tests/assembly/asm/x86-modifiers.rs +++ b/tests/assembly/asm/x86-modifiers.rs @@ -1,3 +1,4 @@ +//@ add-core-stubs //@ revisions: x86_64 i686 //@ assembly-output: emit-asm //@ compile-flags: -O -C panic=abort @@ -9,30 +10,13 @@ //@ compile-flags: -C target-feature=+avx512bw //@ compile-flags: -Zmerge-functions=disabled -#![feature(no_core, lang_items, rustc_attrs)] +#![feature(no_core)] #![crate_type = "rlib"] #![no_core] #![allow(asm_sub_register)] -#[rustc_builtin_macro] -macro_rules! asm { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! concat { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! stringify { - () => {}; -} - -#[lang = "sized"] -trait Sized {} -#[lang = "copy"] -trait Copy {} - -impl Copy for i32 {} +extern crate minicore; +use minicore::*; macro_rules! check { ($func:ident $modifier:literal $reg:ident $mov:literal) => { diff --git a/tests/assembly/asm/x86-types.rs b/tests/assembly/asm/x86-types.rs index 567dc7a8245fe..6120ed0d53275 100644 --- a/tests/assembly/asm/x86-types.rs +++ b/tests/assembly/asm/x86-types.rs @@ -1,3 +1,4 @@ +//@ add-core-stubs //@ revisions: x86_64 i686 //@ assembly-output: emit-asm //@[x86_64] compile-flags: --target x86_64-unknown-linux-gnu @@ -8,31 +9,13 @@ //@ compile-flags: -C target-feature=+avx512bw //@ compile-flags: -Zmerge-functions=disabled -#![feature(no_core, lang_items, rustc_attrs, repr_simd, f16, f128)] +#![feature(no_core, repr_simd, f16, f128)] #![crate_type = "rlib"] #![no_core] #![allow(asm_sub_register, non_camel_case_types)] -#[rustc_builtin_macro] -macro_rules! asm { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! concat { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! stringify { - () => {}; -} - -#[lang = "sized"] -trait Sized {} -#[lang = "copy"] -trait Copy {} - -// Do we really need to use no_core for this?!? -impl Copy for [T; N] {} +extern crate minicore; +use minicore::*; type ptr = *mut u8; @@ -90,7 +73,6 @@ macro_rules! impl_copy { } impl_copy!( - i8 i16 f16 i32 f32 i64 f64 f128 ptr i8x16 i16x8 i32x4 i64x2 f16x8 f32x4 f64x2 i8x32 i16x16 i32x8 i64x4 f16x16 f32x8 f64x4 i8x64 i16x32 i32x16 i64x8 f16x32 f32x16 f64x8 diff --git a/tests/auxiliary/minicore.rs b/tests/auxiliary/minicore.rs index 2fa0c550efbc8..a68552175c318 100644 --- a/tests/auxiliary/minicore.rs +++ b/tests/auxiliary/minicore.rs @@ -14,7 +14,7 @@ //! . // ignore-tidy-linelength -#![feature(no_core, lang_items, rustc_attrs, decl_macro, naked_functions)] +#![feature(no_core, lang_items, rustc_attrs, decl_macro, naked_functions, f16, f128)] #![allow(unused, improper_ctypes_definitions, internal_features)] #![feature(asm_experimental_arch)] #![no_std] @@ -40,7 +40,12 @@ impl LegacyReceiver for &mut T {} pub trait Copy: Sized {} impl_marker_trait!( - Copy => [ bool, char, isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64 ] + Copy => [ + bool, char, + isize, i8, i16, i32, i64, i128, + usize, u8, u16, u32, u64, u128, + f16, f32, f64, f128, + ] ); impl<'a, T: ?Sized> Copy for &'a T {} impl Copy for *const T {} @@ -88,3 +93,18 @@ pub macro naked_asm("assembly template", $(operands,)* $(options($(option),*))?) pub macro global_asm("assembly template", $(operands,)* $(options($(option),*))?) { /* compiler built-in */ } + +#[rustc_builtin_macro] +#[macro_export] +macro_rules! concat { + ($($e:expr),* $(,)?) => { + /* compiler built-in */ + }; +} +#[rustc_builtin_macro] +#[macro_export] +macro_rules! stringify { + ($($t:tt)*) => { + /* compiler built-in */ + }; +} diff --git a/tests/mir-opt/early_otherwise_branch.opt5.EarlyOtherwiseBranch.diff b/tests/mir-opt/early_otherwise_branch.opt5.EarlyOtherwiseBranch.diff new file mode 100644 index 0000000000000..b7447ef0c4699 --- /dev/null +++ b/tests/mir-opt/early_otherwise_branch.opt5.EarlyOtherwiseBranch.diff @@ -0,0 +1,77 @@ +- // MIR for `opt5` before EarlyOtherwiseBranch ++ // MIR for `opt5` after EarlyOtherwiseBranch + + fn opt5(_1: u32, _2: u32) -> u32 { + debug x => _1; + debug y => _2; + let mut _0: u32; + let mut _3: (u32, u32); + let mut _4: u32; + let mut _5: u32; ++ let mut _6: bool; + + bb0: { + StorageLive(_3); + StorageLive(_4); + _4 = copy _1; + StorageLive(_5); + _5 = copy _2; + _3 = (move _4, move _5); + StorageDead(_5); + StorageDead(_4); +- switchInt(copy (_3.0: u32)) -> [1: bb2, 2: bb3, 3: bb4, otherwise: bb1]; ++ StorageLive(_6); ++ _6 = Ne(copy (_3.0: u32), copy (_3.1: u32)); ++ switchInt(move _6) -> [0: bb6, otherwise: bb1]; + } + + bb1: { ++ StorageDead(_6); + _0 = const 0_u32; +- goto -> bb8; ++ goto -> bb5; + } + + bb2: { +- switchInt(copy (_3.1: u32)) -> [1: bb7, otherwise: bb1]; ++ _0 = const 6_u32; ++ goto -> bb5; + } + + bb3: { +- switchInt(copy (_3.1: u32)) -> [2: bb6, otherwise: bb1]; ++ _0 = const 5_u32; ++ goto -> bb5; + } + + bb4: { +- switchInt(copy (_3.1: u32)) -> [3: bb5, otherwise: bb1]; ++ _0 = const 4_u32; ++ goto -> bb5; + } + + bb5: { +- _0 = const 6_u32; +- goto -> bb8; ++ StorageDead(_3); ++ return; + } + + bb6: { +- _0 = const 5_u32; +- goto -> bb8; +- } +- +- bb7: { +- _0 = const 4_u32; +- goto -> bb8; +- } +- +- bb8: { +- StorageDead(_3); +- return; ++ StorageDead(_6); ++ switchInt(copy (_3.0: u32)) -> [1: bb4, 2: bb3, 3: bb2, otherwise: bb1]; + } + } + diff --git a/tests/mir-opt/early_otherwise_branch.opt5_failed.EarlyOtherwiseBranch.diff b/tests/mir-opt/early_otherwise_branch.opt5_failed.EarlyOtherwiseBranch.diff new file mode 100644 index 0000000000000..af16271f8b1a8 --- /dev/null +++ b/tests/mir-opt/early_otherwise_branch.opt5_failed.EarlyOtherwiseBranch.diff @@ -0,0 +1,61 @@ +- // MIR for `opt5_failed` before EarlyOtherwiseBranch ++ // MIR for `opt5_failed` after EarlyOtherwiseBranch + + fn opt5_failed(_1: u32, _2: u32) -> u32 { + debug x => _1; + debug y => _2; + let mut _0: u32; + let mut _3: (u32, u32); + let mut _4: u32; + let mut _5: u32; + + bb0: { + StorageLive(_3); + StorageLive(_4); + _4 = copy _1; + StorageLive(_5); + _5 = copy _2; + _3 = (move _4, move _5); + StorageDead(_5); + StorageDead(_4); + switchInt(copy (_3.0: u32)) -> [1: bb2, 2: bb3, 3: bb4, otherwise: bb1]; + } + + bb1: { + _0 = const 0_u32; + goto -> bb8; + } + + bb2: { + switchInt(copy (_3.1: u32)) -> [1: bb7, otherwise: bb1]; + } + + bb3: { + switchInt(copy (_3.1: u32)) -> [2: bb6, otherwise: bb1]; + } + + bb4: { + switchInt(copy (_3.1: u32)) -> [2: bb5, otherwise: bb1]; + } + + bb5: { + _0 = const 6_u32; + goto -> bb8; + } + + bb6: { + _0 = const 5_u32; + goto -> bb8; + } + + bb7: { + _0 = const 4_u32; + goto -> bb8; + } + + bb8: { + StorageDead(_3); + return; + } + } + diff --git a/tests/mir-opt/early_otherwise_branch.opt5_failed_type.EarlyOtherwiseBranch.diff b/tests/mir-opt/early_otherwise_branch.opt5_failed_type.EarlyOtherwiseBranch.diff new file mode 100644 index 0000000000000..23f14b843b37c --- /dev/null +++ b/tests/mir-opt/early_otherwise_branch.opt5_failed_type.EarlyOtherwiseBranch.diff @@ -0,0 +1,61 @@ +- // MIR for `opt5_failed_type` before EarlyOtherwiseBranch ++ // MIR for `opt5_failed_type` after EarlyOtherwiseBranch + + fn opt5_failed_type(_1: u32, _2: u64) -> u32 { + debug x => _1; + debug y => _2; + let mut _0: u32; + let mut _3: (u32, u64); + let mut _4: u32; + let mut _5: u64; + + bb0: { + StorageLive(_3); + StorageLive(_4); + _4 = copy _1; + StorageLive(_5); + _5 = copy _2; + _3 = (move _4, move _5); + StorageDead(_5); + StorageDead(_4); + switchInt(copy (_3.0: u32)) -> [1: bb2, 2: bb3, 3: bb4, otherwise: bb1]; + } + + bb1: { + _0 = const 0_u32; + goto -> bb8; + } + + bb2: { + switchInt(copy (_3.1: u64)) -> [1: bb7, otherwise: bb1]; + } + + bb3: { + switchInt(copy (_3.1: u64)) -> [2: bb6, otherwise: bb1]; + } + + bb4: { + switchInt(copy (_3.1: u64)) -> [3: bb5, otherwise: bb1]; + } + + bb5: { + _0 = const 6_u32; + goto -> bb8; + } + + bb6: { + _0 = const 5_u32; + goto -> bb8; + } + + bb7: { + _0 = const 4_u32; + goto -> bb8; + } + + bb8: { + StorageDead(_3); + return; + } + } + diff --git a/tests/mir-opt/early_otherwise_branch.rs b/tests/mir-opt/early_otherwise_branch.rs index 47bd4be295b04..382c38ceb3abb 100644 --- a/tests/mir-opt/early_otherwise_branch.rs +++ b/tests/mir-opt/early_otherwise_branch.rs @@ -78,9 +78,57 @@ fn opt4(x: Option2, y: Option2) -> u32 { } } +// EMIT_MIR early_otherwise_branch.opt5.EarlyOtherwiseBranch.diff +fn opt5(x: u32, y: u32) -> u32 { + // CHECK-LABEL: fn opt5( + // CHECK: let mut [[CMP_LOCAL:_.*]]: bool; + // CHECK: bb0: { + // CHECK: [[CMP_LOCAL]] = Ne( + // CHECK: switchInt(move [[CMP_LOCAL]]) -> [ + // CHECK-NEXT: } + match (x, y) { + (1, 1) => 4, + (2, 2) => 5, + (3, 3) => 6, + _ => 0, + } +} + +// EMIT_MIR early_otherwise_branch.opt5_failed.EarlyOtherwiseBranch.diff +fn opt5_failed(x: u32, y: u32) -> u32 { + // CHECK-LABEL: fn opt5_failed( + // CHECK: bb0: { + // CHECK-NOT: Ne( + // CHECK: switchInt( + // CHECK-NEXT: } + match (x, y) { + (1, 1) => 4, + (2, 2) => 5, + (3, 2) => 6, + _ => 0, + } +} + +// EMIT_MIR early_otherwise_branch.opt5_failed_type.EarlyOtherwiseBranch.diff +fn opt5_failed_type(x: u32, y: u64) -> u32 { + // CHECK-LABEL: fn opt5_failed_type( + // CHECK: bb0: { + // CHECK-NOT: Ne( + // CHECK: switchInt( + // CHECK-NEXT: } + match (x, y) { + (1, 1) => 4, + (2, 2) => 5, + (3, 3) => 6, + _ => 0, + } +} + fn main() { opt1(None, Some(0)); opt2(None, Some(0)); opt3(Option2::None, Option2::Some(false)); opt4(Option2::None, Option2::Some(0)); + opt5(0, 0); + opt5_failed(0, 0); } diff --git a/tests/mir-opt/early_otherwise_branch_unwind.poll.EarlyOtherwiseBranch.diff b/tests/mir-opt/early_otherwise_branch_unwind.poll.EarlyOtherwiseBranch.diff new file mode 100644 index 0000000000000..b6450e43b09e6 --- /dev/null +++ b/tests/mir-opt/early_otherwise_branch_unwind.poll.EarlyOtherwiseBranch.diff @@ -0,0 +1,126 @@ +- // MIR for `poll` before EarlyOtherwiseBranch ++ // MIR for `poll` after EarlyOtherwiseBranch + + fn poll(_1: Poll>, u8>>) -> () { + debug val => _1; + let mut _0: (); + let mut _2: isize; + let mut _3: isize; + let mut _4: isize; + let _5: std::vec::Vec; + let _6: u8; + let mut _7: bool; + let mut _8: bool; + let mut _9: isize; + scope 1 { + debug _trailers => _5; + } + scope 2 { + debug _err => _6; + } + + bb0: { + _7 = const false; + _8 = const false; + _7 = const true; + _8 = const true; + _4 = discriminant(_1); + switchInt(copy _4) -> [0: bb2, 1: bb4, otherwise: bb1]; + } + + bb1: { + unreachable; + } + + bb2: { + _3 = discriminant(((_1 as Ready).0: std::result::Result>, u8>)); + switchInt(copy _3) -> [0: bb3, 1: bb6, otherwise: bb1]; + } + + bb3: { + _2 = discriminant(((((_1 as Ready).0: std::result::Result>, u8>) as Ok).0: std::option::Option>)); + switchInt(copy _2) -> [0: bb5, 1: bb7, otherwise: bb1]; + } + + bb4: { + _0 = const (); + goto -> bb17; + } + + bb5: { + _0 = const (); + goto -> bb17; + } + + bb6: { + StorageLive(_6); + _6 = copy ((((_1 as Ready).0: std::result::Result>, u8>) as Err).0: u8); + _0 = const (); + StorageDead(_6); + goto -> bb17; + } + + bb7: { + StorageLive(_5); + _5 = move ((((((_1 as Ready).0: std::result::Result>, u8>) as Ok).0: std::option::Option>) as Some).0: std::vec::Vec); + _0 = const (); + drop(_5) -> [return: bb8, unwind: bb20]; + } + + bb8: { + StorageDead(_5); + goto -> bb17; + } + + bb9 (cleanup): { + resume; + } + + bb10: { + return; + } + + bb11: { + switchInt(copy _7) -> [0: bb12, otherwise: bb16]; + } + + bb12: { + _7 = const false; + goto -> bb10; + } + + bb13: { + switchInt(copy _8) -> [0: bb14, otherwise: bb15]; + } + + bb14: { + _8 = const false; + goto -> bb12; + } + + bb15: { + goto -> bb14; + } + + bb16: { + _9 = discriminant(((_1 as Ready).0: std::result::Result>, u8>)); + switchInt(move _9) -> [0: bb13, otherwise: bb12]; + } + + bb17: { + switchInt(copy _4) -> [0: bb11, otherwise: bb10]; + } + + bb18 (cleanup): { + switchInt(copy _3) -> [0: bb19, otherwise: bb9]; + } + + bb19 (cleanup): { + goto -> bb9; + } + + bb20 (cleanup): { + switchInt(copy _4) -> [0: bb18, otherwise: bb9]; + } + } + diff --git a/tests/mir-opt/early_otherwise_branch_unwind.rs b/tests/mir-opt/early_otherwise_branch_unwind.rs new file mode 100644 index 0000000000000..cbccf11729ab3 --- /dev/null +++ b/tests/mir-opt/early_otherwise_branch_unwind.rs @@ -0,0 +1,38 @@ +//@ test-mir-pass: EarlyOtherwiseBranch +//@ compile-flags: -Zmir-enable-passes=+GVN,+SimplifyLocals-after-value-numbering +//@ needs-unwind + +use std::task::Poll; + +// We find a matching pattern in the unwind path, +// and we need to create a cleanup BB for this case to meet the unwind invariants rule. +// NB: This transform is not happening currently. + +// EMIT_MIR early_otherwise_branch_unwind.unwind.EarlyOtherwiseBranch.diff +fn unwind(val: Option>>) { + // CHECK-LABEL: fn unwind( + // CHECK: drop({{.*}}) -> [return: bb{{.*}}, unwind: [[PARENT_UNWIND_BB:bb.*]]]; + // CHECK: [[PARENT_UNWIND_BB]] (cleanup): { + // CHECK-NEXT: switchInt + match val { + Some(Some(Some(_v))) => {} + Some(Some(None)) => {} + Some(None) => {} + None => {} + } +} + +// From https://github.com/rust-lang/rust/issues/130769#issuecomment-2370443086. +// EMIT_MIR early_otherwise_branch_unwind.poll.EarlyOtherwiseBranch.diff +pub fn poll(val: Poll>, u8>>) { + // CHECK-LABEL: fn poll( + // CHECK: drop({{.*}}) -> [return: bb{{.*}}, unwind: [[PARENT_UNWIND_BB:bb.*]]]; + // CHECK: [[PARENT_UNWIND_BB]] (cleanup): { + // CHECK-NEXT: switchInt + match val { + Poll::Ready(Ok(Some(_trailers))) => {} + Poll::Ready(Err(_err)) => {} + Poll::Ready(Ok(None)) => {} + Poll::Pending => {} + } +} diff --git a/tests/mir-opt/early_otherwise_branch_unwind.unwind.EarlyOtherwiseBranch.diff b/tests/mir-opt/early_otherwise_branch_unwind.unwind.EarlyOtherwiseBranch.diff new file mode 100644 index 0000000000000..2b2c007e082a7 --- /dev/null +++ b/tests/mir-opt/early_otherwise_branch_unwind.unwind.EarlyOtherwiseBranch.diff @@ -0,0 +1,119 @@ +- // MIR for `unwind` before EarlyOtherwiseBranch ++ // MIR for `unwind` after EarlyOtherwiseBranch + + fn unwind(_1: Option>>) -> () { + debug val => _1; + let mut _0: (); + let mut _2: isize; + let mut _3: isize; + let mut _4: isize; + let _5: T; + let mut _6: bool; + let mut _7: bool; + let mut _8: isize; + scope 1 { + debug _v => _5; + } + + bb0: { + _6 = const false; + _7 = const false; + _6 = const true; + _7 = const true; + _4 = discriminant(_1); + switchInt(copy _4) -> [0: bb4, 1: bb2, otherwise: bb1]; + } + + bb1: { + unreachable; + } + + bb2: { + _3 = discriminant(((_1 as Some).0: std::option::Option>)); + switchInt(copy _3) -> [0: bb5, 1: bb3, otherwise: bb1]; + } + + bb3: { + _2 = discriminant(((((_1 as Some).0: std::option::Option>) as Some).0: std::option::Option)); + switchInt(copy _2) -> [0: bb6, 1: bb7, otherwise: bb1]; + } + + bb4: { + _0 = const (); + goto -> bb17; + } + + bb5: { + _0 = const (); + goto -> bb17; + } + + bb6: { + _0 = const (); + goto -> bb17; + } + + bb7: { + StorageLive(_5); + _5 = move ((((((_1 as Some).0: std::option::Option>) as Some).0: std::option::Option) as Some).0: T); + _0 = const (); + drop(_5) -> [return: bb8, unwind: bb20]; + } + + bb8: { + StorageDead(_5); + goto -> bb17; + } + + bb9 (cleanup): { + resume; + } + + bb10: { + return; + } + + bb11: { + switchInt(copy _6) -> [0: bb12, otherwise: bb16]; + } + + bb12: { + _6 = const false; + goto -> bb10; + } + + bb13: { + switchInt(copy _7) -> [0: bb14, otherwise: bb15]; + } + + bb14: { + _7 = const false; + goto -> bb12; + } + + bb15: { + goto -> bb14; + } + + bb16: { + _8 = discriminant(((_1 as Some).0: std::option::Option>)); + switchInt(move _8) -> [1: bb13, otherwise: bb12]; + } + + bb17: { + switchInt(copy _4) -> [1: bb11, otherwise: bb10]; + } + + bb18 (cleanup): { + switchInt(copy _3) -> [1: bb19, otherwise: bb9]; + } + + bb19 (cleanup): { + goto -> bb9; + } + + bb20 (cleanup): { + switchInt(copy _4) -> [1: bb18, otherwise: bb9]; + } + } + diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.fixed b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.fixed index 086671e69cba1..e2b2c98761046 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.fixed +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.fixed @@ -23,22 +23,22 @@ fn main() { assert_type_eq(x, &mut 0u8); let &Foo(mut x) = &Foo(0); - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); let &mut Foo(mut x) = &mut Foo(0); - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); let &Foo(ref x) = &Foo(0); - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, &0u8); let &mut Foo(ref x) = &mut Foo(0); - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, &0u8); @@ -55,22 +55,22 @@ fn main() { assert_type_eq(x, &0u8); let &Foo(&x) = &Foo(&0); - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); let &Foo(&mut x) = &Foo(&mut 0); - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); let &mut Foo(&x) = &mut Foo(&0); - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); let &mut Foo(&mut x) = &mut Foo(&mut 0); - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); @@ -79,25 +79,25 @@ fn main() { } if let &&&&&Some(&x) = &&&&&Some(&0u8) { - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); } if let &&&&&Some(&mut x) = &&&&&Some(&mut 0u8) { - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); } if let &&&&&mut Some(&x) = &&&&&mut Some(&0u8) { - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); } if let &mut Some(&mut Some(&mut Some(ref mut x))) = &mut Some(&mut Some(&mut Some(0u8))) { - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, &mut 0u8); } @@ -109,20 +109,20 @@ fn main() { } let &Struct { ref a, mut b, ref c } = &Struct { a: 0, b: 0, c: 0 }; - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, &0u32); assert_type_eq(b, 0u32); let &Struct { a: &a, ref b, ref c } = &Struct { a: &0, b: &0, c: &0 }; - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, 0u32); assert_type_eq(b, &&0u32); assert_type_eq(c, &&0u32); if let &Struct { a: &Some(a), b: &Some(&b), c: &Some(ref c) } = - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 //~| WARN: this changes meaning in Rust 2024 &(Struct { a: &Some(&0), b: &Some(&0), c: &Some(&0) }) { @@ -135,7 +135,7 @@ fn main() { // The two patterns are the same syntactically, but because they're defined in different // editions they don't mean the same thing. &(Some(mut x), migration_lint_macros::mixed_edition_pat!(y)) => { - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 assert_type_eq(x, 0u32); assert_type_eq(y, 0u32); } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.rs index acceafdb7ec06..098540adfa2cf 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.rs @@ -23,22 +23,22 @@ fn main() { assert_type_eq(x, &mut 0u8); let Foo(mut x) = &Foo(0); - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); let Foo(mut x) = &mut Foo(0); - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); let Foo(ref x) = &Foo(0); - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, &0u8); let Foo(ref x) = &mut Foo(0); - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, &0u8); @@ -55,22 +55,22 @@ fn main() { assert_type_eq(x, &0u8); let Foo(&x) = &Foo(&0); - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); let Foo(&mut x) = &Foo(&mut 0); - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); let Foo(&x) = &mut Foo(&0); - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); let Foo(&mut x) = &mut Foo(&mut 0); - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); @@ -79,25 +79,25 @@ fn main() { } if let Some(&x) = &&&&&Some(&0u8) { - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); } if let Some(&mut x) = &&&&&Some(&mut 0u8) { - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); } if let Some(&x) = &&&&&mut Some(&0u8) { - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); } if let Some(&mut Some(Some(x))) = &mut Some(&mut Some(&mut Some(0u8))) { - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, &mut 0u8); } @@ -109,20 +109,20 @@ fn main() { } let Struct { a, mut b, c } = &Struct { a: 0, b: 0, c: 0 }; - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, &0u32); assert_type_eq(b, 0u32); let Struct { a: &a, b, ref c } = &Struct { a: &0, b: &0, c: &0 }; - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, 0u32); assert_type_eq(b, &&0u32); assert_type_eq(c, &&0u32); if let Struct { a: &Some(a), b: Some(&b), c: Some(c) } = - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 //~| WARN: this changes meaning in Rust 2024 &(Struct { a: &Some(&0), b: &Some(&0), c: &Some(&0) }) { @@ -135,7 +135,7 @@ fn main() { // The two patterns are the same syntactically, but because they're defined in different // editions they don't mean the same thing. (Some(mut x), migration_lint_macros::mixed_edition_pat!(y)) => { - //~^ ERROR: patterns are not allowed to reset the default binding mode + //~^ ERROR: this pattern relies on behavior which may change in edition 2024 assert_type_eq(x, 0u32); assert_type_eq(y, 0u32); } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.stderr index 91aa987c7371f..83346b9dd4a84 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.stderr @@ -1,10 +1,8 @@ -error: patterns are not allowed to reset the default binding mode in edition 2024 - --> $DIR/migration_lint.rs:25:9 +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/migration_lint.rs:25:13 | LL | let Foo(mut x) = &Foo(0); - | -^^^^^^^^^ - | | - | help: desugar the match ergonomics: `&` + | ^^^ requires binding by-value, but the implicit default is by-reference | = warning: this changes meaning in Rust 2024 = note: for more information, see @@ -13,176 +11,211 @@ note: the lint level is defined here | LL | #![deny(rust_2024_incompatible_pat)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: make the implied reference pattern explicit + | +LL | let &Foo(mut x) = &Foo(0); + | + -error: patterns are not allowed to reset the default binding mode in edition 2024 - --> $DIR/migration_lint.rs:30:9 +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/migration_lint.rs:30:13 | LL | let Foo(mut x) = &mut Foo(0); - | -^^^^^^^^^ - | | - | help: desugar the match ergonomics: `&mut` + | ^^^ requires binding by-value, but the implicit default is by-reference | = warning: this changes meaning in Rust 2024 = note: for more information, see +help: make the implied reference pattern explicit + | +LL | let &mut Foo(mut x) = &mut Foo(0); + | ++++ -error: patterns are not allowed to reset the default binding mode in edition 2024 - --> $DIR/migration_lint.rs:35:9 +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/migration_lint.rs:35:13 | LL | let Foo(ref x) = &Foo(0); - | -^^^^^^^^^ - | | - | help: desugar the match ergonomics: `&` + | ^^^ cannot override to bind by-reference when that is the implicit default | = warning: this changes meaning in Rust 2024 = note: for more information, see +help: make the implied reference pattern explicit + | +LL | let &Foo(ref x) = &Foo(0); + | + -error: patterns are not allowed to reset the default binding mode in edition 2024 - --> $DIR/migration_lint.rs:40:9 +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/migration_lint.rs:40:13 | LL | let Foo(ref x) = &mut Foo(0); - | -^^^^^^^^^ - | | - | help: desugar the match ergonomics: `&mut` + | ^^^ cannot override to bind by-reference when that is the implicit default | = warning: this changes meaning in Rust 2024 = note: for more information, see +help: make the implied reference pattern explicit + | +LL | let &mut Foo(ref x) = &mut Foo(0); + | ++++ -error: patterns are not allowed to reset the default binding mode in edition 2024 - --> $DIR/migration_lint.rs:57:9 +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/migration_lint.rs:57:13 | LL | let Foo(&x) = &Foo(&0); - | -^^^^^^ - | | - | help: desugar the match ergonomics: `&` + | ^ cannot implicitly match against multiple layers of reference | = warning: this changes meaning in Rust 2024 = note: for more information, see +help: make the implied reference pattern explicit + | +LL | let &Foo(&x) = &Foo(&0); + | + -error: patterns are not allowed to reset the default binding mode in edition 2024 - --> $DIR/migration_lint.rs:62:9 +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/migration_lint.rs:62:13 | LL | let Foo(&mut x) = &Foo(&mut 0); - | -^^^^^^^^^^ - | | - | help: desugar the match ergonomics: `&` + | ^^^^ cannot implicitly match against multiple layers of reference | = warning: this changes meaning in Rust 2024 = note: for more information, see +help: make the implied reference pattern explicit + | +LL | let &Foo(&mut x) = &Foo(&mut 0); + | + -error: patterns are not allowed to reset the default binding mode in edition 2024 - --> $DIR/migration_lint.rs:67:9 +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/migration_lint.rs:67:13 | LL | let Foo(&x) = &mut Foo(&0); - | -^^^^^^ - | | - | help: desugar the match ergonomics: `&mut` + | ^ cannot implicitly match against multiple layers of reference | = warning: this changes meaning in Rust 2024 = note: for more information, see +help: make the implied reference pattern explicit + | +LL | let &mut Foo(&x) = &mut Foo(&0); + | ++++ -error: patterns are not allowed to reset the default binding mode in edition 2024 - --> $DIR/migration_lint.rs:72:9 +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/migration_lint.rs:72:13 | LL | let Foo(&mut x) = &mut Foo(&mut 0); - | -^^^^^^^^^^ - | | - | help: desugar the match ergonomics: `&mut` + | ^^^^ cannot implicitly match against multiple layers of reference | = warning: this changes meaning in Rust 2024 = note: for more information, see +help: make the implied reference pattern explicit + | +LL | let &mut Foo(&mut x) = &mut Foo(&mut 0); + | ++++ -error: patterns are not allowed to reset the default binding mode in edition 2024 - --> $DIR/migration_lint.rs:81:12 +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/migration_lint.rs:81:17 | LL | if let Some(&x) = &&&&&Some(&0u8) { - | -^^^^^^^ - | | - | help: desugar the match ergonomics: `&&&&&` + | ^ cannot implicitly match against multiple layers of reference | = warning: this changes meaning in Rust 2024 = note: for more information, see +help: make the implied reference patterns explicit + | +LL | if let &&&&&Some(&x) = &&&&&Some(&0u8) { + | +++++ -error: patterns are not allowed to reset the default binding mode in edition 2024 - --> $DIR/migration_lint.rs:87:12 +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/migration_lint.rs:87:17 | LL | if let Some(&mut x) = &&&&&Some(&mut 0u8) { - | -^^^^^^^^^^^ - | | - | help: desugar the match ergonomics: `&&&&&` + | ^^^^ cannot implicitly match against multiple layers of reference | = warning: this changes meaning in Rust 2024 = note: for more information, see +help: make the implied reference patterns explicit + | +LL | if let &&&&&Some(&mut x) = &&&&&Some(&mut 0u8) { + | +++++ -error: patterns are not allowed to reset the default binding mode in edition 2024 - --> $DIR/migration_lint.rs:93:12 +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/migration_lint.rs:93:17 | LL | if let Some(&x) = &&&&&mut Some(&0u8) { - | -^^^^^^^ - | | - | help: desugar the match ergonomics: `&&&&&mut` + | ^ cannot implicitly match against multiple layers of reference | = warning: this changes meaning in Rust 2024 = note: for more information, see +help: make the implied reference patterns explicit + | +LL | if let &&&&&mut Some(&x) = &&&&&mut Some(&0u8) { + | ++++++++ -error: patterns are not allowed to reset the default binding mode in edition 2024 - --> $DIR/migration_lint.rs:99:12 +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/migration_lint.rs:99:17 | LL | if let Some(&mut Some(Some(x))) = &mut Some(&mut Some(&mut Some(0u8))) { - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^ cannot implicitly match against multiple layers of reference | = warning: this changes meaning in Rust 2024 = note: for more information, see -help: desugar the match ergonomics +help: make the implied reference patterns and variable binding mode explicit | LL | if let &mut Some(&mut Some(&mut Some(ref mut x))) = &mut Some(&mut Some(&mut Some(0u8))) { | ++++ ++++ +++++++ -error: patterns are not allowed to reset the default binding mode in edition 2024 - --> $DIR/migration_lint.rs:111:9 +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/migration_lint.rs:111:21 | LL | let Struct { a, mut b, c } = &Struct { a: 0, b: 0, c: 0 }; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^ requires binding by-value, but the implicit default is by-reference | = warning: this changes meaning in Rust 2024 = note: for more information, see -help: desugar the match ergonomics +help: make the implied reference pattern and variable binding modes explicit | LL | let &Struct { ref a, mut b, ref c } = &Struct { a: 0, b: 0, c: 0 }; | + +++ +++ -error: patterns are not allowed to reset the default binding mode in edition 2024 - --> $DIR/migration_lint.rs:117:9 +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/migration_lint.rs:117:21 | LL | let Struct { a: &a, b, ref c } = &Struct { a: &0, b: &0, c: &0 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^ ^^^ cannot override to bind by-reference when that is the implicit default + | | + | cannot implicitly match against multiple layers of reference | = warning: this changes meaning in Rust 2024 = note: for more information, see -help: desugar the match ergonomics +help: make the implied reference pattern and variable binding mode explicit | LL | let &Struct { a: &a, ref b, ref c } = &Struct { a: &0, b: &0, c: &0 }; | + +++ -error: patterns are not allowed to reset the default binding mode in edition 2024 - --> $DIR/migration_lint.rs:124:12 +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/migration_lint.rs:124:24 | LL | if let Struct { a: &Some(a), b: Some(&b), c: Some(c) } = - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^ ^ cannot implicitly match against multiple layers of reference + | | + | cannot implicitly match against multiple layers of reference | = warning: this changes meaning in Rust 2024 = note: for more information, see -help: desugar the match ergonomics +help: make the implied reference patterns and variable binding mode explicit | LL | if let &Struct { a: &Some(a), b: &Some(&b), c: &Some(ref c) } = | + + + +++ -error: patterns are not allowed to reset the default binding mode in edition 2024 - --> $DIR/migration_lint.rs:137:9 +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/migration_lint.rs:137:15 | LL | (Some(mut x), migration_lint_macros::mixed_edition_pat!(y)) => { - | -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | help: desugar the match ergonomics: `&` + | ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ default binding mode is reset within expansion + | | + | requires binding by-value, but the implicit default is by-reference + | + = note: for more information, see + = note: this error originates in the macro `migration_lint_macros::mixed_edition_pat` (in Nightly builds, run with -Z macro-backtrace for more info) +help: make the implied reference pattern explicit + | +LL | &(Some(mut x), migration_lint_macros::mixed_edition_pat!(y)) => { + | + error: aborting due to 16 previous errors diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.rs index 50b716a111138..5ba554fc6e5a4 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.rs @@ -21,17 +21,17 @@ macro_rules! test_pat_on_type { } test_pat_on_type![(&x,): &(T,)]; //~ ERROR mismatched types -test_pat_on_type![(&x,): &(&T,)]; //~ ERROR patterns are not allowed to reset the default binding mode +test_pat_on_type![(&x,): &(&T,)]; //~ ERROR this pattern relies on behavior which may change in edition 2024 test_pat_on_type![(&x,): &(&mut T,)]; //~ ERROR mismatched types test_pat_on_type![(&mut x,): &(&T,)]; //~ ERROR mismatched types -test_pat_on_type![(&mut x,): &(&mut T,)]; //~ ERROR patterns are not allowed to reset the default binding mode +test_pat_on_type![(&mut x,): &(&mut T,)]; //~ ERROR this pattern relies on behavior which may change in edition 2024 test_pat_on_type![(&x,): &&mut &(T,)]; //~ ERROR mismatched types test_pat_on_type![Foo { f: (&x,) }: Foo]; //~ ERROR mismatched types test_pat_on_type![Foo { f: (&x,) }: &mut Foo]; //~ ERROR mismatched types -test_pat_on_type![Foo { f: &(x,) }: &Foo]; //~ ERROR patterns are not allowed to reset the default binding mode -test_pat_on_type![(mut x,): &(T,)]; //~ ERROR patterns are not allowed to reset the default binding mode -test_pat_on_type![(ref x,): &(T,)]; //~ ERROR patterns are not allowed to reset the default binding mode -test_pat_on_type![(ref mut x,): &mut (T,)]; //~ ERROR patterns are not allowed to reset the default binding mode +test_pat_on_type![Foo { f: &(x,) }: &Foo]; //~ ERROR this pattern relies on behavior which may change in edition 2024 +test_pat_on_type![(mut x,): &(T,)]; //~ ERROR this pattern relies on behavior which may change in edition 2024 +test_pat_on_type![(ref x,): &(T,)]; //~ ERROR this pattern relies on behavior which may change in edition 2024 +test_pat_on_type![(ref mut x,): &mut (T,)]; //~ ERROR this pattern relies on behavior which may change in edition 2024 fn get() -> X { unimplemented!() @@ -40,6 +40,6 @@ fn get() -> X { // Make sure this works even when the underlying type is inferred. This test passes on rust stable. fn infer() -> X { match &get() { - (&x,) => x, //~ ERROR patterns are not allowed to reset the default binding mode + (&x,) => x, //~ ERROR this pattern relies on behavior which may change in edition 2024 } } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.stderr index 92058095f84a2..affdca1d44906 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.stderr @@ -99,61 +99,89 @@ LL - test_pat_on_type![Foo { f: (&x,) }: &mut Foo]; LL + test_pat_on_type![Foo { f: (x,) }: &mut Foo]; | -error: patterns are not allowed to reset the default binding mode in edition 2024 - --> $DIR/min_match_ergonomics_fail.rs:24:19 +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/min_match_ergonomics_fail.rs:24:20 | LL | test_pat_on_type![(&x,): &(&T,)]; - | -^^^^ - | | - | help: desugar the match ergonomics: `&` + | ^ cannot implicitly match against multiple layers of reference + | + = note: for more information, see +help: make the implied reference pattern explicit + | +LL | test_pat_on_type![&(&x,): &(&T,)]; + | + -error: patterns are not allowed to reset the default binding mode in edition 2024 - --> $DIR/min_match_ergonomics_fail.rs:27:19 +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/min_match_ergonomics_fail.rs:27:20 | LL | test_pat_on_type![(&mut x,): &(&mut T,)]; - | -^^^^^^^^ - | | - | help: desugar the match ergonomics: `&` + | ^^^^ cannot implicitly match against multiple layers of reference + | + = note: for more information, see +help: make the implied reference pattern explicit + | +LL | test_pat_on_type![&(&mut x,): &(&mut T,)]; + | + -error: patterns are not allowed to reset the default binding mode in edition 2024 - --> $DIR/min_match_ergonomics_fail.rs:31:19 +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/min_match_ergonomics_fail.rs:31:28 | LL | test_pat_on_type![Foo { f: &(x,) }: &Foo]; - | -^^^^^^^^^^^^^^^ - | | - | help: desugar the match ergonomics: `&` + | ^ cannot implicitly match against multiple layers of reference + | + = note: for more information, see +help: make the implied reference pattern explicit + | +LL | test_pat_on_type![&Foo { f: &(x,) }: &Foo]; + | + -error: patterns are not allowed to reset the default binding mode in edition 2024 - --> $DIR/min_match_ergonomics_fail.rs:32:19 +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/min_match_ergonomics_fail.rs:32:20 | LL | test_pat_on_type![(mut x,): &(T,)]; - | -^^^^^^^ - | | - | help: desugar the match ergonomics: `&` + | ^^^ requires binding by-value, but the implicit default is by-reference + | + = note: for more information, see +help: make the implied reference pattern explicit + | +LL | test_pat_on_type![&(mut x,): &(T,)]; + | + -error: patterns are not allowed to reset the default binding mode in edition 2024 - --> $DIR/min_match_ergonomics_fail.rs:33:19 +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/min_match_ergonomics_fail.rs:33:20 | LL | test_pat_on_type![(ref x,): &(T,)]; - | -^^^^^^^ - | | - | help: desugar the match ergonomics: `&` + | ^^^ cannot override to bind by-reference when that is the implicit default + | + = note: for more information, see +help: make the implied reference pattern explicit + | +LL | test_pat_on_type![&(ref x,): &(T,)]; + | + -error: patterns are not allowed to reset the default binding mode in edition 2024 - --> $DIR/min_match_ergonomics_fail.rs:34:19 +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/min_match_ergonomics_fail.rs:34:20 | LL | test_pat_on_type![(ref mut x,): &mut (T,)]; - | -^^^^^^^^^^^ - | | - | help: desugar the match ergonomics: `&mut` + | ^^^^^^^ cannot override to bind by-reference when that is the implicit default + | + = note: for more information, see +help: make the implied reference pattern explicit + | +LL | test_pat_on_type![&mut (ref mut x,): &mut (T,)]; + | ++++ -error: patterns are not allowed to reset the default binding mode in edition 2024 - --> $DIR/min_match_ergonomics_fail.rs:43:9 +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/min_match_ergonomics_fail.rs:43:10 | LL | (&x,) => x, - | -^^^^ - | | - | help: desugar the match ergonomics: `&` + | ^ cannot implicitly match against multiple layers of reference + | + = note: for more information, see +help: make the implied reference pattern explicit + | +LL | &(&x,) => x, + | + error: aborting due to 13 previous errors diff --git a/tests/ui/traits/const-traits/const-cond-for-rpitit.rs b/tests/ui/traits/const-traits/const-cond-for-rpitit.rs new file mode 100644 index 0000000000000..50bf93f9a0328 --- /dev/null +++ b/tests/ui/traits/const-traits/const-cond-for-rpitit.rs @@ -0,0 +1,22 @@ +//@ compile-flags: -Znext-solver +//@ check-pass + +#![feature(const_trait_impl)] +#![allow(refining_impl_trait)] + +#[const_trait] +pub trait Foo { + fn method(self) -> impl ~const Bar; +} + +#[const_trait] +pub trait Bar {} + +struct A(T); +impl const Foo for A where A: ~const Bar { + fn method(self) -> impl ~const Bar { + self + } +} + +fn main() {} diff --git a/tests/ui/traits/const-traits/const-impl-trait.rs b/tests/ui/traits/const-traits/const-impl-trait.rs index 61b8c9a5bff83..d7fe43ef37ced 100644 --- a/tests/ui/traits/const-traits/const-impl-trait.rs +++ b/tests/ui/traits/const-traits/const-impl-trait.rs @@ -1,15 +1,10 @@ //@ compile-flags: -Znext-solver //@ known-bug: #110395 -//@ failure-status: 101 -//@ dont-check-compiler-stderr -// Broken until we have `&T: const Deref` impl in stdlib + +// Broken until we have `const PartialEq` impl in stdlib #![allow(incomplete_features)] -#![feature( - const_trait_impl, - effects, - const_cmp, -)] +#![feature(const_trait_impl, const_cmp, const_destruct)] use std::marker::Destruct; @@ -17,9 +12,9 @@ const fn cmp(a: &impl ~const PartialEq) -> bool { a == a } -const fn wrap(x: impl ~const PartialEq + ~const Destruct) - -> impl ~const PartialEq + ~const Destruct -{ +const fn wrap( + x: impl ~const PartialEq + ~const Destruct, +) -> impl ~const PartialEq + ~const Destruct { x } @@ -48,11 +43,15 @@ trait T {} struct S; impl const T for S {} -const fn rpit() -> impl ~const T { S } +const fn rpit() -> impl ~const T { + S +} const fn apit(_: impl ~const T + ~const Destruct) {} -const fn rpit_assoc_bound() -> impl IntoIterator { Some(S) } +const fn rpit_assoc_bound() -> impl IntoIterator { + Some(S) +} const fn apit_assoc_bound(_: impl IntoIterator + ~const Destruct) {} diff --git a/tests/ui/traits/const-traits/const-impl-trait.stderr b/tests/ui/traits/const-traits/const-impl-trait.stderr new file mode 100644 index 0000000000000..4e3200594485e --- /dev/null +++ b/tests/ui/traits/const-traits/const-impl-trait.stderr @@ -0,0 +1,187 @@ +error[E0635]: unknown feature `const_cmp` + --> $DIR/const-impl-trait.rs:7:30 + | +LL | #![feature(const_trait_impl, const_cmp, const_destruct)] + | ^^^^^^^^^ + +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/const-impl-trait.rs:11:23 + | +LL | const fn cmp(a: &impl ~const PartialEq) -> bool { + | ^^^^^^ can't be applied to `PartialEq` + | +note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]` + --> $SRC_DIR/core/src/cmp.rs:LL:COL + +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/const-impl-trait.rs:11:23 + | +LL | const fn cmp(a: &impl ~const PartialEq) -> bool { + | ^^^^^^ can't be applied to `PartialEq` + | +note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]` + --> $SRC_DIR/core/src/cmp.rs:LL:COL + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/const-impl-trait.rs:16:13 + | +LL | x: impl ~const PartialEq + ~const Destruct, + | ^^^^^^ can't be applied to `PartialEq` + | +note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]` + --> $SRC_DIR/core/src/cmp.rs:LL:COL + +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/const-impl-trait.rs:17:11 + | +LL | ) -> impl ~const PartialEq + ~const Destruct { + | ^^^^^^ can't be applied to `PartialEq` + | +note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]` + --> $SRC_DIR/core/src/cmp.rs:LL:COL + +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/const-impl-trait.rs:17:11 + | +LL | ) -> impl ~const PartialEq + ~const Destruct { + | ^^^^^^ can't be applied to `PartialEq` + | +note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]` + --> $SRC_DIR/core/src/cmp.rs:LL:COL + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/const-impl-trait.rs:17:11 + | +LL | ) -> impl ~const PartialEq + ~const Destruct { + | ^^^^^^ can't be applied to `PartialEq` + | +note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]` + --> $SRC_DIR/core/src/cmp.rs:LL:COL + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/const-impl-trait.rs:16:13 + | +LL | x: impl ~const PartialEq + ~const Destruct, + | ^^^^^^ can't be applied to `PartialEq` + | +note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]` + --> $SRC_DIR/core/src/cmp.rs:LL:COL + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/const-impl-trait.rs:23:22 + | +LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy; + | ^^^^^^ can't be applied to `PartialEq` + | +note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]` + --> $SRC_DIR/core/src/cmp.rs:LL:COL + +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/const-impl-trait.rs:27:22 + | +LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy { + | ^^^^^^ can't be applied to `PartialEq` + | +note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]` + --> $SRC_DIR/core/src/cmp.rs:LL:COL + +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/const-impl-trait.rs:27:22 + | +LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy { + | ^^^^^^ can't be applied to `PartialEq` + | +note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]` + --> $SRC_DIR/core/src/cmp.rs:LL:COL + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/const-impl-trait.rs:23:22 + | +LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy; + | ^^^^^^ can't be applied to `PartialEq` + | +note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]` + --> $SRC_DIR/core/src/cmp.rs:LL:COL + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/const-impl-trait.rs:23:22 + | +LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy; + | ^^^^^^ can't be applied to `PartialEq` + | +note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]` + --> $SRC_DIR/core/src/cmp.rs:LL:COL + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/const-impl-trait.rs:27:22 + | +LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy { + | ^^^^^^ can't be applied to `PartialEq` + | +note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]` + --> $SRC_DIR/core/src/cmp.rs:LL:COL + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/const-impl-trait.rs:23:22 + | +LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy; + | ^^^^^^ can't be applied to `PartialEq` + | +note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]` + --> $SRC_DIR/core/src/cmp.rs:LL:COL + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/const-impl-trait.rs:23:22 + | +LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy; + | ^^^^^^ can't be applied to `PartialEq` + | +note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]` + --> $SRC_DIR/core/src/cmp.rs:LL:COL + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0015]: cannot call non-const operator in constants + --> $DIR/const-impl-trait.rs:35:13 + | +LL | assert!(wrap(123) == wrap(123)); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: calls in constants are limited to constant functions, tuple structs and tuple variants + +error[E0015]: cannot call non-const operator in constants + --> $DIR/const-impl-trait.rs:36:13 + | +LL | assert!(wrap(123) != wrap(456)); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: calls in constants are limited to constant functions, tuple structs and tuple variants + +error[E0015]: cannot call non-const operator in constants + --> $DIR/const-impl-trait.rs:38:13 + | +LL | assert!(x == x); + | ^^^^^^ + | + = note: calls in constants are limited to constant functions, tuple structs and tuple variants + +error[E0015]: cannot call non-const operator in constant functions + --> $DIR/const-impl-trait.rs:12:5 + | +LL | a == a + | ^^^^^^ + | + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants + +error: aborting due to 20 previous errors + +Some errors have detailed explanations: E0015, E0635. +For more information about an error, try `rustc --explain E0015`.