diff options
Diffstat (limited to 'compiler')
577 files changed, 9775 insertions, 7982 deletions
diff --git a/compiler/rustc_abi/src/callconv.rs b/compiler/rustc_abi/src/callconv.rs index ee63e46e88c..400395f99ff 100644 --- a/compiler/rustc_abi/src/callconv.rs +++ b/compiler/rustc_abi/src/callconv.rs @@ -206,7 +206,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { let (mut result, mut total) = from_fields_at(*self, Size::ZERO)?; match &self.variants { - abi::Variants::Single { .. } => {} + abi::Variants::Single { .. } | abi::Variants::Empty => {} abi::Variants::Multiple { variants, .. } => { // Treat enum variants like union members. // HACK(eddyb) pretend the `enum` field (discriminant) diff --git a/compiler/rustc_abi/src/extern_abi/mod.rs b/compiler/rustc_abi/src/extern_abi/mod.rs index f7e41280131..390f2dbc10f 100644 --- a/compiler/rustc_abi/src/extern_abi/mod.rs +++ b/compiler/rustc_abi/src/extern_abi/mod.rs @@ -1,8 +1,7 @@ use std::fmt; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; -use rustc_span::symbol::sym; -use rustc_span::{Span, Symbol}; +use rustc_span::{Span, Symbol, sym}; #[cfg(test)] mod tests; diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index e6d66f608da..226a46f605c 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -213,8 +213,9 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { &self, ) -> LayoutData<FieldIdx, VariantIdx> { let dl = self.cx.data_layout(); + // This is also used for uninhabited enums, so we use `Variants::Empty`. LayoutData { - variants: Variants::Single { index: VariantIdx::new(0) }, + variants: Variants::Empty, fields: FieldsShape::Primitive, backend_repr: BackendRepr::Uninhabited, largest_niche: None, @@ -1004,8 +1005,8 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { Variants::Multiple { tag, tag_encoding, tag_field, .. } => { Variants::Multiple { tag, tag_encoding, tag_field, variants: best_layout.variants } } - Variants::Single { .. } => { - panic!("encountered a single-variant enum during multi-variant layout") + Variants::Single { .. } | Variants::Empty => { + panic!("encountered a single-variant or empty enum during multi-variant layout") } }; Ok(best_layout.layout) diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 15a27c0b6ee..ca15f7d9920 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1504,10 +1504,12 @@ impl BackendRepr { #[derive(PartialEq, Eq, Hash, Clone, Debug)] #[cfg_attr(feature = "nightly", derive(HashStable_Generic))] pub enum Variants<FieldIdx: Idx, VariantIdx: Idx> { + /// A type with no valid variants. Must be uninhabited. + Empty, + /// Single enum variants, structs/tuples, unions, and all non-ADTs. Single { - /// Always 0 for non-enums/generators. - /// For enums without a variant, this is an invalid index! + /// Always `0` for types that cannot have multiple variants. index: VariantIdx, }, diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index f0099fa8adc..cec868e5c8e 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -19,7 +19,7 @@ //! - [`UnOp`], [`BinOp`], and [`BinOpKind`]: Unary and binary operators. use std::borrow::Cow; -use std::{cmp, fmt, mem}; +use std::{cmp, fmt}; pub use GenericArgs::*; pub use UnsafeSource::*; @@ -31,8 +31,7 @@ use rustc_data_structures::sync::Lrc; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; pub use rustc_span::AttrId; use rustc_span::source_map::{Spanned, respan}; -use rustc_span::symbol::{Ident, Symbol, kw, sym}; -use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; +use rustc_span::{DUMMY_SP, ErrorGuaranteed, Ident, Span, Symbol, kw, sym}; use thin_vec::{ThinVec, thin_vec}; pub use crate::format::*; @@ -859,6 +858,8 @@ pub enum PatKind { pub enum PatFieldsRest { /// `module::StructName { field, ..}` Rest, + /// `module::StructName { field, syntax error }` + Recovered(ErrorGuaranteed), /// `module::StructName { field }` None, } @@ -1321,11 +1322,15 @@ impl Expr { } pub fn precedence(&self) -> ExprPrecedence { - match self.kind { - ExprKind::Closure(..) => ExprPrecedence::Closure, + match &self.kind { + ExprKind::Closure(closure) => { + match closure.fn_decl.output { + FnRetTy::Default(_) => ExprPrecedence::Jump, + FnRetTy::Ty(_) => ExprPrecedence::Unambiguous, + } + } ExprKind::Break(..) - | ExprKind::Continue(..) | ExprKind::Ret(..) | ExprKind::Yield(..) | ExprKind::Yeet(..) @@ -1359,6 +1364,7 @@ impl Expr { | ExprKind::Block(..) | ExprKind::Call(..) | ExprKind::ConstBlock(_) + | ExprKind::Continue(..) | ExprKind::Field(..) | ExprKind::ForLoop { .. } | ExprKind::FormatArgs(..) @@ -1758,53 +1764,16 @@ pub enum AttrArgs { Eq { /// Span of the `=` token. eq_span: Span, - - value: AttrArgsEq, + expr: P<Expr>, }, } -// The RHS of an `AttrArgs::Eq` starts out as an expression. Once macro -// expansion is completed, all cases end up either as a meta item literal, -// which is the form used after lowering to HIR, or as an error. -#[derive(Clone, Encodable, Decodable, Debug)] -pub enum AttrArgsEq { - Ast(P<Expr>), - Hir(MetaItemLit), -} - -impl AttrArgsEq { - pub fn span(&self) -> Span { - match self { - AttrArgsEq::Ast(p) => p.span, - AttrArgsEq::Hir(lit) => lit.span, - } - } - - pub fn unwrap_ast(&self) -> &Expr { - match self { - AttrArgsEq::Ast(p) => p, - AttrArgsEq::Hir(lit) => { - unreachable!("in literal form when getting inner tokens: {lit:?}") - } - } - } - - pub fn unwrap_ast_mut(&mut self) -> &mut P<Expr> { - match self { - AttrArgsEq::Ast(p) => p, - AttrArgsEq::Hir(lit) => { - unreachable!("in literal form when getting inner tokens: {lit:?}") - } - } - } -} - impl AttrArgs { pub fn span(&self) -> Option<Span> { match self { AttrArgs::Empty => None, AttrArgs::Delimited(args) => Some(args.dspan.entire()), - AttrArgs::Eq { eq_span, value } => Some(eq_span.to(value.span())), + AttrArgs::Eq { eq_span, expr } => Some(eq_span.to(expr.span)), } } @@ -1814,27 +1783,7 @@ impl AttrArgs { match self { AttrArgs::Empty => TokenStream::default(), AttrArgs::Delimited(args) => args.tokens.clone(), - AttrArgs::Eq { value, .. } => TokenStream::from_ast(value.unwrap_ast()), - } - } -} - -impl<CTX> HashStable<CTX> for AttrArgs -where - CTX: crate::HashStableContext, -{ - fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { - mem::discriminant(self).hash_stable(ctx, hasher); - match self { - AttrArgs::Empty => {} - AttrArgs::Delimited(args) => args.hash_stable(ctx, hasher), - AttrArgs::Eq { value: AttrArgsEq::Ast(expr), .. } => { - unreachable!("hash_stable {:?}", expr); - } - AttrArgs::Eq { eq_span, value: AttrArgsEq::Hir(lit) } => { - eq_span.hash_stable(ctx, hasher); - lit.hash_stable(ctx, hasher); - } + AttrArgs::Eq { expr, .. } => TokenStream::from_ast(expr), } } } @@ -3051,7 +3000,7 @@ impl NormalAttr { } } -#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct AttrItem { pub unsafety: Safety, pub path: Path, diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 0d79cadef34..97385b2eaab 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -1,22 +1,23 @@ //! Functions dealing with attributes and meta items. -use std::iter; +use std::fmt::Debug; use std::sync::atomic::{AtomicU32, Ordering}; use rustc_index::bit_set::GrowableBitSet; -use rustc_span::Span; -use rustc_span::symbol::{Ident, Symbol, sym}; +use rustc_span::{Ident, Span, Symbol, sym}; use smallvec::{SmallVec, smallvec}; use thin_vec::{ThinVec, thin_vec}; use crate::ast::{ - AttrArgs, AttrArgsEq, AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute, DUMMY_NODE_ID, - DelimArgs, Expr, ExprKind, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, - NormalAttr, Path, PathSegment, Safety, + AttrArgs, AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute, DUMMY_NODE_ID, DelimArgs, + Expr, ExprKind, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NormalAttr, Path, + PathSegment, Safety, }; 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; @@ -66,11 +67,27 @@ impl Attribute { AttrKind::DocComment(..) => panic!("unexpected doc comment"), } } +} + +impl AttributeExt for Attribute { + fn id(&self) -> AttrId { + self.id + } + + fn value_span(&self) -> Option<Span> { + match &self.kind { + AttrKind::Normal(normal) => match &normal.item.args { + AttrArgs::Eq { expr, .. } => Some(expr.span), + _ => None, + }, + AttrKind::DocComment(..) => None, + } + } /// Returns `true` if it is a sugared doc comment (`///` or `//!` for example). /// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not /// a doc comment) will return `false`. - pub fn is_doc_comment(&self) -> bool { + fn is_doc_comment(&self) -> bool { match self.kind { AttrKind::Normal(..) => false, AttrKind::DocComment(..) => true, @@ -78,7 +95,7 @@ impl Attribute { } /// For a single-segment attribute, returns its name; otherwise, returns `None`. - pub fn ident(&self) -> Option<Ident> { + fn ident(&self) -> Option<Ident> { match &self.kind { AttrKind::Normal(normal) => { if let [ident] = &*normal.item.path.segments { @@ -91,28 +108,14 @@ impl Attribute { } } - pub fn name_or_empty(&self) -> Symbol { - self.ident().unwrap_or_else(Ident::empty).name - } - - pub fn path(&self) -> SmallVec<[Symbol; 1]> { - match &self.kind { - AttrKind::Normal(normal) => { - normal.item.path.segments.iter().map(|s| s.ident.name).collect() - } - AttrKind::DocComment(..) => smallvec![sym::doc], - } - } - - #[inline] - pub fn has_name(&self, name: Symbol) -> bool { + fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>> { match &self.kind { - AttrKind::Normal(normal) => normal.item.path == name, - AttrKind::DocComment(..) => false, + AttrKind::Normal(p) => Some(p.item.path.segments.iter().map(|i| i.ident).collect()), + AttrKind::DocComment(_, _) => None, } } - pub fn path_matches(&self, name: &[Symbol]) -> bool { + fn path_matches(&self, name: &[Symbol]) -> bool { match &self.kind { AttrKind::Normal(normal) => { normal.item.path.segments.len() == name.len() @@ -128,7 +131,11 @@ impl Attribute { } } - pub fn is_word(&self) -> bool { + fn span(&self) -> Span { + self.span + } + + fn is_word(&self) -> bool { if let AttrKind::Normal(normal) = &self.kind { matches!(normal.item.args, AttrArgs::Empty) } else { @@ -143,7 +150,7 @@ impl Attribute { /// #[attr = ""] // Returns `None`. /// #[attr] // Returns `None`. /// ``` - pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> { + fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> { match &self.kind { AttrKind::Normal(normal) => normal.item.meta_item_list(), AttrKind::DocComment(..) => None, @@ -165,7 +172,7 @@ impl Attribute { /// ```text /// #[attr("value")] /// ``` - pub fn value_str(&self) -> Option<Symbol> { + fn value_str(&self) -> Option<Symbol> { match &self.kind { AttrKind::Normal(normal) => normal.item.value_str(), AttrKind::DocComment(..) => None, @@ -177,7 +184,7 @@ impl Attribute { /// * `/** doc */` returns `Some(("doc", CommentKind::Block))`. /// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`. /// * `#[doc(...)]` returns `None`. - pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> { + fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> { match &self.kind { AttrKind::DocComment(kind, data) => Some((*data, *kind)), AttrKind::Normal(normal) if normal.item.path == sym::doc => { @@ -191,7 +198,7 @@ impl Attribute { /// * `///doc` returns `Some("doc")`. /// * `#[doc = "doc"]` returns `Some("doc")`. /// * `#[doc(...)]` returns `None`. - pub fn doc_str(&self) -> Option<Symbol> { + fn doc_str(&self) -> Option<Symbol> { match &self.kind { AttrKind::DocComment(.., data) => Some(*data), AttrKind::Normal(normal) if normal.item.path == sym::doc => normal.item.value_str(), @@ -199,14 +206,14 @@ impl Attribute { } } - pub fn may_have_doc_links(&self) -> bool { - self.doc_str().is_some_and(|s| comments::may_have_doc_links(s.as_str())) + fn style(&self) -> AttrStyle { + self.style } +} - pub fn is_proc_macro_attr(&self) -> bool { - [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive] - .iter() - .any(|kind| self.has_name(*kind)) +impl Attribute { + pub fn may_have_doc_links(&self) -> bool { + self.doc_str().is_some_and(|s| comments::may_have_doc_links(s.as_str())) } /// Extracts the MetaItem from inside this Attribute. @@ -268,7 +275,12 @@ impl AttrItem { /// ``` fn value_str(&self) -> Option<Symbol> { match &self.args { - AttrArgs::Eq { value, .. } => value.value_str(), + AttrArgs::Eq { expr, .. } => match expr.kind { + ExprKind::Lit(token_lit) => { + LitKind::from_token_lit(token_lit).ok().and_then(|lit| lit.str()) + } + _ => None, + }, AttrArgs::Delimited(_) | AttrArgs::Empty => None, } } @@ -287,20 +299,6 @@ impl AttrItem { } } -impl AttrArgsEq { - fn value_str(&self) -> Option<Symbol> { - match self { - AttrArgsEq::Ast(expr) => match expr.kind { - ExprKind::Lit(token_lit) => { - LitKind::from_token_lit(token_lit).ok().and_then(|lit| lit.str()) - } - _ => None, - }, - AttrArgsEq::Hir(lit) => lit.kind.str(), - } - } -} - impl MetaItem { /// For a single-segment meta item, returns its name; otherwise, returns `None`. pub fn ident(&self) -> Option<Ident> { @@ -368,12 +366,9 @@ impl MetaItem { } } - fn from_tokens<'a, I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem> - where - I: Iterator<Item = &'a TokenTree>, - { + fn from_tokens(iter: &mut TokenStreamIter<'_>) -> Option<MetaItem> { // 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 }, @@ -381,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)); @@ -393,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; } @@ -423,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()), @@ -439,13 +434,14 @@ impl MetaItem { } impl MetaItemKind { - fn list_from_tokens(tokens: TokenStream) -> Option<ThinVec<MetaItemInner>> { - let mut tokens = tokens.trees().peekable(); + // public because it can be called in the hir + pub fn list_from_tokens(tokens: TokenStream) -> Option<ThinVec<MetaItemInner>> { + 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, } @@ -453,12 +449,10 @@ impl MetaItemKind { Some(result) } - fn name_value_from_tokens<'a>( - tokens: &mut impl Iterator<Item = &'a TokenTree>, - ) -> Option<MetaItemKind> { - match tokens.next() { + fn name_value_from_tokens(iter: &mut TokenStreamIter<'_>) -> Option<MetaItemKind> { + 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) @@ -467,19 +461,17 @@ impl MetaItemKind { } } - fn from_tokens<'a>( - tokens: &mut iter::Peekable<impl Iterator<Item = &'a TokenTree>>, - ) -> Option<MetaItemKind> { - match tokens.peek() { + fn from_tokens(iter: &mut TokenStreamIter<'_>) -> Option<MetaItemKind> { + 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), } @@ -492,7 +484,7 @@ impl MetaItemKind { MetaItemKind::list_from_tokens(tokens.clone()).map(MetaItemKind::List) } AttrArgs::Delimited(..) => None, - AttrArgs::Eq { value: AttrArgsEq::Ast(expr), .. } => match expr.kind { + AttrArgs::Eq { expr, .. } => match expr.kind { ExprKind::Lit(token_lit) => { // Turn failures to `None`, we'll get parse errors elsewhere. MetaItemLit::from_token_lit(token_lit, expr.span) @@ -501,9 +493,6 @@ impl MetaItemKind { } _ => None, }, - AttrArgs::Eq { value: AttrArgsEq::Hir(lit), .. } => { - Some(MetaItemKind::NameValue(lit.clone())) - } } } } @@ -598,22 +587,19 @@ impl MetaItemInner { self.meta_item().is_some() } - fn from_tokens<'a, I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItemInner> - where - I: Iterator<Item = &'a TokenTree>, - { - match tokens.peek() { + fn from_tokens(iter: &mut TokenStreamIter<'_>) -> Option<MetaItemInner> { + 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) } } @@ -704,26 +690,175 @@ pub fn mk_attr_name_value_str( tokens: None, }); let path = Path::from_ident(Ident::new(name, span)); - let args = AttrArgs::Eq { eq_span: span, value: AttrArgsEq::Ast(expr) }; + let args = AttrArgs::Eq { eq_span: span, expr }; mk_attr(g, style, unsafety, path, args, span) } -pub fn filter_by_name(attrs: &[Attribute], name: Symbol) -> impl Iterator<Item = &Attribute> { +pub fn filter_by_name<A: AttributeExt>(attrs: &[A], name: Symbol) -> impl Iterator<Item = &A> { attrs.iter().filter(move |attr| attr.has_name(name)) } -pub fn find_by_name(attrs: &[Attribute], name: Symbol) -> Option<&Attribute> { +pub fn find_by_name<A: AttributeExt>(attrs: &[A], name: Symbol) -> Option<&A> { filter_by_name(attrs, name).next() } -pub fn first_attr_value_str_by_name(attrs: &[Attribute], name: Symbol) -> Option<Symbol> { +pub fn first_attr_value_str_by_name(attrs: &[impl AttributeExt], name: Symbol) -> Option<Symbol> { find_by_name(attrs, name).and_then(|attr| attr.value_str()) } -pub fn contains_name(attrs: &[Attribute], name: Symbol) -> bool { +pub fn contains_name(attrs: &[impl AttributeExt], name: Symbol) -> bool { find_by_name(attrs, name).is_some() } pub fn list_contains_name(items: &[MetaItemInner], name: Symbol) -> bool { items.iter().any(|item| item.has_name(name)) } + +impl MetaItemLit { + pub fn value_str(&self) -> Option<Symbol> { + LitKind::from_token_lit(self.as_token_lit()).ok().and_then(|lit| lit.str()) + } +} + +pub trait AttributeExt: Debug { + fn id(&self) -> AttrId; + + fn name_or_empty(&self) -> Symbol { + self.ident().unwrap_or_else(Ident::empty).name + } + + /// Get the meta item list, `#[attr(meta item list)]` + fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>>; + + /// Gets the value literal, as string, when using `#[attr = value]` + fn value_str(&self) -> Option<Symbol>; + + /// Gets the span of the value literal, as string, when using `#[attr = value]` + fn value_span(&self) -> Option<Span>; + + /// For a single-segment attribute, returns its name; otherwise, returns `None`. + fn ident(&self) -> Option<Ident>; + + /// Checks whether the path of this attribute matches the name. + /// + /// Matches one segment of the path to each element in `name` + fn path_matches(&self, name: &[Symbol]) -> bool; + + /// Returns `true` if it is a sugared doc comment (`///` or `//!` for example). + /// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not + /// a doc comment) will return `false`. + fn is_doc_comment(&self) -> bool; + + #[inline] + fn has_name(&self, name: Symbol) -> bool { + self.ident().map(|x| x.name == name).unwrap_or(false) + } + + /// get the span of the entire attribute + fn span(&self) -> Span; + + fn is_word(&self) -> bool; + + fn path(&self) -> SmallVec<[Symbol; 1]> { + self.ident_path() + .map(|i| i.into_iter().map(|i| i.name).collect()) + .unwrap_or(smallvec![sym::doc]) + } + + /// Returns None for doc comments + fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>>; + + /// Returns the documentation if this is a doc comment or a sugared doc comment. + /// * `///doc` returns `Some("doc")`. + /// * `#[doc = "doc"]` returns `Some("doc")`. + /// * `#[doc(...)]` returns `None`. + fn doc_str(&self) -> Option<Symbol>; + + fn is_proc_macro_attr(&self) -> bool { + [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive] + .iter() + .any(|kind| self.has_name(*kind)) + } + + /// Returns the documentation and its kind if this is a doc comment or a sugared doc comment. + /// * `///doc` returns `Some(("doc", CommentKind::Line))`. + /// * `/** doc */` returns `Some(("doc", CommentKind::Block))`. + /// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`. + /// * `#[doc(...)]` returns `None`. + fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)>; + + fn style(&self) -> AttrStyle; +} + +// FIXME(fn_delegation): use function delegation instead of manually forwarding + +impl Attribute { + pub fn id(&self) -> AttrId { + AttributeExt::id(self) + } + + pub fn name_or_empty(&self) -> Symbol { + AttributeExt::name_or_empty(self) + } + + pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> { + AttributeExt::meta_item_list(self) + } + + pub fn value_str(&self) -> Option<Symbol> { + AttributeExt::value_str(self) + } + + pub fn value_span(&self) -> Option<Span> { + AttributeExt::value_span(self) + } + + pub fn ident(&self) -> Option<Ident> { + AttributeExt::ident(self) + } + + pub fn path_matches(&self, name: &[Symbol]) -> bool { + AttributeExt::path_matches(self, name) + } + + pub fn is_doc_comment(&self) -> bool { + AttributeExt::is_doc_comment(self) + } + + #[inline] + pub fn has_name(&self, name: Symbol) -> bool { + AttributeExt::has_name(self, name) + } + + pub fn span(&self) -> Span { + AttributeExt::span(self) + } + + pub fn is_word(&self) -> bool { + AttributeExt::is_word(self) + } + + pub fn path(&self) -> SmallVec<[Symbol; 1]> { + AttributeExt::path(self) + } + + pub fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>> { + AttributeExt::ident_path(self) + } + + pub fn doc_str(&self) -> Option<Symbol> { + AttributeExt::doc_str(self) + } + + pub fn is_proc_macro_attr(&self) -> bool { + AttributeExt::is_proc_macro_attr(self) + } + + pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> { + AttributeExt::doc_str_and_comment_kind(self) + } + + pub fn style(&self) -> AttrStyle { + AttributeExt::style(self) + } +} diff --git a/compiler/rustc_ast/src/entry.rs b/compiler/rustc_ast/src/entry.rs index 45c4caca6e9..ab1413d6080 100644 --- a/compiler/rustc_ast/src/entry.rs +++ b/compiler/rustc_ast/src/entry.rs @@ -1,7 +1,6 @@ -use rustc_span::Symbol; -use rustc_span::symbol::sym; +use rustc_span::{Symbol, sym}; -use crate::{Attribute, attr}; +use crate::attr::{self, AttributeExt}; #[derive(Debug)] pub enum EntryPointType { @@ -37,7 +36,7 @@ pub enum EntryPointType { } pub fn entry_point_type( - attrs: &[Attribute], + attrs: &[impl AttributeExt], at_root: bool, name: Option<Symbol>, ) -> EntryPointType { diff --git a/compiler/rustc_ast/src/expand/allocator.rs b/compiler/rustc_ast/src/expand/allocator.rs index bee7dfb61da..dd8d5ae624a 100644 --- a/compiler/rustc_ast/src/expand/allocator.rs +++ b/compiler/rustc_ast/src/expand/allocator.rs @@ -1,5 +1,5 @@ use rustc_macros::HashStable_Generic; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::{Symbol, sym}; #[derive(Clone, Debug, Copy, Eq, PartialEq, HashStable_Generic)] pub enum AllocatorKind { diff --git a/compiler/rustc_ast/src/expand/mod.rs b/compiler/rustc_ast/src/expand/mod.rs index d259677e98e..04c81629323 100644 --- a/compiler/rustc_ast/src/expand/mod.rs +++ b/compiler/rustc_ast/src/expand/mod.rs @@ -1,8 +1,8 @@ //! Definitions shared by macros / syntax extensions and e.g. `rustc_middle`. use rustc_macros::{Decodable, Encodable, HashStable_Generic}; +use rustc_span::Ident; use rustc_span::def_id::DefId; -use rustc_span::symbol::Ident; use crate::MetaItem; diff --git a/compiler/rustc_ast/src/format.rs b/compiler/rustc_ast/src/format.rs index d5900d83e4d..de628f09853 100644 --- a/compiler/rustc_ast/src/format.rs +++ b/compiler/rustc_ast/src/format.rs @@ -1,7 +1,6 @@ use rustc_data_structures::fx::FxHashMap; use rustc_macros::{Decodable, Encodable}; -use rustc_span::Span; -use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::{Ident, Span, Symbol}; use crate::Expr; use crate::ptr::P; diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index 7730d0b4b78..6372c66050e 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -44,20 +44,10 @@ pub mod token; pub mod tokenstream; pub mod visit; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; - pub use self::ast::*; pub use self::ast_traits::{AstDeref, AstNodeWrapper, HasAttrs, HasNodeId, HasTokens}; /// Requirements for a `StableHashingContext` to be used in this crate. /// This is a hack to allow using the `HashStable_Generic` derive macro /// instead of implementing everything in `rustc_middle`. -pub trait HashStableContext: rustc_span::HashStableContext { - fn hash_attr(&mut self, _: &ast::Attribute, hasher: &mut StableHasher); -} - -impl<AstCtx: crate::HashStableContext> HashStable<AstCtx> for ast::Attribute { - fn hash_stable(&self, hcx: &mut AstCtx, hasher: &mut StableHasher) { - hcx.hash_attr(self, hasher) - } -} +pub trait HashStableContext: rustc_span::HashStableContext {} diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index b44e2d9cace..995924c2a29 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -13,9 +13,8 @@ use std::panic; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::sync::Lrc; -use rustc_span::Span; use rustc_span::source_map::Spanned; -use rustc_span::symbol::Ident; +use rustc_span::{Ident, Span}; use smallvec::{Array, SmallVec, smallvec}; use thin_vec::ThinVec; @@ -451,8 +450,8 @@ fn visit_attr_args<T: MutVisitor>(vis: &mut T, args: &mut AttrArgs) { match args { AttrArgs::Empty => {} AttrArgs::Delimited(args) => visit_delim_args(vis, args), - AttrArgs::Eq { eq_span, value } => { - vis.visit_expr(value.unwrap_ast_mut()); + AttrArgs::Eq { eq_span, expr } => { + vis.visit_expr(expr); vis.visit_span(eq_span); } } diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 678f43e3511..f639e785bc4 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -11,11 +11,10 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::Lrc; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::edition::Edition; +use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, kw, sym}; #[allow(clippy::useless_attribute)] // FIXME: following use of `hidden_glob_reexports` incorrectly triggers `useless_attribute` lint. #[allow(hidden_glob_reexports)] -use rustc_span::symbol::{Ident, Symbol}; -use rustc_span::symbol::{kw, sym}; -use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; +use rustc_span::{Ident, Symbol}; use crate::ast; use crate::ptr::P; @@ -904,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 @@ -917,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 c6b6addc946..e7b393d869d 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<TokenStream> 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/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs index 498df5a7144..4459cb962e8 100644 --- a/compiler/rustc_ast/src/util/literal.rs +++ b/compiler/rustc_ast/src/util/literal.rs @@ -5,8 +5,7 @@ use std::{ascii, fmt, str}; use rustc_lexer::unescape::{ MixedUnit, Mode, byte_from_char, unescape_byte, unescape_char, unescape_mixed, unescape_unicode, }; -use rustc_span::Span; -use rustc_span::symbol::{Symbol, kw, sym}; +use rustc_span::{Span, Symbol, kw, sym}; use tracing::debug; use crate::ast::{self, LitKind, MetaItemLit, StrStyle}; diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs index e88bf27021a..8f2b7a23c01 100644 --- a/compiler/rustc_ast/src/util/parser.rs +++ b/compiler/rustc_ast/src/util/parser.rs @@ -1,4 +1,4 @@ -use rustc_span::symbol::kw; +use rustc_span::kw; use crate::ast::{self, BinOpKind}; use crate::token::{self, BinOpToken, Token}; @@ -153,9 +153,10 @@ impl AssocOp { match *self { Assign | AssignOp(_) => Fixity::Right, As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd - | BitXor | BitOr | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual - | LAnd | LOr => Fixity::Left, - DotDot | DotDotEq => Fixity::None, + | BitXor | BitOr | LAnd | LOr => Fixity::Left, + Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | DotDot | DotDotEq => { + Fixity::None + } } } @@ -231,8 +232,7 @@ impl AssocOp { #[derive(Clone, Copy, PartialEq, PartialOrd)] pub enum ExprPrecedence { - Closure, - // return, break, yield + // return, break, yield, closures Jump, // = += -= *= /= %= &= |= ^= <<= >>= Assign, diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 22db4438e31..c7cc772dabb 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -15,8 +15,7 @@ pub use rustc_ast_ir::visit::VisitorResult; pub use rustc_ast_ir::{try_visit, visit_opt, walk_list, walk_visitable_list}; -use rustc_span::Span; -use rustc_span::symbol::Ident; +use rustc_span::{Ident, Span}; use crate::ast::*; use crate::ptr::P; @@ -1287,7 +1286,7 @@ pub fn walk_attr_args<'a, V: Visitor<'a>>(visitor: &mut V, args: &'a AttrArgs) - match args { AttrArgs::Empty => {} AttrArgs::Delimited(_args) => {} - AttrArgs::Eq { value, .. } => try_visit!(visitor.visit_expr(value.unwrap_ast())), + AttrArgs::Eq { expr, .. } => try_visit!(visitor.visit_expr(expr)), } V::Result::output() } diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index 569a15b0e07..2f1f1269ece 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -7,8 +7,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_session::parse::feature_err; -use rustc_span::symbol::kw; -use rustc_span::{Span, sym}; +use rustc_span::{Span, kw, sym}; use rustc_target::asm; use super::LoweringContext; diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index 70c94f4019a..758f1dc1c35 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -46,8 +46,7 @@ use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::DefId; use rustc_middle::span_bug; use rustc_middle::ty::{Asyncness, ResolverAstLowering}; -use rustc_span::Span; -use rustc_span::symbol::Ident; +use rustc_span::{Ident, Span}; use rustc_target::spec::abi; use {rustc_ast as ast, rustc_hir as hir}; diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs index 2564d4e2772..f727691bf47 100644 --- a/compiler/rustc_ast_lowering/src/errors.rs +++ b/compiler/rustc_ast_lowering/src/errors.rs @@ -1,8 +1,7 @@ use rustc_errors::codes::*; use rustc_errors::{Diag, DiagArgFromDisplay, EmissionGuarantee, SubdiagMessageOp, Subdiagnostic}; use rustc_macros::{Diagnostic, Subdiagnostic}; -use rustc_span::symbol::Ident; -use rustc_span::{Span, Symbol}; +use rustc_span::{Ident, Span, Symbol}; #[derive(Diagnostic)] #[diag(ast_lowering_generic_type_with_parentheses, code = E0214)] diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 32905806343..d16a3ce390d 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -13,8 +13,7 @@ use rustc_middle::span_bug; use rustc_middle::ty::TyCtxt; use rustc_session::errors::report_lit_error; use rustc_span::source_map::{Spanned, respan}; -use rustc_span::symbol::{Ident, Symbol, kw, sym}; -use rustc_span::{DUMMY_SP, DesugaringKind, Span}; +use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym}; use thin_vec::{ThinVec, thin_vec}; use visit::{Visitor, walk_expr}; diff --git a/compiler/rustc_ast_lowering/src/format.rs b/compiler/rustc_ast_lowering/src/format.rs index 653116e1fe0..22aa1e6fc20 100644 --- a/compiler/rustc_ast_lowering/src/format.rs +++ b/compiler/rustc_ast_lowering/src/format.rs @@ -6,8 +6,7 @@ use rustc_ast::*; use rustc_data_structures::fx::FxIndexMap; use rustc_hir as hir; use rustc_session::config::FmtDebug; -use rustc_span::symbol::{Ident, kw}; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Ident, Span, Symbol, kw, sym}; use super::LoweringContext; diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index d63131eacb5..2cf6a2a909b 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -10,8 +10,7 @@ use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::span_bug; use rustc_middle::ty::{ResolverAstLowering, TyCtxt}; use rustc_span::edit_distance::find_best_match_for_name; -use rustc_span::symbol::{Ident, kw, sym}; -use rustc_span::{DesugaringKind, Span, Symbol}; +use rustc_span::{DesugaringKind, Ident, Span, Symbol, kw, sym}; use rustc_target::spec::abi; use smallvec::{SmallVec, smallvec}; use thin_vec::ThinVec; @@ -176,7 +175,7 @@ impl<'hir> LoweringContext<'_, 'hir> { id: NodeId, hir_id: hir::HirId, ident: &mut Ident, - attrs: &'hir [Attribute], + attrs: &'hir [hir::Attribute], vis_span: Span, i: &ItemKind, ) -> hir::ItemKind<'hir> { @@ -467,7 +466,7 @@ impl<'hir> LoweringContext<'_, 'hir> { id: NodeId, vis_span: Span, ident: &mut Ident, - attrs: &'hir [Attribute], + attrs: &'hir [hir::Attribute], ) -> hir::ItemKind<'hir> { let path = &tree.prefix; let segments = prefix.segments.iter().chain(path.segments.iter()).cloned().collect(); @@ -1172,9 +1171,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // we can keep the same name for the parameter. // This lets rustdoc render it correctly in documentation. hir::PatKind::Binding(_, _, ident, _) => (ident, false), - hir::PatKind::Wild => { - (Ident::with_dummy_span(rustc_span::symbol::kw::Underscore), false) - } + hir::PatKind::Wild => (Ident::with_dummy_span(rustc_span::kw::Underscore), false), _ => { // Replace the ident for bindings that aren't simple. let name = format!("__arg{index}"); @@ -1392,7 +1389,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } } - pub(super) fn lower_safety(&mut self, s: Safety, default: hir::Safety) -> hir::Safety { + pub(super) fn lower_safety(&self, s: Safety, default: hir::Safety) -> hir::Safety { match s { Safety::Unsafe(_) => hir::Safety::Unsafe, Safety::Default => default, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index e4600b0f636..8438a421226 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -41,7 +41,6 @@ // tidy-alphabetical-end use rustc_ast::node_id::NodeMap; -use rustc_ast::ptr::P; use rustc_ast::{self as ast, *}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fingerprint::Fingerprint; @@ -59,8 +58,7 @@ use rustc_macros::extension; use rustc_middle::span_bug; use rustc_middle::ty::{ResolverAstLowering, TyCtxt}; use rustc_session::parse::{add_feature_diagnostics, feature_err}; -use rustc_span::symbol::{Ident, Symbol, kw, sym}; -use rustc_span::{DUMMY_SP, DesugaringKind, Span}; +use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym}; use smallvec::{SmallVec, smallvec}; use thin_vec::ThinVec; use tracing::{debug, instrument, trace}; @@ -96,7 +94,7 @@ struct LoweringContext<'a, 'hir> { /// Bodies inside the owner being lowered. bodies: Vec<(hir::ItemLocalId, &'hir hir::Body<'hir>)>, /// Attributes inside the owner being lowered. - attrs: SortedMap<hir::ItemLocalId, &'hir [Attribute]>, + attrs: SortedMap<hir::ItemLocalId, &'hir [hir::Attribute]>, /// Collect items that were created by lowering the current owner. children: Vec<(LocalDefId, hir::MaybeOwner<'hir>)>, @@ -847,7 +845,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ret } - fn lower_attrs(&mut self, id: HirId, attrs: &[Attribute]) -> &'hir [Attribute] { + fn lower_attrs(&mut self, id: HirId, attrs: &[Attribute]) -> &'hir [hir::Attribute] { if attrs.is_empty() { &[] } else { @@ -859,25 +857,33 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } - fn lower_attr(&self, attr: &Attribute) -> Attribute { + fn lower_attr(&self, attr: &Attribute) -> hir::Attribute { // Note that we explicitly do not walk the path. Since we don't really // lower attributes (we use the AST version) there is nowhere to keep // the `HirId`s. We don't actually need HIR version of attributes anyway. // Tokens are also not needed after macro expansion and parsing. let kind = match attr.kind { - AttrKind::Normal(ref normal) => AttrKind::Normal(P(NormalAttr { - item: AttrItem { - unsafety: normal.item.unsafety, - path: normal.item.path.clone(), - args: self.lower_attr_args(&normal.item.args), - tokens: None, + AttrKind::Normal(ref normal) => hir::AttrKind::Normal(Box::new(hir::AttrItem { + unsafety: self.lower_safety(normal.item.unsafety, hir::Safety::Safe), + path: hir::AttrPath { + segments: normal + .item + .path + .segments + .iter() + .map(|i| i.ident) + .collect::<Vec<_>>() + .into_boxed_slice(), + span: normal.item.path.span, }, - tokens: None, + args: self.lower_attr_args(&normal.item.args), })), - AttrKind::DocComment(comment_kind, data) => AttrKind::DocComment(comment_kind, data), + AttrKind::DocComment(comment_kind, data) => { + hir::AttrKind::DocComment(comment_kind, data) + } }; - Attribute { kind, id: attr.id, style: attr.style, span: self.lower_span(attr.span) } + hir::Attribute { kind, id: attr.id, style: attr.style, span: self.lower_span(attr.span) } } fn alias_attrs(&mut self, id: HirId, target_id: HirId) { @@ -889,15 +895,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } - fn lower_attr_args(&self, args: &AttrArgs) -> AttrArgs { + fn lower_attr_args(&self, args: &AttrArgs) -> hir::AttrArgs { match args { - AttrArgs::Empty => AttrArgs::Empty, - AttrArgs::Delimited(args) => AttrArgs::Delimited(self.lower_delim_args(args)), + AttrArgs::Empty => hir::AttrArgs::Empty, + AttrArgs::Delimited(args) => hir::AttrArgs::Delimited(self.lower_delim_args(args)), // This is an inert key-value attribute - it will never be visible to macros // after it gets lowered to HIR. Therefore, we can extract literals to handle // nonterminals in `#[doc]` (e.g. `#[doc = $e]`). - &AttrArgs::Eq { eq_span, ref value } => { - let expr = value.unwrap_ast(); + &AttrArgs::Eq { eq_span, ref expr } => { // In valid code the value always ends up as a single literal. Otherwise, a dummy // literal suffices because the error is handled elsewhere. let lit = if let ExprKind::Lit(token_lit) = expr.kind @@ -913,7 +918,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { span: DUMMY_SP, } }; - AttrArgs::Eq { eq_span, value: AttrArgsEq::Hir(lit) } + hir::AttrArgs::Eq { eq_span, expr: lit } } } } @@ -2201,7 +2206,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn stmt_let_pat( &mut self, - attrs: Option<&'hir [Attribute]>, + attrs: Option<&'hir [hir::Attribute]>, span: Span, init: Option<&'hir hir::Expr<'hir>>, pat: &'hir hir::Pat<'hir>, diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index c4bae084a3f..a4ab2561b72 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -3,9 +3,8 @@ use rustc_ast::*; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir as hir; use rustc_hir::def::Res; -use rustc_span::Span; use rustc_span::source_map::Spanned; -use rustc_span::symbol::Ident; +use rustc_span::{Ident, Span}; use super::errors::{ ArbitraryExpressionInPattern, ExtraDoubleDot, MisplacedDoubleDot, SubTupleBinding, @@ -92,7 +91,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { span: self.lower_span(f.span), } })); - break hir::PatKind::Struct(qpath, fs, *etc == ast::PatFieldsRest::Rest); + break hir::PatKind::Struct( + qpath, + fs, + matches!( + etc, + ast::PatFieldsRest::Rest | ast::PatFieldsRest::Recovered(_) + ), + ); } PatKind::Tuple(pats) => { let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple"); diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 133793e26ea..043144a5464 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -6,8 +6,7 @@ use rustc_hir::def::{DefKind, PartialRes, Res}; use rustc_hir::def_id::DefId; use rustc_middle::span_bug; use rustc_session::parse::add_feature_diagnostics; -use rustc_span::symbol::{Ident, kw, sym}; -use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Span, Symbol}; +use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym}; use smallvec::{SmallVec, smallvec}; use tracing::{debug, instrument}; diff --git a/compiler/rustc_ast_passes/Cargo.toml b/compiler/rustc_ast_passes/Cargo.toml index eace5ce8208..8046765647e 100644 --- a/compiler/rustc_ast_passes/Cargo.toml +++ b/compiler/rustc_ast_passes/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" itertools = "0.12" rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 290c2e52970..d1cf9c53d66 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -34,8 +34,7 @@ use rustc_session::lint::builtin::{ PATTERNS_IN_FNS_WITHOUT_BODY, }; use rustc_session::lint::{BuiltinLintDiag, LintBuffer}; -use rustc_span::Span; -use rustc_span::symbol::{Ident, kw, sym}; +use rustc_span::{Ident, Span, kw, sym}; use rustc_target::spec::abi; use thin_vec::thin_vec; @@ -342,7 +341,7 @@ impl<'a> AstValidator<'a> { sym::forbid, sym::warn, ]; - !arr.contains(&attr.name_or_empty()) && rustc_attr::is_builtin_attr(attr) + !arr.contains(&attr.name_or_empty()) && rustc_attr_parsing::is_builtin_attr(*attr) }) .for_each(|attr| { if attr.is_doc_comment() { diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 9b600e3ee92..0eb2043eaa3 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -4,8 +4,7 @@ use rustc_ast::ParamKindOrd; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, EmissionGuarantee, SubdiagMessageOp, Subdiagnostic}; use rustc_macros::{Diagnostic, Subdiagnostic}; -use rustc_span::symbol::Ident; -use rustc_span::{Span, Symbol}; +use rustc_span::{Ident, Span, Symbol}; use crate::fluent_generated as fluent; diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index c10b3296497..f885b20c761 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -4,9 +4,8 @@ use rustc_ast::{NodeId, PatKind, attr, token}; use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, Features, GateIssue}; use rustc_session::Session; use rustc_session::parse::{feature_err, feature_err_issue, feature_warn}; -use rustc_span::Span; use rustc_span::source_map::Spanned; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::{Span, Symbol, sym}; use rustc_target::spec::abi; use thin_vec::ThinVec; diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 04ffa2cffe3..172df102929 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -17,15 +17,15 @@ use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree}; use rustc_ast::util::classify; use rustc_ast::util::comments::{Comment, CommentStyle}; use rustc_ast::{ - self as ast, AttrArgs, AttrArgsEq, BindingMode, BlockCheckMode, ByRef, DelimArgs, GenericArg, - GenericBound, InlineAsmOperand, InlineAsmOptions, InlineAsmRegOrRegClass, - InlineAsmTemplatePiece, PatKind, RangeEnd, RangeSyntax, Safety, SelfKind, Term, attr, + self as ast, AttrArgs, BindingMode, BlockCheckMode, ByRef, DelimArgs, GenericArg, GenericBound, + InlineAsmOperand, InlineAsmOptions, InlineAsmRegOrRegClass, InlineAsmTemplatePiece, PatKind, + RangeEnd, RangeSyntax, Safety, SelfKind, Term, attr, }; use rustc_data_structures::sync::Lrc; use rustc_span::edition::Edition; use rustc_span::source_map::{SourceMap, Spanned}; -use rustc_span::symbol::{Ident, IdentPrinter, Symbol, kw, sym}; -use rustc_span::{BytePos, CharPos, DUMMY_SP, FileName, Pos, Span}; +use rustc_span::symbol::IdentPrinter; +use rustc_span::{BytePos, CharPos, DUMMY_SP, FileName, Ident, Pos, Span, Symbol, kw, sym}; use thin_vec::ThinVec; use crate::pp::Breaks::{Consistent, Inconsistent}; @@ -359,7 +359,7 @@ fn binop_to_string(op: BinOpToken) -> &'static str { } } -fn doc_comment_to_string( +pub fn doc_comment_to_string( comment_kind: CommentKind, attr_style: ast::AttrStyle, data: Symbol, @@ -648,20 +648,13 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere AttrArgs::Empty => { self.print_path(&item.path, false, 0); } - AttrArgs::Eq { value: AttrArgsEq::Ast(expr), .. } => { + AttrArgs::Eq { expr, .. } => { self.print_path(&item.path, false, 0); self.space(); self.word_space("="); let token_str = self.expr_to_string(expr); self.word(token_str); } - AttrArgs::Eq { value: AttrArgsEq::Hir(lit), .. } => { - self.print_path(&item.path, false, 0); - self.space(); - self.word_space("="); - let token_str = self.meta_item_lit_to_string(lit); - self.word(token_str); - } } match item.unsafety { ast::Safety::Unsafe(_) => self.pclose(), @@ -732,7 +725,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + 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() { @@ -1211,8 +1204,10 @@ impl<'a> State<'a> { } ast::TyKind::Path(Some(qself), path) => self.print_qpath(path, qself, false), ast::TyKind::TraitObject(bounds, syntax) => { - if *syntax == ast::TraitObjectSyntax::Dyn { - self.word_nbsp("dyn"); + match syntax { + ast::TraitObjectSyntax::Dyn => self.word_nbsp("dyn"), + ast::TraitObjectSyntax::DynStar => self.word_nbsp("dyn*"), + ast::TraitObjectSyntax::None => {} } self.print_type_bounds(bounds); } @@ -1661,11 +1656,14 @@ impl<'a> State<'a> { }, |f| f.pat.span, ); - if *etc == ast::PatFieldsRest::Rest { + if let ast::PatFieldsRest::Rest | ast::PatFieldsRest::Recovered(_) = etc { if !fields.is_empty() { self.word_space(","); } self.word(".."); + if let ast::PatFieldsRest::Recovered(_) = etc { + self.word("/* recovered parse error */"); + } } if !empty { self.space(); diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 1ae765c0130..897c275d850 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -3,7 +3,7 @@ use itertools::{Itertools, Position}; use rustc_ast as ast; use rustc_ast::ModKind; use rustc_ast::ptr::P; -use rustc_span::symbol::Ident; +use rustc_span::Ident; use crate::pp::Breaks::Inconsistent; use crate::pprust::state::fixup::FixupContext; diff --git a/compiler/rustc_ast_pretty/src/pprust/tests.rs b/compiler/rustc_ast_pretty/src/pprust/tests.rs index 01e5dff34b7..4c42dd1f202 100644 --- a/compiler/rustc_ast_pretty/src/pprust/tests.rs +++ b/compiler/rustc_ast_pretty/src/pprust/tests.rs @@ -1,6 +1,5 @@ use rustc_ast as ast; -use rustc_span::symbol::Ident; -use rustc_span::{DUMMY_SP, create_default_session_globals_then}; +use rustc_span::{DUMMY_SP, Ident, create_default_session_globals_then}; use thin_vec::ThinVec; use super::*; diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs deleted file mode 100644 index 94f9727eb7f..00000000000 --- a/compiler/rustc_attr/src/builtin.rs +++ /dev/null @@ -1,1349 +0,0 @@ -//! Parsing and validation of builtin attributes - -use std::num::NonZero; - -use rustc_abi::Align; -use rustc_ast::{ - self as ast, Attribute, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId, - attr, -}; -use rustc_ast_pretty::pprust; -use rustc_errors::ErrorGuaranteed; -use rustc_feature::{Features, GatedCfg, find_gated_cfg, is_builtin_attr_name}; -use rustc_macros::{Decodable, Encodable, HashStable_Generic}; -use rustc_session::config::ExpectedValues; -use rustc_session::lint::BuiltinLintDiag; -use rustc_session::lint::builtin::UNEXPECTED_CFGS; -use rustc_session::parse::feature_err; -use rustc_session::{RustcVersion, Session}; -use rustc_span::Span; -use rustc_span::hygiene::Transparency; -use rustc_span::symbol::{Symbol, kw, sym}; - -use crate::fluent_generated; -use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause}; - -/// The version placeholder that recently stabilized features contain inside the -/// `since` field of the `#[stable]` attribute. -/// -/// For more, see [this pull request](https://github.com/rust-lang/rust/pull/100591). -pub const VERSION_PLACEHOLDER: &str = "CURRENT_RUSTC_VERSION"; - -pub fn is_builtin_attr(attr: &Attribute) -> bool { - attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name)) -} - -pub(crate) enum UnsupportedLiteralReason { - Generic, - CfgString, - CfgBoolean, - DeprecatedString, - DeprecatedKvPair, -} - -#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] -pub enum InlineAttr { - None, - Hint, - Always, - Never, -} - -#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, HashStable_Generic)] -pub enum InstructionSetAttr { - ArmA32, - ArmT32, -} - -#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] -pub enum OptimizeAttr { - None, - Speed, - Size, -} - -/// Represents the following attributes: -/// -/// - `#[stable]` -/// - `#[unstable]` -#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] -#[derive(HashStable_Generic)] -pub struct Stability { - pub level: StabilityLevel, - pub feature: Symbol, -} - -impl Stability { - pub fn is_unstable(&self) -> bool { - self.level.is_unstable() - } - - pub fn is_stable(&self) -> bool { - self.level.is_stable() - } - - pub fn stable_since(&self) -> Option<StableSince> { - self.level.stable_since() - } -} - -/// Represents the `#[rustc_const_unstable]` and `#[rustc_const_stable]` attributes. -#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] -#[derive(HashStable_Generic)] -pub struct ConstStability { - pub level: StabilityLevel, - pub feature: Symbol, - /// This is true iff the `const_stable_indirect` attribute is present. - pub const_stable_indirect: bool, - /// whether the function has a `#[rustc_promotable]` attribute - pub promotable: bool, -} - -impl ConstStability { - pub fn is_const_unstable(&self) -> bool { - self.level.is_unstable() - } - - pub fn is_const_stable(&self) -> bool { - self.level.is_stable() - } -} - -/// Represents the `#[rustc_default_body_unstable]` attribute. -#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] -#[derive(HashStable_Generic)] -pub struct DefaultBodyStability { - pub level: StabilityLevel, - pub feature: Symbol, -} - -/// The available stability levels. -#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)] -#[derive(HashStable_Generic)] -pub enum StabilityLevel { - /// `#[unstable]` - Unstable { - /// Reason for the current stability level. - reason: UnstableReason, - /// Relevant `rust-lang/rust` issue. - issue: Option<NonZero<u32>>, - is_soft: bool, - /// If part of a feature is stabilized and a new feature is added for the remaining parts, - /// then the `implied_by` attribute is used to indicate which now-stable feature previously - /// contained an item. - /// - /// ```pseudo-Rust - /// #[unstable(feature = "foo", issue = "...")] - /// fn foo() {} - /// #[unstable(feature = "foo", issue = "...")] - /// fn foobar() {} - /// ``` - /// - /// ...becomes... - /// - /// ```pseudo-Rust - /// #[stable(feature = "foo", since = "1.XX.X")] - /// fn foo() {} - /// #[unstable(feature = "foobar", issue = "...", implied_by = "foo")] - /// fn foobar() {} - /// ``` - implied_by: Option<Symbol>, - }, - /// `#[stable]` - Stable { - /// Rust release which stabilized this feature. - since: StableSince, - /// Is this item allowed to be referred to on stable, despite being contained in unstable - /// modules? - allowed_through_unstable_modules: bool, - }, -} - -/// Rust release in which a feature is stabilized. -#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, PartialOrd, Ord, Hash)] -#[derive(HashStable_Generic)] -pub enum StableSince { - Version(RustcVersion), - /// Stabilized in the upcoming version, whatever number that is. - Current, - /// Failed to parse a stabilization version. - Err, -} - -impl StabilityLevel { - pub fn is_unstable(&self) -> bool { - matches!(self, StabilityLevel::Unstable { .. }) - } - pub fn is_stable(&self) -> bool { - matches!(self, StabilityLevel::Stable { .. }) - } - pub fn stable_since(&self) -> Option<StableSince> { - match *self { - StabilityLevel::Stable { since, .. } => Some(since), - StabilityLevel::Unstable { .. } => None, - } - } -} - -#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)] -#[derive(HashStable_Generic)] -pub enum UnstableReason { - None, - Default, - Some(Symbol), -} - -impl UnstableReason { - fn from_opt_reason(reason: Option<Symbol>) -> Self { - // UnstableReason::Default constructed manually - match reason { - Some(r) => Self::Some(r), - None => Self::None, - } - } - - pub fn to_opt_reason(&self) -> Option<Symbol> { - match self { - Self::None => None, - Self::Default => Some(sym::unstable_location_reason_default), - Self::Some(r) => Some(*r), - } - } -} - -/// Collects stability info from `stable`/`unstable`/`rustc_allowed_through_unstable_modules` -/// attributes in `attrs`. Returns `None` if no stability attributes are found. -pub fn find_stability( - sess: &Session, - attrs: &[Attribute], - item_sp: Span, -) -> Option<(Stability, Span)> { - let mut stab: Option<(Stability, Span)> = None; - let mut allowed_through_unstable_modules = false; - - for attr in attrs { - match attr.name_or_empty() { - sym::rustc_allowed_through_unstable_modules => allowed_through_unstable_modules = true, - sym::unstable => { - if stab.is_some() { - sess.dcx() - .emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span }); - break; - } - - if let Some((feature, level)) = parse_unstability(sess, attr) { - stab = Some((Stability { level, feature }, attr.span)); - } - } - sym::stable => { - if stab.is_some() { - sess.dcx() - .emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span }); - break; - } - if let Some((feature, level)) = parse_stability(sess, attr) { - stab = Some((Stability { level, feature }, attr.span)); - } - } - _ => {} - } - } - - if allowed_through_unstable_modules { - match &mut stab { - Some(( - Stability { - level: StabilityLevel::Stable { allowed_through_unstable_modules, .. }, - .. - }, - _, - )) => *allowed_through_unstable_modules = true, - _ => { - sess.dcx() - .emit_err(session_diagnostics::RustcAllowedUnstablePairing { span: item_sp }); - } - } - } - - stab -} - -/// Collects stability info from `rustc_const_stable`/`rustc_const_unstable`/`rustc_promotable` -/// attributes in `attrs`. Returns `None` if no stability attributes are found. -pub fn find_const_stability( - sess: &Session, - attrs: &[Attribute], - item_sp: Span, -) -> Option<(ConstStability, Span)> { - let mut const_stab: Option<(ConstStability, Span)> = None; - let mut promotable = false; - let mut const_stable_indirect = false; - - for attr in attrs { - match attr.name_or_empty() { - sym::rustc_promotable => promotable = true, - sym::rustc_const_stable_indirect => const_stable_indirect = true, - sym::rustc_const_unstable => { - if const_stab.is_some() { - sess.dcx() - .emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span }); - break; - } - - if let Some((feature, level)) = parse_unstability(sess, attr) { - const_stab = Some(( - ConstStability { - level, - feature, - const_stable_indirect: false, - promotable: false, - }, - attr.span, - )); - } - } - sym::rustc_const_stable => { - if const_stab.is_some() { - sess.dcx() - .emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span }); - break; - } - if let Some((feature, level)) = parse_stability(sess, attr) { - const_stab = Some(( - ConstStability { - level, - feature, - const_stable_indirect: false, - promotable: false, - }, - attr.span, - )); - } - } - _ => {} - } - } - - // Merge promotable and const_stable_indirect into stability info - if promotable { - match &mut const_stab { - Some((stab, _)) => stab.promotable = promotable, - _ => { - _ = sess - .dcx() - .emit_err(session_diagnostics::RustcPromotablePairing { span: item_sp }) - } - } - } - if const_stable_indirect { - match &mut const_stab { - Some((stab, _)) => { - if stab.is_const_unstable() { - stab.const_stable_indirect = true; - } else { - _ = sess.dcx().emit_err(session_diagnostics::RustcConstStableIndirectPairing { - span: item_sp, - }) - } - } - _ => { - // This function has no const stability attribute, but has `const_stable_indirect`. - // We ignore that; unmarked functions are subject to recursive const stability - // checks by default so we do carry out the user's intent. - } - } - } - - const_stab -} - -/// Calculates the const stability for a const function in a `-Zforce-unstable-if-unmarked` crate -/// without the `staged_api` feature. -pub fn unmarked_crate_const_stab( - _sess: &Session, - attrs: &[Attribute], - regular_stab: Stability, -) -> ConstStability { - assert!(regular_stab.level.is_unstable()); - // The only attribute that matters here is `rustc_const_stable_indirect`. - // We enforce recursive const stability rules for those functions. - let const_stable_indirect = - attrs.iter().any(|a| a.name_or_empty() == sym::rustc_const_stable_indirect); - ConstStability { - feature: regular_stab.feature, - const_stable_indirect, - promotable: false, - level: regular_stab.level, - } -} - -/// Collects stability info from `rustc_default_body_unstable` attributes in `attrs`. -/// Returns `None` if no stability attributes are found. -pub fn find_body_stability( - sess: &Session, - attrs: &[Attribute], -) -> Option<(DefaultBodyStability, Span)> { - let mut body_stab: Option<(DefaultBodyStability, Span)> = None; - - for attr in attrs { - if attr.has_name(sym::rustc_default_body_unstable) { - if body_stab.is_some() { - sess.dcx() - .emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span }); - break; - } - - if let Some((feature, level)) = parse_unstability(sess, attr) { - body_stab = Some((DefaultBodyStability { level, feature }, attr.span)); - } - } - } - - body_stab -} - -fn insert_or_error(sess: &Session, meta: &MetaItem, item: &mut Option<Symbol>) -> Option<()> { - if item.is_some() { - sess.dcx().emit_err(session_diagnostics::MultipleItem { - span: meta.span, - item: pprust::path_to_string(&meta.path), - }); - None - } else if let Some(v) = meta.value_str() { - *item = Some(v); - Some(()) - } else { - sess.dcx().emit_err(session_diagnostics::IncorrectMetaItem { span: meta.span }); - None - } -} - -/// Read the content of a `stable`/`rustc_const_stable` attribute, and return the feature name and -/// its stability information. -fn parse_stability(sess: &Session, attr: &Attribute) -> Option<(Symbol, StabilityLevel)> { - let meta = attr.meta()?; - let MetaItem { kind: MetaItemKind::List(ref metas), .. } = meta else { return None }; - - let mut feature = None; - let mut since = None; - for meta in metas { - let Some(mi) = meta.meta_item() else { - sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral { - span: meta.span(), - reason: UnsupportedLiteralReason::Generic, - is_bytestr: false, - start_point_span: sess.source_map().start_point(meta.span()), - }); - return None; - }; - - match mi.name_or_empty() { - sym::feature => insert_or_error(sess, mi, &mut feature)?, - sym::since => insert_or_error(sess, mi, &mut since)?, - _ => { - sess.dcx().emit_err(session_diagnostics::UnknownMetaItem { - span: meta.span(), - item: pprust::path_to_string(&mi.path), - expected: &["feature", "since"], - }); - return None; - } - } - } - - let feature = match feature { - Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature), - Some(_bad_feature) => { - Err(sess.dcx().emit_err(session_diagnostics::NonIdentFeature { span: attr.span })) - } - None => Err(sess.dcx().emit_err(session_diagnostics::MissingFeature { span: attr.span })), - }; - - let since = if let Some(since) = since { - if since.as_str() == VERSION_PLACEHOLDER { - StableSince::Current - } else if let Some(version) = parse_version(since) { - StableSince::Version(version) - } else { - sess.dcx().emit_err(session_diagnostics::InvalidSince { span: attr.span }); - StableSince::Err - } - } else { - sess.dcx().emit_err(session_diagnostics::MissingSince { span: attr.span }); - StableSince::Err - }; - - match feature { - Ok(feature) => { - let level = StabilityLevel::Stable { since, allowed_through_unstable_modules: false }; - Some((feature, level)) - } - Err(ErrorGuaranteed { .. }) => None, - } -} - -/// Read the content of a `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable` -/// attribute, and return the feature name and its stability information. -fn parse_unstability(sess: &Session, attr: &Attribute) -> Option<(Symbol, StabilityLevel)> { - let meta = attr.meta()?; - let MetaItem { kind: MetaItemKind::List(ref metas), .. } = meta else { return None }; - - let mut feature = None; - let mut reason = None; - let mut issue = None; - let mut issue_num = None; - let mut is_soft = false; - let mut implied_by = None; - for meta in metas { - let Some(mi) = meta.meta_item() else { - sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral { - span: meta.span(), - reason: UnsupportedLiteralReason::Generic, - is_bytestr: false, - start_point_span: sess.source_map().start_point(meta.span()), - }); - return None; - }; - - match mi.name_or_empty() { - sym::feature => insert_or_error(sess, mi, &mut feature)?, - sym::reason => insert_or_error(sess, mi, &mut reason)?, - sym::issue => { - insert_or_error(sess, mi, &mut issue)?; - - // These unwraps are safe because `insert_or_error` ensures the meta item - // is a name/value pair string literal. - issue_num = match issue.unwrap().as_str() { - "none" => None, - issue => match issue.parse::<NonZero<u32>>() { - Ok(num) => Some(num), - Err(err) => { - sess.dcx().emit_err( - session_diagnostics::InvalidIssueString { - span: mi.span, - cause: session_diagnostics::InvalidIssueStringCause::from_int_error_kind( - mi.name_value_literal_span().unwrap(), - err.kind(), - ), - }, - ); - return None; - } - }, - }; - } - sym::soft => { - if !mi.is_word() { - sess.dcx().emit_err(session_diagnostics::SoftNoArgs { span: mi.span }); - } - is_soft = true; - } - sym::implied_by => insert_or_error(sess, mi, &mut implied_by)?, - _ => { - sess.dcx().emit_err(session_diagnostics::UnknownMetaItem { - span: meta.span(), - item: pprust::path_to_string(&mi.path), - expected: &["feature", "reason", "issue", "soft", "implied_by"], - }); - return None; - } - } - } - - let feature = match feature { - Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature), - Some(_bad_feature) => { - Err(sess.dcx().emit_err(session_diagnostics::NonIdentFeature { span: attr.span })) - } - None => Err(sess.dcx().emit_err(session_diagnostics::MissingFeature { span: attr.span })), - }; - - let issue = issue - .ok_or_else(|| sess.dcx().emit_err(session_diagnostics::MissingIssue { span: attr.span })); - - match (feature, issue) { - (Ok(feature), Ok(_)) => { - let level = StabilityLevel::Unstable { - reason: UnstableReason::from_opt_reason(reason), - issue: issue_num, - is_soft, - implied_by, - }; - Some((feature, level)) - } - (Err(ErrorGuaranteed { .. }), _) | (_, Err(ErrorGuaranteed { .. })) => None, - } -} - -pub fn find_crate_name(attrs: &[Attribute]) -> Option<Symbol> { - attr::first_attr_value_str_by_name(attrs, sym::crate_name) -} - -#[derive(Clone, Debug)] -pub struct Condition { - pub name: Symbol, - pub name_span: Span, - pub value: Option<Symbol>, - pub value_span: Option<Span>, - pub span: Span, -} - -/// Tests if a cfg-pattern matches the cfg set -pub fn cfg_matches( - cfg: &ast::MetaItemInner, - sess: &Session, - lint_node_id: NodeId, - features: Option<&Features>, -) -> bool { - eval_condition(cfg, sess, features, &mut |cfg| { - try_gate_cfg(cfg.name, cfg.span, sess, features); - match sess.psess.check_config.expecteds.get(&cfg.name) { - Some(ExpectedValues::Some(values)) if !values.contains(&cfg.value) => { - sess.psess.buffer_lint( - UNEXPECTED_CFGS, - cfg.span, - lint_node_id, - BuiltinLintDiag::UnexpectedCfgValue( - (cfg.name, cfg.name_span), - cfg.value.map(|v| (v, cfg.value_span.unwrap())), - ), - ); - } - None if sess.psess.check_config.exhaustive_names => { - sess.psess.buffer_lint( - UNEXPECTED_CFGS, - cfg.span, - lint_node_id, - BuiltinLintDiag::UnexpectedCfgName( - (cfg.name, cfg.name_span), - cfg.value.map(|v| (v, cfg.value_span.unwrap())), - ), - ); - } - _ => { /* not unexpected */ } - } - sess.psess.config.contains(&(cfg.name, cfg.value)) - }) -} - -fn try_gate_cfg(name: Symbol, span: Span, sess: &Session, features: Option<&Features>) { - let gate = find_gated_cfg(|sym| sym == name); - if let (Some(feats), Some(gated_cfg)) = (features, gate) { - gate_cfg(gated_cfg, span, sess, feats); - } -} - -#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable -fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &Session, features: &Features) { - let (cfg, feature, has_feature) = gated_cfg; - if !has_feature(features) && !cfg_span.allows_unstable(*feature) { - let explain = format!("`cfg({cfg})` is experimental and subject to change"); - feature_err(sess, *feature, cfg_span, explain).emit(); - } -} - -/// Parse a rustc version number written inside string literal in an attribute, -/// like appears in `since = "1.0.0"`. Suffixes like "-dev" and "-nightly" are -/// not accepted in this position, unlike when parsing CFG_RELEASE. -pub fn parse_version(s: Symbol) -> Option<RustcVersion> { - let mut components = s.as_str().split('-'); - let d = components.next()?; - if components.next().is_some() { - return None; - } - let mut digits = d.splitn(3, '.'); - let major = digits.next()?.parse().ok()?; - let minor = digits.next()?.parse().ok()?; - let patch = digits.next().unwrap_or("0").parse().ok()?; - Some(RustcVersion { major, minor, patch }) -} - -/// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to -/// evaluate individual items. -pub fn eval_condition( - cfg: &ast::MetaItemInner, - sess: &Session, - features: Option<&Features>, - eval: &mut impl FnMut(Condition) -> bool, -) -> bool { - let dcx = sess.dcx(); - - let cfg = match cfg { - ast::MetaItemInner::MetaItem(meta_item) => meta_item, - ast::MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => { - if let Some(features) = features { - // we can't use `try_gate_cfg` as symbols don't differentiate between `r#true` - // and `true`, and we want to keep the former working without feature gate - gate_cfg( - &( - if *b { kw::True } else { kw::False }, - sym::cfg_boolean_literals, - |features: &Features| features.cfg_boolean_literals(), - ), - cfg.span(), - sess, - features, - ); - } - return *b; - } - _ => { - dcx.emit_err(session_diagnostics::UnsupportedLiteral { - span: cfg.span(), - reason: UnsupportedLiteralReason::CfgBoolean, - is_bytestr: false, - start_point_span: sess.source_map().start_point(cfg.span()), - }); - return false; - } - }; - - match &cfg.kind { - ast::MetaItemKind::List(mis) if cfg.name_or_empty() == sym::version => { - try_gate_cfg(sym::version, cfg.span, sess, features); - let (min_version, span) = match &mis[..] { - [MetaItemInner::Lit(MetaItemLit { kind: LitKind::Str(sym, ..), span, .. })] => { - (sym, span) - } - [ - MetaItemInner::Lit(MetaItemLit { span, .. }) - | MetaItemInner::MetaItem(MetaItem { span, .. }), - ] => { - dcx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: *span }); - return false; - } - [..] => { - dcx.emit_err(session_diagnostics::ExpectedSingleVersionLiteral { - span: cfg.span, - }); - return false; - } - }; - let Some(min_version) = parse_version(*min_version) else { - dcx.emit_warn(session_diagnostics::UnknownVersionLiteral { span: *span }); - return false; - }; - - // See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details - if sess.psess.assume_incomplete_release { - RustcVersion::CURRENT > min_version - } else { - RustcVersion::CURRENT >= min_version - } - } - ast::MetaItemKind::List(mis) => { - for mi in mis.iter() { - if mi.meta_item_or_bool().is_none() { - dcx.emit_err(session_diagnostics::UnsupportedLiteral { - span: mi.span(), - reason: UnsupportedLiteralReason::Generic, - is_bytestr: false, - start_point_span: sess.source_map().start_point(mi.span()), - }); - return false; - } - } - - // The unwraps below may look dangerous, but we've already asserted - // that they won't fail with the loop above. - match cfg.name_or_empty() { - sym::any => mis - .iter() - // We don't use any() here, because we want to evaluate all cfg condition - // as eval_condition can (and does) extra checks - .fold(false, |res, mi| res | eval_condition(mi, sess, features, eval)), - sym::all => mis - .iter() - // We don't use all() here, because we want to evaluate all cfg condition - // as eval_condition can (and does) extra checks - .fold(true, |res, mi| res & eval_condition(mi, sess, features, eval)), - sym::not => { - let [mi] = mis.as_slice() else { - dcx.emit_err(session_diagnostics::ExpectedOneCfgPattern { span: cfg.span }); - return false; - }; - - !eval_condition(mi, sess, features, eval) - } - sym::target => { - if let Some(features) = features - && !features.cfg_target_compact() - { - feature_err( - sess, - sym::cfg_target_compact, - cfg.span, - fluent_generated::attr_unstable_cfg_target_compact, - ) - .emit(); - } - - mis.iter().fold(true, |res, mi| { - let Some(mut mi) = mi.meta_item().cloned() else { - dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { - span: mi.span(), - }); - return false; - }; - - if let [seg, ..] = &mut mi.path.segments[..] { - seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name)); - } - - res & eval_condition( - &ast::MetaItemInner::MetaItem(mi), - sess, - features, - eval, - ) - }) - } - _ => { - dcx.emit_err(session_diagnostics::InvalidPredicate { - span: cfg.span, - predicate: pprust::path_to_string(&cfg.path), - }); - false - } - } - } - ast::MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => { - dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: cfg.path.span }); - true - } - MetaItemKind::NameValue(lit) if !lit.kind.is_str() => { - dcx.emit_err(session_diagnostics::UnsupportedLiteral { - span: lit.span, - reason: UnsupportedLiteralReason::CfgString, - is_bytestr: lit.kind.is_bytestr(), - start_point_span: sess.source_map().start_point(lit.span), - }); - true - } - ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => { - let ident = cfg.ident().expect("multi-segment cfg predicate"); - eval(Condition { - name: ident.name, - name_span: ident.span, - value: cfg.value_str(), - value_span: cfg.name_value_literal_span(), - span: cfg.span, - }) - } - } -} - -#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)] -pub struct Deprecation { - pub since: DeprecatedSince, - /// The note to issue a reason. - pub note: Option<Symbol>, - /// A text snippet used to completely replace any use of the deprecated item in an expression. - /// - /// This is currently unstable. - pub suggestion: Option<Symbol>, -} - -/// Release in which an API is deprecated. -#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)] -pub enum DeprecatedSince { - RustcVersion(RustcVersion), - /// Deprecated in the future ("to be determined"). - Future, - /// `feature(staged_api)` is off. Deprecation versions outside the standard - /// library are allowed to be arbitrary strings, for better or worse. - NonStandard(Symbol), - /// Deprecation version is unspecified but optional. - Unspecified, - /// Failed to parse a deprecation version, or the deprecation version is - /// unspecified and required. An error has already been emitted. - Err, -} - -impl Deprecation { - /// Whether an item marked with #[deprecated(since = "X")] is currently - /// deprecated (i.e., whether X is not greater than the current rustc - /// version). - pub fn is_in_effect(&self) -> bool { - match self.since { - DeprecatedSince::RustcVersion(since) => since <= RustcVersion::CURRENT, - DeprecatedSince::Future => false, - // The `since` field doesn't have semantic purpose without `#![staged_api]`. - DeprecatedSince::NonStandard(_) => true, - // Assume deprecation is in effect if "since" field is absent or invalid. - DeprecatedSince::Unspecified | DeprecatedSince::Err => true, - } - } - - pub fn is_since_rustc_version(&self) -> bool { - matches!(self.since, DeprecatedSince::RustcVersion(_)) - } -} - -/// Finds the deprecation attribute. `None` if none exists. -pub fn find_deprecation( - sess: &Session, - features: &Features, - attrs: &[Attribute], -) -> Option<(Deprecation, Span)> { - let mut depr: Option<(Deprecation, Span)> = None; - let is_rustc = features.staged_api(); - - 'outer: for attr in attrs { - if !attr.has_name(sym::deprecated) { - continue; - } - - let Some(meta) = attr.meta() else { - continue; - }; - let mut since = None; - let mut note = None; - let mut suggestion = None; - match &meta.kind { - MetaItemKind::Word => {} - MetaItemKind::NameValue(..) => note = meta.value_str(), - MetaItemKind::List(list) => { - let get = |meta: &MetaItem, item: &mut Option<Symbol>| { - if item.is_some() { - sess.dcx().emit_err(session_diagnostics::MultipleItem { - span: meta.span, - item: pprust::path_to_string(&meta.path), - }); - return false; - } - if let Some(v) = meta.value_str() { - *item = Some(v); - true - } else { - if let Some(lit) = meta.name_value_literal() { - sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral { - span: lit.span, - reason: UnsupportedLiteralReason::DeprecatedString, - is_bytestr: lit.kind.is_bytestr(), - start_point_span: sess.source_map().start_point(lit.span), - }); - } else { - sess.dcx().emit_err(session_diagnostics::IncorrectMetaItem { - span: meta.span, - }); - } - - false - } - }; - - for meta in list { - match meta { - MetaItemInner::MetaItem(mi) => match mi.name_or_empty() { - sym::since => { - if !get(mi, &mut since) { - continue 'outer; - } - } - sym::note => { - if !get(mi, &mut note) { - continue 'outer; - } - } - sym::suggestion => { - if !features.deprecated_suggestion() { - sess.dcx().emit_err( - session_diagnostics::DeprecatedItemSuggestion { - span: mi.span, - is_nightly: sess.is_nightly_build(), - details: (), - }, - ); - } - - if !get(mi, &mut suggestion) { - continue 'outer; - } - } - _ => { - sess.dcx().emit_err(session_diagnostics::UnknownMetaItem { - span: meta.span(), - item: pprust::path_to_string(&mi.path), - expected: if features.deprecated_suggestion() { - &["since", "note", "suggestion"] - } else { - &["since", "note"] - }, - }); - continue 'outer; - } - }, - MetaItemInner::Lit(lit) => { - sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral { - span: lit.span, - reason: UnsupportedLiteralReason::DeprecatedKvPair, - is_bytestr: false, - start_point_span: sess.source_map().start_point(lit.span), - }); - continue 'outer; - } - } - } - } - } - - let since = if let Some(since) = since { - if since.as_str() == "TBD" { - DeprecatedSince::Future - } else if !is_rustc { - DeprecatedSince::NonStandard(since) - } else if let Some(version) = parse_version(since) { - DeprecatedSince::RustcVersion(version) - } else { - sess.dcx().emit_err(session_diagnostics::InvalidSince { span: attr.span }); - DeprecatedSince::Err - } - } else if is_rustc { - sess.dcx().emit_err(session_diagnostics::MissingSince { span: attr.span }); - DeprecatedSince::Err - } else { - DeprecatedSince::Unspecified - }; - - if is_rustc && note.is_none() { - sess.dcx().emit_err(session_diagnostics::MissingNote { span: attr.span }); - continue; - } - - depr = Some((Deprecation { since, note, suggestion }, attr.span)); - } - - depr -} - -#[derive(PartialEq, Debug, Encodable, Decodable, Copy, Clone)] -pub enum ReprAttr { - ReprInt(IntType), - ReprRust, - ReprC, - ReprPacked(Align), - ReprSimd, - ReprTransparent, - ReprAlign(Align), -} - -#[derive(Eq, PartialEq, Debug, Copy, Clone)] -#[derive(Encodable, Decodable, HashStable_Generic)] -pub enum IntType { - SignedInt(ast::IntTy), - UnsignedInt(ast::UintTy), -} - -impl IntType { - #[inline] - pub fn is_signed(self) -> bool { - use IntType::*; - - match self { - SignedInt(..) => true, - UnsignedInt(..) => false, - } - } -} - -/// Parse #[repr(...)] forms. -/// -/// Valid repr contents: any of the primitive integral type names (see -/// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use -/// the same discriminant size that the corresponding C enum would or C -/// structure layout, `packed` to remove padding, and `transparent` to delegate representation -/// concerns to the only non-ZST field. -pub fn find_repr_attrs(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> { - if attr.has_name(sym::repr) { parse_repr_attr(sess, attr) } else { Vec::new() } -} - -pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> { - assert!(attr.has_name(sym::repr), "expected `#[repr(..)]`, found: {attr:?}"); - use ReprAttr::*; - let mut acc = Vec::new(); - let dcx = sess.dcx(); - - if let Some(items) = attr.meta_item_list() { - for item in items { - let mut recognised = false; - if item.is_word() { - let hint = match item.name_or_empty() { - sym::Rust => Some(ReprRust), - sym::C => Some(ReprC), - sym::packed => Some(ReprPacked(Align::ONE)), - sym::simd => Some(ReprSimd), - sym::transparent => Some(ReprTransparent), - sym::align => { - sess.dcx().emit_err(session_diagnostics::InvalidReprAlignNeedArg { - span: item.span(), - }); - recognised = true; - None - } - name => int_type_of_word(name).map(ReprInt), - }; - - if let Some(h) = hint { - recognised = true; - acc.push(h); - } - } else if let Some((name, value)) = item.singleton_lit_list() { - let mut literal_error = None; - let mut err_span = item.span(); - if name == sym::align { - recognised = true; - match parse_alignment(&value.kind) { - Ok(literal) => acc.push(ReprAlign(literal)), - Err(message) => { - err_span = value.span; - literal_error = Some(message) - } - }; - } else if name == sym::packed { - recognised = true; - match parse_alignment(&value.kind) { - Ok(literal) => acc.push(ReprPacked(literal)), - Err(message) => { - err_span = value.span; - literal_error = Some(message) - } - }; - } else if matches!(name, sym::Rust | sym::C | sym::simd | sym::transparent) - || int_type_of_word(name).is_some() - { - recognised = true; - sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoParen { - span: item.span(), - name: name.to_ident_string(), - }); - } - if let Some(literal_error) = literal_error { - sess.dcx().emit_err(session_diagnostics::InvalidReprGeneric { - span: err_span, - repr_arg: name.to_ident_string(), - error_part: literal_error, - }); - } - } else if let Some(meta_item) = item.meta_item() { - match &meta_item.kind { - MetaItemKind::NameValue(value) => { - if meta_item.has_name(sym::align) || meta_item.has_name(sym::packed) { - let name = meta_item.name_or_empty().to_ident_string(); - recognised = true; - sess.dcx().emit_err(session_diagnostics::IncorrectReprFormatGeneric { - span: item.span(), - repr_arg: &name, - cause: IncorrectReprFormatGenericCause::from_lit_kind( - item.span(), - &value.kind, - &name, - ), - }); - } else if matches!( - meta_item.name_or_empty(), - sym::Rust | sym::C | sym::simd | sym::transparent - ) || int_type_of_word(meta_item.name_or_empty()).is_some() - { - recognised = true; - sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoValue { - span: meta_item.span, - name: meta_item.name_or_empty().to_ident_string(), - }); - } - } - MetaItemKind::List(nested_items) => { - if meta_item.has_name(sym::align) { - recognised = true; - if let [nested_item] = nested_items.as_slice() { - sess.dcx().emit_err( - session_diagnostics::IncorrectReprFormatExpectInteger { - span: nested_item.span(), - }, - ); - } else { - sess.dcx().emit_err( - session_diagnostics::IncorrectReprFormatAlignOneArg { - span: meta_item.span, - }, - ); - } - } else if meta_item.has_name(sym::packed) { - recognised = true; - if let [nested_item] = nested_items.as_slice() { - sess.dcx().emit_err( - session_diagnostics::IncorrectReprFormatPackedExpectInteger { - span: nested_item.span(), - }, - ); - } else { - sess.dcx().emit_err( - session_diagnostics::IncorrectReprFormatPackedOneOrZeroArg { - span: meta_item.span, - }, - ); - } - } else if matches!( - meta_item.name_or_empty(), - sym::Rust | sym::C | sym::simd | sym::transparent - ) || int_type_of_word(meta_item.name_or_empty()).is_some() - { - recognised = true; - sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoParen { - span: meta_item.span, - name: meta_item.name_or_empty().to_ident_string(), - }); - } - } - _ => (), - } - } - if !recognised { - // Not a word we recognize. This will be caught and reported by - // the `check_mod_attrs` pass, but this pass doesn't always run - // (e.g. if we only pretty-print the source), so we have to gate - // the `span_delayed_bug` call as follows: - if sess.opts.pretty.map_or(true, |pp| pp.needs_analysis()) { - dcx.span_delayed_bug(item.span(), "unrecognized representation hint"); - } - } - } - } - acc -} - -fn int_type_of_word(s: Symbol) -> Option<IntType> { - use IntType::*; - - match s { - sym::i8 => Some(SignedInt(ast::IntTy::I8)), - sym::u8 => Some(UnsignedInt(ast::UintTy::U8)), - sym::i16 => Some(SignedInt(ast::IntTy::I16)), - sym::u16 => Some(UnsignedInt(ast::UintTy::U16)), - sym::i32 => Some(SignedInt(ast::IntTy::I32)), - sym::u32 => Some(UnsignedInt(ast::UintTy::U32)), - sym::i64 => Some(SignedInt(ast::IntTy::I64)), - sym::u64 => Some(UnsignedInt(ast::UintTy::U64)), - sym::i128 => Some(SignedInt(ast::IntTy::I128)), - sym::u128 => Some(UnsignedInt(ast::UintTy::U128)), - sym::isize => Some(SignedInt(ast::IntTy::Isize)), - sym::usize => Some(UnsignedInt(ast::UintTy::Usize)), - _ => None, - } -} - -pub enum TransparencyError { - UnknownTransparency(Symbol, Span), - MultipleTransparencyAttrs(Span, Span), -} - -pub fn find_transparency( - attrs: &[Attribute], - macro_rules: bool, -) -> (Transparency, Option<TransparencyError>) { - let mut transparency = None; - let mut error = None; - for attr in attrs { - if attr.has_name(sym::rustc_macro_transparency) { - if let Some((_, old_span)) = transparency { - error = Some(TransparencyError::MultipleTransparencyAttrs(old_span, attr.span)); - break; - } else if let Some(value) = attr.value_str() { - transparency = Some(( - match value { - sym::transparent => Transparency::Transparent, - sym::semitransparent => Transparency::SemiTransparent, - sym::opaque => Transparency::Opaque, - _ => { - error = Some(TransparencyError::UnknownTransparency(value, attr.span)); - continue; - } - }, - attr.span, - )); - } - } - } - let fallback = if macro_rules { Transparency::SemiTransparent } else { Transparency::Opaque }; - (transparency.map_or(fallback, |t| t.0), error) -} - -pub fn allow_internal_unstable<'a>( - sess: &'a Session, - attrs: &'a [Attribute], -) -> impl Iterator<Item = Symbol> + 'a { - allow_unstable(sess, attrs, sym::allow_internal_unstable) -} - -pub fn rustc_allow_const_fn_unstable<'a>( - sess: &'a Session, - attrs: &'a [Attribute], -) -> impl Iterator<Item = Symbol> + 'a { - allow_unstable(sess, attrs, sym::rustc_allow_const_fn_unstable) -} - -fn allow_unstable<'a>( - sess: &'a Session, - attrs: &'a [Attribute], - symbol: Symbol, -) -> impl Iterator<Item = Symbol> + 'a { - let attrs = attr::filter_by_name(attrs, symbol); - let list = attrs - .filter_map(move |attr| { - attr.meta_item_list().or_else(|| { - sess.dcx().emit_err(session_diagnostics::ExpectsFeatureList { - span: attr.span, - name: symbol.to_ident_string(), - }); - None - }) - }) - .flatten(); - - list.into_iter().filter_map(move |it| { - let name = it.ident().map(|ident| ident.name); - if name.is_none() { - sess.dcx().emit_err(session_diagnostics::ExpectsFeatures { - span: it.span(), - name: symbol.to_ident_string(), - }); - } - name - }) -} - -pub fn parse_alignment(node: &ast::LitKind) -> Result<Align, &'static str> { - if let ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed) = node { - // `Align::from_bytes` accepts 0 as an input, check is_power_of_two() first - if literal.get().is_power_of_two() { - // Only possible error is larger than 2^29 - literal - .get() - .try_into() - .ok() - .and_then(|v| Align::from_bytes(v).ok()) - .ok_or("larger than 2^29") - } else { - Err("not a power of two") - } - } else { - Err("not an unsuffixed integer") - } -} - -/// Read the content of a `rustc_confusables` attribute, and return the list of candidate names. -pub fn parse_confusables(attr: &Attribute) -> Option<Vec<Symbol>> { - let meta = attr.meta()?; - let MetaItem { kind: MetaItemKind::List(ref metas), .. } = meta else { return None }; - - let mut candidates = Vec::new(); - - for meta in metas { - let MetaItemInner::Lit(meta_lit) = meta else { - return None; - }; - candidates.push(meta_lit.symbol); - } - - Some(candidates) -} diff --git a/compiler/rustc_attr/Cargo.toml b/compiler/rustc_attr_data_structures/Cargo.toml index 3b24452450a..2ee58f24470 100644 --- a/compiler/rustc_attr/Cargo.toml +++ b/compiler/rustc_attr_data_structures/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "rustc_attr" +name = "rustc_attr_data_structures" version = "0.0.0" edition = "2021" diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs new file mode 100644 index 00000000000..8986bec57de --- /dev/null +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -0,0 +1,106 @@ +use rustc_abi::Align; +use rustc_ast as ast; +use rustc_macros::{Decodable, Encodable, HashStable_Generic}; +use rustc_span::{Span, Symbol}; + +use crate::RustcVersion; + +#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] +pub enum InlineAttr { + None, + Hint, + Always, + Never, +} + +#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, HashStable_Generic)] +pub enum InstructionSetAttr { + ArmA32, + ArmT32, +} + +#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] +pub enum OptimizeAttr { + None, + Speed, + Size, +} + +#[derive(Clone, Debug, Encodable, Decodable)] +pub enum DiagnosticAttribute { + // tidy-alphabetical-start + DoNotRecommend, + OnUnimplemented, + // tidy-alphabetical-end +} + +#[derive(PartialEq, Debug, Encodable, Decodable, Copy, Clone)] +pub enum ReprAttr { + ReprInt(IntType), + ReprRust, + ReprC, + ReprPacked(Align), + ReprSimd, + ReprTransparent, + ReprAlign(Align), +} +pub use ReprAttr::*; + +pub enum TransparencyError { + UnknownTransparency(Symbol, Span), + MultipleTransparencyAttrs(Span, Span), +} + +#[derive(Eq, PartialEq, Debug, Copy, Clone)] +#[derive(Encodable, Decodable)] +pub enum IntType { + SignedInt(ast::IntTy), + UnsignedInt(ast::UintTy), +} + +#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)] +pub struct Deprecation { + pub since: DeprecatedSince, + /// The note to issue a reason. + pub note: Option<Symbol>, + /// A text snippet used to completely replace any use of the deprecated item in an expression. + /// + /// This is currently unstable. + pub suggestion: Option<Symbol>, +} + +/// Release in which an API is deprecated. +#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)] +pub enum DeprecatedSince { + RustcVersion(RustcVersion), + /// Deprecated in the future ("to be determined"). + Future, + /// `feature(staged_api)` is off. Deprecation versions outside the standard + /// library are allowed to be arbitrary strings, for better or worse. + NonStandard(Symbol), + /// Deprecation version is unspecified but optional. + Unspecified, + /// Failed to parse a deprecation version, or the deprecation version is + /// unspecified and required. An error has already been emitted. + Err, +} + +impl Deprecation { + /// Whether an item marked with #[deprecated(since = "X")] is currently + /// deprecated (i.e., whether X is not greater than the current rustc + /// version). + pub fn is_in_effect(&self) -> bool { + match self.since { + DeprecatedSince::RustcVersion(since) => since <= RustcVersion::CURRENT, + DeprecatedSince::Future => false, + // The `since` field doesn't have semantic purpose without `#![staged_api]`. + DeprecatedSince::NonStandard(_) => true, + // Assume deprecation is in effect if "since" field is absent or invalid. + DeprecatedSince::Unspecified | DeprecatedSince::Err => true, + } + } + + pub fn is_since_rustc_version(&self) -> bool { + matches!(self.since, DeprecatedSince::RustcVersion(_)) + } +} diff --git a/compiler/rustc_attr_data_structures/src/lib.rs b/compiler/rustc_attr_data_structures/src/lib.rs new file mode 100644 index 00000000000..4f204aeab64 --- /dev/null +++ b/compiler/rustc_attr_data_structures/src/lib.rs @@ -0,0 +1,16 @@ +// tidy-alphabetical-start +#![allow(internal_features)] +#![doc(rust_logo)] +#![feature(let_chains)] +#![feature(rustdoc_internals)] +#![warn(unreachable_pub)] +// tidy-alphabetical-end + +mod attributes; +mod stability; +mod version; + +pub use attributes::*; +pub(crate) use rustc_session::HashStableContext; +pub use stability::*; +pub use version::*; diff --git a/compiler/rustc_attr_data_structures/src/stability.rs b/compiler/rustc_attr_data_structures/src/stability.rs new file mode 100644 index 00000000000..021fe40e3e0 --- /dev/null +++ b/compiler/rustc_attr_data_structures/src/stability.rs @@ -0,0 +1,200 @@ +use std::num::NonZero; + +use rustc_macros::{Decodable, Encodable, HashStable_Generic}; +use rustc_span::{Symbol, sym}; + +use crate::RustcVersion; + +/// The version placeholder that recently stabilized features contain inside the +/// `since` field of the `#[stable]` attribute. +/// +/// For more, see [this pull request](https://github.com/rust-lang/rust/pull/100591). +pub const VERSION_PLACEHOLDER: &str = "CURRENT_RUSTC_VERSION"; + +/// Represents the following attributes: +/// +/// - `#[stable]` +/// - `#[unstable]` +#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(HashStable_Generic)] +pub struct Stability { + pub level: StabilityLevel, + pub feature: Symbol, +} + +impl Stability { + pub fn is_unstable(&self) -> bool { + self.level.is_unstable() + } + + pub fn is_stable(&self) -> bool { + self.level.is_stable() + } + + pub fn stable_since(&self) -> Option<StableSince> { + self.level.stable_since() + } +} + +/// Represents the `#[rustc_const_unstable]` and `#[rustc_const_stable]` attributes. +#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(HashStable_Generic)] +pub struct ConstStability { + pub level: StabilityLevel, + pub feature: Symbol, + /// whether the function has a `#[rustc_promotable]` attribute + pub promotable: bool, + /// This is true iff the `const_stable_indirect` attribute is present. + pub const_stable_indirect: bool, +} + +impl ConstStability { + pub fn from_partial( + PartialConstStability { level, feature, promotable }: PartialConstStability, + const_stable_indirect: bool, + ) -> Self { + Self { const_stable_indirect, level, feature, promotable } + } + + /// The stability assigned to unmarked items when -Zforce-unstable-if-unmarked is set. + pub fn unmarked(const_stable_indirect: bool, regular_stab: Stability) -> Self { + Self { + feature: regular_stab.feature, + promotable: false, + level: regular_stab.level, + const_stable_indirect, + } + } + + pub fn is_const_unstable(&self) -> bool { + self.level.is_unstable() + } + + pub fn is_const_stable(&self) -> bool { + self.level.is_stable() + } +} + +/// Excludes `const_stable_indirect`. This is necessary because when `-Zforce-unstable-if-unmarked` +/// is set, we need to encode standalone `#[rustc_const_stable_indirect]` attributes +#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(HashStable_Generic)] +pub struct PartialConstStability { + pub level: StabilityLevel, + pub feature: Symbol, + /// whether the function has a `#[rustc_promotable]` attribute + pub promotable: bool, +} + +impl PartialConstStability { + pub fn is_const_unstable(&self) -> bool { + self.level.is_unstable() + } + + pub fn is_const_stable(&self) -> bool { + self.level.is_stable() + } +} + +/// The available stability levels. +#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)] +#[derive(HashStable_Generic)] +pub enum StabilityLevel { + /// `#[unstable]` + Unstable { + /// Reason for the current stability level. + reason: UnstableReason, + /// Relevant `rust-lang/rust` issue. + issue: Option<NonZero<u32>>, + is_soft: bool, + /// If part of a feature is stabilized and a new feature is added for the remaining parts, + /// then the `implied_by` attribute is used to indicate which now-stable feature previously + /// contained an item. + /// + /// ```pseudo-Rust + /// #[unstable(feature = "foo", issue = "...")] + /// fn foo() {} + /// #[unstable(feature = "foo", issue = "...")] + /// fn foobar() {} + /// ``` + /// + /// ...becomes... + /// + /// ```pseudo-Rust + /// #[stable(feature = "foo", since = "1.XX.X")] + /// fn foo() {} + /// #[unstable(feature = "foobar", issue = "...", implied_by = "foo")] + /// fn foobar() {} + /// ``` + implied_by: Option<Symbol>, + }, + /// `#[stable]` + Stable { + /// Rust release which stabilized this feature. + since: StableSince, + /// Is this item allowed to be referred to on stable, despite being contained in unstable + /// modules? + allowed_through_unstable_modules: bool, + }, +} + +/// Rust release in which a feature is stabilized. +#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, PartialOrd, Ord, Hash)] +#[derive(HashStable_Generic)] +pub enum StableSince { + /// also stores the original symbol for printing + Version(RustcVersion), + /// Stabilized in the upcoming version, whatever number that is. + Current, + /// Failed to parse a stabilization version. + Err, +} + +impl StabilityLevel { + pub fn is_unstable(&self) -> bool { + matches!(self, StabilityLevel::Unstable { .. }) + } + pub fn is_stable(&self) -> bool { + matches!(self, StabilityLevel::Stable { .. }) + } + pub fn stable_since(&self) -> Option<StableSince> { + match *self { + StabilityLevel::Stable { since, .. } => Some(since), + StabilityLevel::Unstable { .. } => None, + } + } +} + +#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)] +#[derive(HashStable_Generic)] +pub enum UnstableReason { + None, + Default, + Some(Symbol), +} + +/// Represents the `#[rustc_default_body_unstable]` attribute. +#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(HashStable_Generic)] +pub struct DefaultBodyStability { + pub level: StabilityLevel, + pub feature: Symbol, +} + +impl UnstableReason { + pub fn from_opt_reason(reason: Option<Symbol>) -> Self { + // UnstableReason::Default constructed manually + match reason { + Some(r) => Self::Some(r), + None => Self::None, + } + } + + pub fn to_opt_reason(&self) -> Option<Symbol> { + match self { + Self::None => None, + Self::Default => Some(sym::unstable_location_reason_default), + Self::Some(r) => Some(*r), + } + } +} diff --git a/compiler/rustc_session/src/version.rs b/compiler/rustc_attr_data_structures/src/version.rs index 1696eaf902b..6be875ad4be 100644 --- a/compiler/rustc_session/src/version.rs +++ b/compiler/rustc_attr_data_structures/src/version.rs @@ -1,7 +1,5 @@ -use std::borrow::Cow; use std::fmt::{self, Display}; -use rustc_errors::IntoDiagArg; use rustc_macros::{Decodable, Encodable, HashStable_Generic, current_rustc_version}; #[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -21,9 +19,3 @@ impl Display for RustcVersion { write!(formatter, "{}.{}.{}", self.major, self.minor, self.patch) } } - -impl IntoDiagArg for RustcVersion { - fn into_diag_arg(self) -> rustc_errors::DiagArgValue { - rustc_errors::DiagArgValue::Str(Cow::Owned(self.to_string())) - } -} diff --git a/compiler/rustc_attr_parsing/Cargo.toml b/compiler/rustc_attr_parsing/Cargo.toml new file mode 100644 index 00000000000..7ccedf40c3f --- /dev/null +++ b/compiler/rustc_attr_parsing/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "rustc_attr_parsing" +version = "0.0.0" +edition = "2021" + +[dependencies] +# tidy-alphabetical-start +rustc_abi = { path = "../rustc_abi" } +rustc_ast = { path = "../rustc_ast" } +rustc_ast_pretty = { path = "../rustc_ast_pretty" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_errors = { path = "../rustc_errors" } +rustc_feature = { path = "../rustc_feature" } +rustc_fluent_macro = { path = "../rustc_fluent_macro" } +rustc_lexer = { path = "../rustc_lexer" } +rustc_macros = { path = "../rustc_macros" } +rustc_serialize = { path = "../rustc_serialize" } +rustc_session = { path = "../rustc_session" } +rustc_span = { path = "../rustc_span" } +# tidy-alphabetical-end diff --git a/compiler/rustc_attr/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 235ab7572c4..faa2865cb91 100644 --- a/compiler/rustc_attr/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -1,46 +1,46 @@ -attr_cfg_predicate_identifier = +attr_parsing_cfg_predicate_identifier = `cfg` predicate key must be an identifier -attr_deprecated_item_suggestion = +attr_parsing_deprecated_item_suggestion = suggestions on deprecated items are unstable .help = add `#![feature(deprecated_suggestion)]` to the crate root .note = see #94785 for more details -attr_expected_one_cfg_pattern = +attr_parsing_expected_one_cfg_pattern = expected 1 cfg-pattern -attr_expected_single_version_literal = +attr_parsing_expected_single_version_literal = expected single version literal -attr_expected_version_literal = +attr_parsing_expected_version_literal = expected a version literal -attr_expects_feature_list = +attr_parsing_expects_feature_list = `{$name}` expects a list of feature names -attr_expects_features = +attr_parsing_expects_features = `{$name}` expects feature names -attr_incorrect_meta_item = +attr_parsing_incorrect_meta_item = incorrect meta item -attr_incorrect_repr_format_align_one_arg = +attr_parsing_incorrect_repr_format_align_one_arg = incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses -attr_incorrect_repr_format_expect_literal_integer = +attr_parsing_incorrect_repr_format_expect_literal_integer = incorrect `repr(align)` attribute format: `align` expects a literal integer as argument -attr_incorrect_repr_format_generic = +attr_parsing_incorrect_repr_format_generic = incorrect `repr({$repr_arg})` attribute format .suggestion = use parentheses instead -attr_incorrect_repr_format_packed_expect_integer = +attr_parsing_incorrect_repr_format_packed_expect_integer = incorrect `repr(packed)` attribute format: `packed` expects a literal integer as argument -attr_incorrect_repr_format_packed_one_or_zero_arg = +attr_parsing_incorrect_repr_format_packed_one_or_zero_arg = incorrect `repr(packed)` attribute format: `packed` takes exactly one parenthesized argument, or no parentheses at all -attr_invalid_issue_string = +attr_parsing_invalid_issue_string = `issue` must be a non-zero numeric string or "none" .must_not_be_zero = `issue` must not be "0", use "none" instead .empty = cannot parse integer from empty string @@ -48,77 +48,77 @@ attr_invalid_issue_string = .pos_overflow = number too large to fit in target type .neg_overflow = number too small to fit in target type -attr_invalid_predicate = +attr_parsing_invalid_predicate = invalid predicate `{$predicate}` -attr_invalid_repr_align_need_arg = +attr_parsing_invalid_repr_align_need_arg = invalid `repr(align)` attribute: `align` needs an argument .suggestion = supply an argument here -attr_invalid_repr_generic = +attr_parsing_invalid_repr_generic = invalid `repr({$repr_arg})` attribute: {$error_part} -attr_invalid_repr_hint_no_paren = +attr_parsing_invalid_repr_hint_no_paren = invalid representation hint: `{$name}` does not take a parenthesized argument list -attr_invalid_repr_hint_no_value = +attr_parsing_invalid_repr_hint_no_value = invalid representation hint: `{$name}` does not take a value -attr_invalid_since = +attr_parsing_invalid_since = 'since' must be a Rust version number, such as "1.31.0" -attr_missing_feature = +attr_parsing_missing_feature = missing 'feature' -attr_missing_issue = +attr_parsing_missing_issue = missing 'issue' -attr_missing_note = +attr_parsing_missing_note = missing 'note' -attr_missing_since = +attr_parsing_missing_since = missing 'since' -attr_multiple_item = +attr_parsing_multiple_item = multiple '{$item}' items -attr_multiple_stability_levels = +attr_parsing_multiple_stability_levels = multiple stability levels -attr_non_ident_feature = +attr_parsing_non_ident_feature = 'feature' is not an identifier -attr_rustc_allowed_unstable_pairing = +attr_parsing_rustc_allowed_unstable_pairing = `rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute -attr_rustc_const_stable_indirect_pairing = +attr_parsing_rustc_const_stable_indirect_pairing = `const_stable_indirect` attribute does not make sense on `rustc_const_stable` function, its behavior is already implied -attr_rustc_promotable_pairing = +attr_parsing_rustc_promotable_pairing = `rustc_promotable` attribute must be paired with either a `rustc_const_unstable` or a `rustc_const_stable` attribute -attr_soft_no_args = +attr_parsing_soft_no_args = `soft` should not have any arguments -attr_unknown_meta_item = +attr_parsing_unknown_meta_item = unknown meta item '{$item}' .label = expected one of {$expected} -attr_unknown_version_literal = +attr_parsing_unknown_version_literal = unknown version literal format, assuming it refers to a future version -attr_unstable_cfg_target_compact = +attr_parsing_unstable_cfg_target_compact = compact `cfg(target(..))` is experimental and subject to change -attr_unsupported_literal_cfg_boolean = +attr_parsing_unsupported_literal_cfg_boolean = literal in `cfg` predicate value must be a boolean -attr_unsupported_literal_cfg_string = +attr_parsing_unsupported_literal_cfg_string = literal in `cfg` predicate value must be a string -attr_unsupported_literal_deprecated_kv_pair = +attr_parsing_unsupported_literal_deprecated_kv_pair = item in `deprecated` must be a key/value pair -attr_unsupported_literal_deprecated_string = +attr_parsing_unsupported_literal_deprecated_string = literal in `deprecated` value must be a string -attr_unsupported_literal_generic = +attr_parsing_unsupported_literal_generic = unsupported literal -attr_unsupported_literal_suggestion = +attr_parsing_unsupported_literal_suggestion = consider removing the prefix diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs new file mode 100644 index 00000000000..471168ed4f5 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs @@ -0,0 +1,49 @@ +use rustc_ast::attr::{AttributeExt, filter_by_name}; +use rustc_session::Session; +use rustc_span::{Symbol, sym}; + +use crate::session_diagnostics; + +pub fn allow_internal_unstable<'a>( + sess: &'a Session, + attrs: &'a [impl AttributeExt], +) -> impl Iterator<Item = Symbol> + 'a { + allow_unstable(sess, attrs, sym::allow_internal_unstable) +} + +pub fn rustc_allow_const_fn_unstable<'a>( + sess: &'a Session, + attrs: &'a [impl AttributeExt], +) -> impl Iterator<Item = Symbol> + 'a { + allow_unstable(sess, attrs, sym::rustc_allow_const_fn_unstable) +} + +fn allow_unstable<'a>( + sess: &'a Session, + attrs: &'a [impl AttributeExt], + symbol: Symbol, +) -> impl Iterator<Item = Symbol> + 'a { + let attrs = filter_by_name(attrs, symbol); + let list = attrs + .filter_map(move |attr| { + attr.meta_item_list().or_else(|| { + sess.dcx().emit_err(session_diagnostics::ExpectsFeatureList { + span: attr.span(), + name: symbol.to_ident_string(), + }); + None + }) + }) + .flatten(); + + list.into_iter().filter_map(move |it| { + let name = it.ident().map(|ident| ident.name); + if name.is_none() { + sess.dcx().emit_err(session_diagnostics::ExpectsFeatures { + span: it.span(), + name: symbol.to_ident_string(), + }); + } + name + }) +} diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs new file mode 100644 index 00000000000..bb9aaaa2fea --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -0,0 +1,253 @@ +//! Parsing and validation of builtin attributes + +use rustc_ast::{self as ast, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId}; +use rustc_ast_pretty::pprust; +use rustc_attr_data_structures::RustcVersion; +use rustc_feature::{Features, GatedCfg, find_gated_cfg}; +use rustc_session::Session; +use rustc_session::config::ExpectedValues; +use rustc_session::lint::BuiltinLintDiag; +use rustc_session::lint::builtin::UNEXPECTED_CFGS; +use rustc_session::parse::feature_err; +use rustc_span::{Span, Symbol, kw, sym}; + +use crate::util::UnsupportedLiteralReason; +use crate::{fluent_generated, parse_version, session_diagnostics}; + +#[derive(Clone, Debug)] +pub struct Condition { + pub name: Symbol, + pub name_span: Span, + pub value: Option<Symbol>, + pub value_span: Option<Span>, + pub span: Span, +} + +/// Tests if a cfg-pattern matches the cfg set +pub fn cfg_matches( + cfg: &ast::MetaItemInner, + sess: &Session, + lint_node_id: NodeId, + features: Option<&Features>, +) -> bool { + eval_condition(cfg, sess, features, &mut |cfg| { + try_gate_cfg(cfg.name, cfg.span, sess, features); + match sess.psess.check_config.expecteds.get(&cfg.name) { + Some(ExpectedValues::Some(values)) if !values.contains(&cfg.value) => { + sess.psess.buffer_lint( + UNEXPECTED_CFGS, + cfg.span, + lint_node_id, + BuiltinLintDiag::UnexpectedCfgValue( + (cfg.name, cfg.name_span), + cfg.value.map(|v| (v, cfg.value_span.unwrap())), + ), + ); + } + None if sess.psess.check_config.exhaustive_names => { + sess.psess.buffer_lint( + UNEXPECTED_CFGS, + cfg.span, + lint_node_id, + BuiltinLintDiag::UnexpectedCfgName( + (cfg.name, cfg.name_span), + cfg.value.map(|v| (v, cfg.value_span.unwrap())), + ), + ); + } + _ => { /* not unexpected */ } + } + sess.psess.config.contains(&(cfg.name, cfg.value)) + }) +} + +fn try_gate_cfg(name: Symbol, span: Span, sess: &Session, features: Option<&Features>) { + let gate = find_gated_cfg(|sym| sym == name); + if let (Some(feats), Some(gated_cfg)) = (features, gate) { + gate_cfg(gated_cfg, span, sess, feats); + } +} + +#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable +fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &Session, features: &Features) { + let (cfg, feature, has_feature) = gated_cfg; + if !has_feature(features) && !cfg_span.allows_unstable(*feature) { + let explain = format!("`cfg({cfg})` is experimental and subject to change"); + feature_err(sess, *feature, cfg_span, explain).emit(); + } +} + +/// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to +/// evaluate individual items. +pub fn eval_condition( + cfg: &ast::MetaItemInner, + sess: &Session, + features: Option<&Features>, + eval: &mut impl FnMut(Condition) -> bool, +) -> bool { + let dcx = sess.dcx(); + + let cfg = match cfg { + ast::MetaItemInner::MetaItem(meta_item) => meta_item, + ast::MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => { + if let Some(features) = features { + // we can't use `try_gate_cfg` as symbols don't differentiate between `r#true` + // and `true`, and we want to keep the former working without feature gate + gate_cfg( + &( + if *b { kw::True } else { kw::False }, + sym::cfg_boolean_literals, + |features: &Features| features.cfg_boolean_literals(), + ), + cfg.span(), + sess, + features, + ); + } + return *b; + } + _ => { + dcx.emit_err(session_diagnostics::UnsupportedLiteral { + span: cfg.span(), + reason: UnsupportedLiteralReason::CfgBoolean, + is_bytestr: false, + start_point_span: sess.source_map().start_point(cfg.span()), + }); + return false; + } + }; + + match &cfg.kind { + ast::MetaItemKind::List(mis) if cfg.name_or_empty() == sym::version => { + try_gate_cfg(sym::version, cfg.span, sess, features); + let (min_version, span) = match &mis[..] { + [MetaItemInner::Lit(MetaItemLit { kind: LitKind::Str(sym, ..), span, .. })] => { + (sym, span) + } + [ + MetaItemInner::Lit(MetaItemLit { span, .. }) + | MetaItemInner::MetaItem(MetaItem { span, .. }), + ] => { + dcx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: *span }); + return false; + } + [..] => { + dcx.emit_err(session_diagnostics::ExpectedSingleVersionLiteral { + span: cfg.span, + }); + return false; + } + }; + let Some(min_version) = parse_version(*min_version) else { + dcx.emit_warn(session_diagnostics::UnknownVersionLiteral { span: *span }); + return false; + }; + + // See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details + if sess.psess.assume_incomplete_release { + RustcVersion::CURRENT > min_version + } else { + RustcVersion::CURRENT >= min_version + } + } + ast::MetaItemKind::List(mis) => { + for mi in mis.iter() { + if mi.meta_item_or_bool().is_none() { + dcx.emit_err(session_diagnostics::UnsupportedLiteral { + span: mi.span(), + reason: UnsupportedLiteralReason::Generic, + is_bytestr: false, + start_point_span: sess.source_map().start_point(mi.span()), + }); + return false; + } + } + + // The unwraps below may look dangerous, but we've already asserted + // that they won't fail with the loop above. + match cfg.name_or_empty() { + sym::any => mis + .iter() + // We don't use any() here, because we want to evaluate all cfg condition + // as eval_condition can (and does) extra checks + .fold(false, |res, mi| res | eval_condition(mi, sess, features, eval)), + sym::all => mis + .iter() + // We don't use all() here, because we want to evaluate all cfg condition + // as eval_condition can (and does) extra checks + .fold(true, |res, mi| res & eval_condition(mi, sess, features, eval)), + sym::not => { + let [mi] = mis.as_slice() else { + dcx.emit_err(session_diagnostics::ExpectedOneCfgPattern { span: cfg.span }); + return false; + }; + + !eval_condition(mi, sess, features, eval) + } + sym::target => { + if let Some(features) = features + && !features.cfg_target_compact() + { + feature_err( + sess, + sym::cfg_target_compact, + cfg.span, + fluent_generated::attr_parsing_unstable_cfg_target_compact, + ) + .emit(); + } + + mis.iter().fold(true, |res, mi| { + let Some(mut mi) = mi.meta_item().cloned() else { + dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { + span: mi.span(), + }); + return false; + }; + + if let [seg, ..] = &mut mi.path.segments[..] { + seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name)); + } + + res & eval_condition( + &ast::MetaItemInner::MetaItem(mi), + sess, + features, + eval, + ) + }) + } + _ => { + dcx.emit_err(session_diagnostics::InvalidPredicate { + span: cfg.span, + predicate: pprust::path_to_string(&cfg.path), + }); + false + } + } + } + ast::MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => { + dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: cfg.path.span }); + true + } + MetaItemKind::NameValue(lit) if !lit.kind.is_str() => { + dcx.emit_err(session_diagnostics::UnsupportedLiteral { + span: lit.span, + reason: UnsupportedLiteralReason::CfgString, + is_bytestr: lit.kind.is_bytestr(), + start_point_span: sess.source_map().start_point(lit.span), + }); + true + } + ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => { + let ident = cfg.ident().expect("multi-segment cfg predicate"); + eval(Condition { + name: ident.name, + name_span: ident.span, + value: cfg.value_str(), + value_span: cfg.name_value_literal_span(), + span: cfg.span, + }) + } + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/confusables.rs b/compiler/rustc_attr_parsing/src/attributes/confusables.rs new file mode 100644 index 00000000000..2ced759fd88 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/confusables.rs @@ -0,0 +1,21 @@ +//! Parsing and validation of builtin attributes + +use rustc_ast::MetaItemInner; +use rustc_ast::attr::AttributeExt; +use rustc_span::Symbol; + +/// Read the content of a `rustc_confusables` attribute, and return the list of candidate names. +pub fn parse_confusables(attr: &impl AttributeExt) -> Option<Vec<Symbol>> { + let metas = attr.meta_item_list()?; + + let mut candidates = Vec::new(); + + for meta in metas { + let MetaItemInner::Lit(meta_lit) = meta else { + return None; + }; + candidates.push(meta_lit.symbol); + } + + Some(candidates) +} diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs new file mode 100644 index 00000000000..d7415a7198f --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -0,0 +1,148 @@ +//! Parsing and validation of builtin attributes + +use rustc_ast::attr::AttributeExt; +use rustc_ast::{MetaItem, MetaItemInner}; +use rustc_ast_pretty::pprust; +use rustc_attr_data_structures::{DeprecatedSince, Deprecation}; +use rustc_feature::Features; +use rustc_session::Session; +use rustc_span::{Span, Symbol, sym}; + +use super::util::UnsupportedLiteralReason; +use crate::{parse_version, session_diagnostics}; + +/// Finds the deprecation attribute. `None` if none exists. +pub fn find_deprecation( + sess: &Session, + features: &Features, + attrs: &[impl AttributeExt], +) -> Option<(Deprecation, Span)> { + let mut depr: Option<(Deprecation, Span)> = None; + let is_rustc = features.staged_api(); + + 'outer: for attr in attrs { + if !attr.has_name(sym::deprecated) { + continue; + } + + let mut since = None; + let mut note = None; + let mut suggestion = None; + + if attr.is_doc_comment() { + continue; + } else if attr.is_word() { + } else if let Some(value) = attr.value_str() { + note = Some(value) + } else if let Some(list) = attr.meta_item_list() { + let get = |meta: &MetaItem, item: &mut Option<Symbol>| { + if item.is_some() { + sess.dcx().emit_err(session_diagnostics::MultipleItem { + span: meta.span, + item: pprust::path_to_string(&meta.path), + }); + return false; + } + if let Some(v) = meta.value_str() { + *item = Some(v); + true + } else { + if let Some(lit) = meta.name_value_literal() { + sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral { + span: lit.span, + reason: UnsupportedLiteralReason::DeprecatedString, + is_bytestr: lit.kind.is_bytestr(), + start_point_span: sess.source_map().start_point(lit.span), + }); + } else { + sess.dcx() + .emit_err(session_diagnostics::IncorrectMetaItem { span: meta.span }); + } + false + } + }; + + for meta in &list { + match meta { + MetaItemInner::MetaItem(mi) => match mi.name_or_empty() { + sym::since => { + if !get(mi, &mut since) { + continue 'outer; + } + } + sym::note => { + if !get(mi, &mut note) { + continue 'outer; + } + } + sym::suggestion => { + if !features.deprecated_suggestion() { + sess.dcx().emit_err( + session_diagnostics::DeprecatedItemSuggestion { + span: mi.span, + is_nightly: sess.is_nightly_build(), + details: (), + }, + ); + } + + if !get(mi, &mut suggestion) { + continue 'outer; + } + } + _ => { + sess.dcx().emit_err(session_diagnostics::UnknownMetaItem { + span: meta.span(), + item: pprust::path_to_string(&mi.path), + expected: if features.deprecated_suggestion() { + &["since", "note", "suggestion"] + } else { + &["since", "note"] + }, + }); + continue 'outer; + } + }, + MetaItemInner::Lit(lit) => { + sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral { + span: lit.span, + reason: UnsupportedLiteralReason::DeprecatedKvPair, + is_bytestr: false, + start_point_span: sess.source_map().start_point(lit.span), + }); + continue 'outer; + } + } + } + } else { + continue; + } + + let since = if let Some(since) = since { + if since.as_str() == "TBD" { + DeprecatedSince::Future + } else if !is_rustc { + DeprecatedSince::NonStandard(since) + } else if let Some(version) = parse_version(since) { + DeprecatedSince::RustcVersion(version) + } else { + sess.dcx().emit_err(session_diagnostics::InvalidSince { span: attr.span() }); + DeprecatedSince::Err + } + } else if is_rustc { + sess.dcx().emit_err(session_diagnostics::MissingSince { span: attr.span() }); + DeprecatedSince::Err + } else { + DeprecatedSince::Unspecified + }; + + if is_rustc && note.is_none() { + sess.dcx().emit_err(session_diagnostics::MissingNote { span: attr.span() }); + continue; + } + + depr = Some((Deprecation { since, note, suggestion }, attr.span())); + } + + depr +} diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs new file mode 100644 index 00000000000..a78e0b54b64 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -0,0 +1,17 @@ +mod allow_unstable; +mod cfg; +mod confusables; +mod deprecation; +mod repr; +mod stability; +mod transparency; + +pub mod util; + +pub use allow_unstable::*; +pub use cfg::*; +pub use confusables::*; +pub use deprecation::*; +pub use repr::*; +pub use stability::*; +pub use transparency::*; diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs new file mode 100644 index 00000000000..124f0aa3eff --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs @@ -0,0 +1,215 @@ +//! Parsing and validation of builtin attributes + +use rustc_abi::Align; +use rustc_ast::attr::AttributeExt; +use rustc_ast::{self as ast, MetaItemKind}; +use rustc_attr_data_structures::IntType; +use rustc_attr_data_structures::ReprAttr::*; +use rustc_session::Session; +use rustc_span::{Symbol, sym}; + +use crate::ReprAttr; +use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause}; + +/// Parse #[repr(...)] forms. +/// +/// Valid repr contents: any of the primitive integral type names (see +/// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use +/// the same discriminant size that the corresponding C enum would or C +/// structure layout, `packed` to remove padding, and `transparent` to delegate representation +/// concerns to the only non-ZST field. +pub fn find_repr_attrs(sess: &Session, attr: &impl AttributeExt) -> Vec<ReprAttr> { + if attr.has_name(sym::repr) { parse_repr_attr(sess, attr) } else { Vec::new() } +} + +pub fn parse_repr_attr(sess: &Session, attr: &impl AttributeExt) -> Vec<ReprAttr> { + assert!(attr.has_name(sym::repr), "expected `#[repr(..)]`, found: {attr:?}"); + let mut acc = Vec::new(); + let dcx = sess.dcx(); + + if let Some(items) = attr.meta_item_list() { + for item in items { + let mut recognised = false; + if item.is_word() { + let hint = match item.name_or_empty() { + sym::Rust => Some(ReprRust), + sym::C => Some(ReprC), + sym::packed => Some(ReprPacked(Align::ONE)), + sym::simd => Some(ReprSimd), + sym::transparent => Some(ReprTransparent), + sym::align => { + sess.dcx().emit_err(session_diagnostics::InvalidReprAlignNeedArg { + span: item.span(), + }); + recognised = true; + None + } + name => int_type_of_word(name).map(ReprInt), + }; + + if let Some(h) = hint { + recognised = true; + acc.push(h); + } + } else if let Some((name, value)) = item.singleton_lit_list() { + let mut literal_error = None; + let mut err_span = item.span(); + if name == sym::align { + recognised = true; + match parse_alignment(&value.kind) { + Ok(literal) => acc.push(ReprAlign(literal)), + Err(message) => { + err_span = value.span; + literal_error = Some(message) + } + }; + } else if name == sym::packed { + recognised = true; + match parse_alignment(&value.kind) { + Ok(literal) => acc.push(ReprPacked(literal)), + Err(message) => { + err_span = value.span; + literal_error = Some(message) + } + }; + } else if matches!(name, sym::Rust | sym::C | sym::simd | sym::transparent) + || int_type_of_word(name).is_some() + { + recognised = true; + sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoParen { + span: item.span(), + name: name.to_ident_string(), + }); + } + if let Some(literal_error) = literal_error { + sess.dcx().emit_err(session_diagnostics::InvalidReprGeneric { + span: err_span, + repr_arg: name.to_ident_string(), + error_part: literal_error, + }); + } + } else if let Some(meta_item) = item.meta_item() { + match &meta_item.kind { + MetaItemKind::NameValue(value) => { + if meta_item.has_name(sym::align) || meta_item.has_name(sym::packed) { + let name = meta_item.name_or_empty().to_ident_string(); + recognised = true; + sess.dcx().emit_err(session_diagnostics::IncorrectReprFormatGeneric { + span: item.span(), + repr_arg: &name, + cause: IncorrectReprFormatGenericCause::from_lit_kind( + item.span(), + &value.kind, + &name, + ), + }); + } else if matches!( + meta_item.name_or_empty(), + sym::Rust | sym::C | sym::simd | sym::transparent + ) || int_type_of_word(meta_item.name_or_empty()).is_some() + { + recognised = true; + sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoValue { + span: meta_item.span, + name: meta_item.name_or_empty().to_ident_string(), + }); + } + } + MetaItemKind::List(nested_items) => { + if meta_item.has_name(sym::align) { + recognised = true; + if let [nested_item] = nested_items.as_slice() { + sess.dcx().emit_err( + session_diagnostics::IncorrectReprFormatExpectInteger { + span: nested_item.span(), + }, + ); + } else { + sess.dcx().emit_err( + session_diagnostics::IncorrectReprFormatAlignOneArg { + span: meta_item.span, + }, + ); + } + } else if meta_item.has_name(sym::packed) { + recognised = true; + if let [nested_item] = nested_items.as_slice() { + sess.dcx().emit_err( + session_diagnostics::IncorrectReprFormatPackedExpectInteger { + span: nested_item.span(), + }, + ); + } else { + sess.dcx().emit_err( + session_diagnostics::IncorrectReprFormatPackedOneOrZeroArg { + span: meta_item.span, + }, + ); + } + } else if matches!( + meta_item.name_or_empty(), + sym::Rust | sym::C | sym::simd | sym::transparent + ) || int_type_of_word(meta_item.name_or_empty()).is_some() + { + recognised = true; + sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoParen { + span: meta_item.span, + name: meta_item.name_or_empty().to_ident_string(), + }); + } + } + _ => (), + } + } + if !recognised { + // Not a word we recognize. This will be caught and reported by + // the `check_mod_attrs` pass, but this pass doesn't always run + // (e.g. if we only pretty-print the source), so we have to gate + // the `span_delayed_bug` call as follows: + if sess.opts.pretty.map_or(true, |pp| pp.needs_analysis()) { + dcx.span_delayed_bug(item.span(), "unrecognized representation hint"); + } + } + } + } + acc +} + +fn int_type_of_word(s: Symbol) -> Option<IntType> { + use rustc_attr_data_structures::IntType::*; + + match s { + sym::i8 => Some(SignedInt(ast::IntTy::I8)), + sym::u8 => Some(UnsignedInt(ast::UintTy::U8)), + sym::i16 => Some(SignedInt(ast::IntTy::I16)), + sym::u16 => Some(UnsignedInt(ast::UintTy::U16)), + sym::i32 => Some(SignedInt(ast::IntTy::I32)), + sym::u32 => Some(UnsignedInt(ast::UintTy::U32)), + sym::i64 => Some(SignedInt(ast::IntTy::I64)), + sym::u64 => Some(UnsignedInt(ast::UintTy::U64)), + sym::i128 => Some(SignedInt(ast::IntTy::I128)), + sym::u128 => Some(UnsignedInt(ast::UintTy::U128)), + sym::isize => Some(SignedInt(ast::IntTy::Isize)), + sym::usize => Some(UnsignedInt(ast::UintTy::Usize)), + _ => None, + } +} + +pub fn parse_alignment(node: &ast::LitKind) -> Result<Align, &'static str> { + if let ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed) = node { + // `Align::from_bytes` accepts 0 as an input, check is_power_of_two() first + if literal.get().is_power_of_two() { + // Only possible error is larger than 2^29 + literal + .get() + .try_into() + .ok() + .and_then(|v| Align::from_bytes(v).ok()) + .ok_or("larger than 2^29") + } else { + Err("not a power of two") + } + } else { + Err("not an unsuffixed integer") + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs new file mode 100644 index 00000000000..89937e1c593 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -0,0 +1,384 @@ +//! Parsing and validation of builtin attributes + +use std::num::NonZero; + +use rustc_ast::MetaItem; +use rustc_ast::attr::AttributeExt; +use rustc_ast_pretty::pprust; +use rustc_attr_data_structures::{ + ConstStability, DefaultBodyStability, Stability, StabilityLevel, StableSince, UnstableReason, + VERSION_PLACEHOLDER, +}; +use rustc_errors::ErrorGuaranteed; +use rustc_session::Session; +use rustc_span::{Span, Symbol, sym}; + +use crate::attributes::util::UnsupportedLiteralReason; +use crate::{parse_version, session_diagnostics}; + +/// Collects stability info from `stable`/`unstable`/`rustc_allowed_through_unstable_modules` +/// attributes in `attrs`. Returns `None` if no stability attributes are found. +pub fn find_stability( + sess: &Session, + attrs: &[impl AttributeExt], + item_sp: Span, +) -> Option<(Stability, Span)> { + let mut stab: Option<(Stability, Span)> = None; + let mut allowed_through_unstable_modules = false; + + for attr in attrs { + match attr.name_or_empty() { + sym::rustc_allowed_through_unstable_modules => allowed_through_unstable_modules = true, + sym::unstable => { + if stab.is_some() { + sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels { + span: attr.span(), + }); + break; + } + + if let Some((feature, level)) = parse_unstability(sess, attr) { + stab = Some((Stability { level, feature }, attr.span())); + } + } + sym::stable => { + if stab.is_some() { + sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels { + span: attr.span(), + }); + break; + } + if let Some((feature, level)) = parse_stability(sess, attr) { + stab = Some((Stability { level, feature }, attr.span())); + } + } + _ => {} + } + } + + if allowed_through_unstable_modules { + match &mut stab { + Some(( + Stability { + level: StabilityLevel::Stable { allowed_through_unstable_modules, .. }, + .. + }, + _, + )) => *allowed_through_unstable_modules = true, + _ => { + sess.dcx() + .emit_err(session_diagnostics::RustcAllowedUnstablePairing { span: item_sp }); + } + } + } + + stab +} + +/// Collects stability info from `rustc_const_stable`/`rustc_const_unstable`/`rustc_promotable` +/// attributes in `attrs`. Returns `None` if no stability attributes are found. +pub fn find_const_stability( + sess: &Session, + attrs: &[impl AttributeExt], + item_sp: Span, +) -> Option<(ConstStability, Span)> { + let mut const_stab: Option<(ConstStability, Span)> = None; + let mut promotable = false; + let mut const_stable_indirect = false; + + for attr in attrs { + match attr.name_or_empty() { + sym::rustc_promotable => promotable = true, + sym::rustc_const_stable_indirect => const_stable_indirect = true, + sym::rustc_const_unstable => { + if const_stab.is_some() { + sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels { + span: attr.span(), + }); + break; + } + + if let Some((feature, level)) = parse_unstability(sess, attr) { + const_stab = Some(( + ConstStability { + level, + feature, + const_stable_indirect: false, + promotable: false, + }, + attr.span(), + )); + } + } + sym::rustc_const_stable => { + if const_stab.is_some() { + sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels { + span: attr.span(), + }); + break; + } + if let Some((feature, level)) = parse_stability(sess, attr) { + const_stab = Some(( + ConstStability { + level, + feature, + const_stable_indirect: false, + promotable: false, + }, + attr.span(), + )); + } + } + _ => {} + } + } + + // Merge promotable and const_stable_indirect into stability info + if promotable { + match &mut const_stab { + Some((stab, _)) => stab.promotable = promotable, + _ => { + _ = sess + .dcx() + .emit_err(session_diagnostics::RustcPromotablePairing { span: item_sp }) + } + } + } + if const_stable_indirect { + match &mut const_stab { + Some((stab, _)) => { + if stab.is_const_unstable() { + stab.const_stable_indirect = true; + } else { + _ = sess.dcx().emit_err(session_diagnostics::RustcConstStableIndirectPairing { + span: item_sp, + }) + } + } + _ => { + // This function has no const stability attribute, but has `const_stable_indirect`. + // We ignore that; unmarked functions are subject to recursive const stability + // checks by default so we do carry out the user's intent. + } + } + } + + const_stab +} + +/// Calculates the const stability for a const function in a `-Zforce-unstable-if-unmarked` crate +/// without the `staged_api` feature. +pub fn unmarked_crate_const_stab( + _sess: &Session, + attrs: &[impl AttributeExt], + regular_stab: Stability, +) -> ConstStability { + assert!(regular_stab.level.is_unstable()); + // The only attribute that matters here is `rustc_const_stable_indirect`. + // We enforce recursive const stability rules for those functions. + let const_stable_indirect = + attrs.iter().any(|a| a.name_or_empty() == sym::rustc_const_stable_indirect); + ConstStability { + feature: regular_stab.feature, + const_stable_indirect, + promotable: false, + level: regular_stab.level, + } +} + +/// Collects stability info from `rustc_default_body_unstable` attributes in `attrs`. +/// Returns `None` if no stability attributes are found. +pub fn find_body_stability( + sess: &Session, + attrs: &[impl AttributeExt], +) -> Option<(DefaultBodyStability, Span)> { + let mut body_stab: Option<(DefaultBodyStability, Span)> = None; + + for attr in attrs { + if attr.has_name(sym::rustc_default_body_unstable) { + if body_stab.is_some() { + sess.dcx() + .emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span() }); + break; + } + + if let Some((feature, level)) = parse_unstability(sess, attr) { + body_stab = Some((DefaultBodyStability { level, feature }, attr.span())); + } + } + } + + body_stab +} + +fn insert_or_error(sess: &Session, meta: &MetaItem, item: &mut Option<Symbol>) -> Option<()> { + if item.is_some() { + sess.dcx().emit_err(session_diagnostics::MultipleItem { + span: meta.span, + item: pprust::path_to_string(&meta.path), + }); + None + } else if let Some(v) = meta.value_str() { + *item = Some(v); + Some(()) + } else { + sess.dcx().emit_err(session_diagnostics::IncorrectMetaItem { span: meta.span }); + None + } +} + +/// Read the content of a `stable`/`rustc_const_stable` attribute, and return the feature name and +/// its stability information. +fn parse_stability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol, StabilityLevel)> { + let metas = attr.meta_item_list()?; + + let mut feature = None; + let mut since = None; + for meta in metas { + let Some(mi) = meta.meta_item() else { + sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral { + span: meta.span(), + reason: UnsupportedLiteralReason::Generic, + is_bytestr: false, + start_point_span: sess.source_map().start_point(meta.span()), + }); + return None; + }; + + match mi.name_or_empty() { + sym::feature => insert_or_error(sess, mi, &mut feature)?, + sym::since => insert_or_error(sess, mi, &mut since)?, + _ => { + sess.dcx().emit_err(session_diagnostics::UnknownMetaItem { + span: meta.span(), + item: pprust::path_to_string(&mi.path), + expected: &["feature", "since"], + }); + return None; + } + } + } + + let feature = match feature { + Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature), + Some(_bad_feature) => { + Err(sess.dcx().emit_err(session_diagnostics::NonIdentFeature { span: attr.span() })) + } + None => Err(sess.dcx().emit_err(session_diagnostics::MissingFeature { span: attr.span() })), + }; + + let since = if let Some(since) = since { + if since.as_str() == VERSION_PLACEHOLDER { + StableSince::Current + } else if let Some(version) = parse_version(since) { + StableSince::Version(version) + } else { + sess.dcx().emit_err(session_diagnostics::InvalidSince { span: attr.span() }); + StableSince::Err + } + } else { + sess.dcx().emit_err(session_diagnostics::MissingSince { span: attr.span() }); + StableSince::Err + }; + + match feature { + Ok(feature) => { + let level = StabilityLevel::Stable { since, allowed_through_unstable_modules: false }; + Some((feature, level)) + } + Err(ErrorGuaranteed { .. }) => None, + } +} + +/// Read the content of a `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable` +/// attribute, and return the feature name and its stability information. +fn parse_unstability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol, StabilityLevel)> { + let metas = attr.meta_item_list()?; + + let mut feature = None; + let mut reason = None; + let mut issue = None; + let mut issue_num = None; + let mut is_soft = false; + let mut implied_by = None; + for meta in metas { + let Some(mi) = meta.meta_item() else { + sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral { + span: meta.span(), + reason: UnsupportedLiteralReason::Generic, + is_bytestr: false, + start_point_span: sess.source_map().start_point(meta.span()), + }); + return None; + }; + + match mi.name_or_empty() { + sym::feature => insert_or_error(sess, mi, &mut feature)?, + sym::reason => insert_or_error(sess, mi, &mut reason)?, + sym::issue => { + insert_or_error(sess, mi, &mut issue)?; + + // These unwraps are safe because `insert_or_error` ensures the meta item + // is a name/value pair string literal. + issue_num = match issue.unwrap().as_str() { + "none" => None, + issue => match issue.parse::<NonZero<u32>>() { + Ok(num) => Some(num), + Err(err) => { + sess.dcx().emit_err( + session_diagnostics::InvalidIssueString { + span: mi.span, + cause: session_diagnostics::InvalidIssueStringCause::from_int_error_kind( + mi.name_value_literal_span().unwrap(), + err.kind(), + ), + }, + ); + return None; + } + }, + }; + } + sym::soft => { + if !mi.is_word() { + sess.dcx().emit_err(session_diagnostics::SoftNoArgs { span: mi.span }); + } + is_soft = true; + } + sym::implied_by => insert_or_error(sess, mi, &mut implied_by)?, + _ => { + sess.dcx().emit_err(session_diagnostics::UnknownMetaItem { + span: meta.span(), + item: pprust::path_to_string(&mi.path), + expected: &["feature", "reason", "issue", "soft", "implied_by"], + }); + return None; + } + } + } + + let feature = match feature { + Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature), + Some(_bad_feature) => { + Err(sess.dcx().emit_err(session_diagnostics::NonIdentFeature { span: attr.span() })) + } + None => Err(sess.dcx().emit_err(session_diagnostics::MissingFeature { span: attr.span() })), + }; + + let issue = issue.ok_or_else(|| { + sess.dcx().emit_err(session_diagnostics::MissingIssue { span: attr.span() }) + }); + + match (feature, issue) { + (Ok(feature), Ok(_)) => { + let level = StabilityLevel::Unstable { + reason: UnstableReason::from_opt_reason(reason), + issue: issue_num, + is_soft, + implied_by, + }; + Some((feature, level)) + } + (Err(ErrorGuaranteed { .. }), _) | (_, Err(ErrorGuaranteed { .. })) => None, + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs new file mode 100644 index 00000000000..f4065a77048 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs @@ -0,0 +1,36 @@ +use rustc_ast::attr::AttributeExt; +use rustc_attr_data_structures::TransparencyError; +use rustc_span::hygiene::Transparency; +use rustc_span::sym; + +pub fn find_transparency( + attrs: &[impl AttributeExt], + macro_rules: bool, +) -> (Transparency, Option<TransparencyError>) { + let mut transparency = None; + let mut error = None; + for attr in attrs { + if attr.has_name(sym::rustc_macro_transparency) { + if let Some((_, old_span)) = transparency { + error = Some(TransparencyError::MultipleTransparencyAttrs(old_span, attr.span())); + break; + } else if let Some(value) = attr.value_str() { + transparency = Some(( + match value { + sym::transparent => Transparency::Transparent, + sym::semitransparent => Transparency::SemiTransparent, + sym::opaque => Transparency::Opaque, + _ => { + error = + Some(TransparencyError::UnknownTransparency(value, attr.span())); + continue; + } + }, + attr.span(), + )); + } + } + } + let fallback = if macro_rules { Transparency::SemiTransparent } else { Transparency::Opaque }; + (transparency.map_or(fallback, |t| t.0), error) +} diff --git a/compiler/rustc_attr_parsing/src/attributes/util.rs b/compiler/rustc_attr_parsing/src/attributes/util.rs new file mode 100644 index 00000000000..e36f7dfff5a --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/util.rs @@ -0,0 +1,36 @@ +use rustc_ast::attr::{AttributeExt, first_attr_value_str_by_name}; +use rustc_attr_data_structures::RustcVersion; +use rustc_feature::is_builtin_attr_name; +use rustc_span::{Symbol, sym}; + +pub(crate) enum UnsupportedLiteralReason { + Generic, + CfgString, + CfgBoolean, + DeprecatedString, + DeprecatedKvPair, +} + +pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool { + attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name)) +} + +pub fn find_crate_name(attrs: &[impl AttributeExt]) -> Option<Symbol> { + first_attr_value_str_by_name(attrs, sym::crate_name) +} + +/// Parse a rustc version number written inside string literal in an attribute, +/// like appears in `since = "1.0.0"`. Suffixes like "-dev" and "-nightly" are +/// not accepted in this position, unlike when parsing CFG_RELEASE. +pub fn parse_version(s: Symbol) -> Option<RustcVersion> { + let mut components = s.as_str().split('-'); + let d = components.next()?; + if components.next().is_some() { + return None; + } + let mut digits = d.splitn(3, '.'); + let major = digits.next()?.parse().ok()?; + let minor = digits.next()?.parse().ok()?; + let patch = digits.next().unwrap_or("0").parse().ok()?; + Some(RustcVersion { major, minor, patch }) +} diff --git a/compiler/rustc_attr/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index bb207c5c952..a1264a6875f 100644 --- a/compiler/rustc_attr/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -12,14 +12,11 @@ #![warn(unreachable_pub)] // tidy-alphabetical-end -mod builtin; +mod attributes; mod session_diagnostics; -pub use IntType::*; -pub use ReprAttr::*; -pub use StabilityLevel::*; -pub use builtin::*; -pub use rustc_ast::attr::*; -pub(crate) use rustc_session::HashStableContext; +pub use attributes::*; +pub use rustc_attr_data_structures::*; +pub use util::{find_crate_name, is_builtin_attr, parse_version}; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_attr/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 9d08a9f5754..b1d8ec49774 100644 --- a/compiler/rustc_attr/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -6,17 +6,18 @@ use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuar use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; -use crate::{UnsupportedLiteralReason, fluent_generated as fluent}; +use crate::attributes::util::UnsupportedLiteralReason; +use crate::fluent_generated as fluent; #[derive(Diagnostic)] -#[diag(attr_expected_one_cfg_pattern, code = E0536)] +#[diag(attr_parsing_expected_one_cfg_pattern, code = E0536)] pub(crate) struct ExpectedOneCfgPattern { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_invalid_predicate, code = E0537)] +#[diag(attr_parsing_invalid_predicate, code = E0537)] pub(crate) struct InvalidPredicate { #[primary_span] pub span: Span, @@ -25,7 +26,7 @@ pub(crate) struct InvalidPredicate { } #[derive(Diagnostic)] -#[diag(attr_multiple_item, code = E0538)] +#[diag(attr_parsing_multiple_item, code = E0538)] pub(crate) struct MultipleItem { #[primary_span] pub span: Span, @@ -34,7 +35,7 @@ pub(crate) struct MultipleItem { } #[derive(Diagnostic)] -#[diag(attr_incorrect_meta_item, code = E0539)] +#[diag(attr_parsing_incorrect_meta_item, code = E0539)] pub(crate) struct IncorrectMetaItem { #[primary_span] pub span: Span, @@ -51,38 +52,38 @@ pub(crate) struct UnknownMetaItem<'a> { impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnknownMetaItem<'_> { fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { let expected = self.expected.iter().map(|name| format!("`{name}`")).collect::<Vec<_>>(); - Diag::new(dcx, level, fluent::attr_unknown_meta_item) + Diag::new(dcx, level, fluent::attr_parsing_unknown_meta_item) .with_span(self.span) .with_code(E0541) .with_arg("item", self.item) .with_arg("expected", expected.join(", ")) - .with_span_label(self.span, fluent::attr_label) + .with_span_label(self.span, fluent::attr_parsing_label) } } #[derive(Diagnostic)] -#[diag(attr_missing_since, code = E0542)] +#[diag(attr_parsing_missing_since, code = E0542)] pub(crate) struct MissingSince { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_missing_note, code = E0543)] +#[diag(attr_parsing_missing_note, code = E0543)] pub(crate) struct MissingNote { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_multiple_stability_levels, code = E0544)] +#[diag(attr_parsing_multiple_stability_levels, code = E0544)] pub(crate) struct MultipleStabilityLevels { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_invalid_issue_string, code = E0545)] +#[diag(attr_parsing_invalid_issue_string, code = E0545)] pub(crate) struct InvalidIssueString { #[primary_span] pub span: Span, @@ -95,31 +96,31 @@ pub(crate) struct InvalidIssueString { // translatable. #[derive(Subdiagnostic)] pub(crate) enum InvalidIssueStringCause { - #[label(attr_must_not_be_zero)] + #[label(attr_parsing_must_not_be_zero)] MustNotBeZero { #[primary_span] span: Span, }, - #[label(attr_empty)] + #[label(attr_parsing_empty)] Empty { #[primary_span] span: Span, }, - #[label(attr_invalid_digit)] + #[label(attr_parsing_invalid_digit)] InvalidDigit { #[primary_span] span: Span, }, - #[label(attr_pos_overflow)] + #[label(attr_parsing_pos_overflow)] PosOverflow { #[primary_span] span: Span, }, - #[label(attr_neg_overflow)] + #[label(attr_parsing_neg_overflow)] NegOverflow { #[primary_span] span: Span, @@ -140,21 +141,21 @@ impl InvalidIssueStringCause { } #[derive(Diagnostic)] -#[diag(attr_missing_feature, code = E0546)] +#[diag(attr_parsing_missing_feature, code = E0546)] pub(crate) struct MissingFeature { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_non_ident_feature, code = E0546)] +#[diag(attr_parsing_non_ident_feature, code = E0546)] pub(crate) struct NonIdentFeature { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_missing_issue, code = E0547)] +#[diag(attr_parsing_missing_issue, code = E0547)] pub(crate) struct MissingIssue { #[primary_span] pub span: Span, @@ -163,20 +164,20 @@ pub(crate) struct MissingIssue { // FIXME: Why is this the same error code as `InvalidReprHintNoParen` and `InvalidReprHintNoValue`? // It is more similar to `IncorrectReprFormatGeneric`. #[derive(Diagnostic)] -#[diag(attr_incorrect_repr_format_packed_one_or_zero_arg, code = E0552)] +#[diag(attr_parsing_incorrect_repr_format_packed_one_or_zero_arg, code = E0552)] pub(crate) struct IncorrectReprFormatPackedOneOrZeroArg { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_incorrect_repr_format_packed_expect_integer, code = E0552)] +#[diag(attr_parsing_incorrect_repr_format_packed_expect_integer, code = E0552)] pub(crate) struct IncorrectReprFormatPackedExpectInteger { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_invalid_repr_hint_no_paren, code = E0552)] +#[diag(attr_parsing_invalid_repr_hint_no_paren, code = E0552)] pub(crate) struct InvalidReprHintNoParen { #[primary_span] pub span: Span, @@ -185,7 +186,7 @@ pub(crate) struct InvalidReprHintNoParen { } #[derive(Diagnostic)] -#[diag(attr_invalid_repr_hint_no_value, code = E0552)] +#[diag(attr_parsing_invalid_repr_hint_no_value, code = E0552)] pub(crate) struct InvalidReprHintNoValue { #[primary_span] pub span: Span, @@ -204,14 +205,18 @@ pub(crate) struct UnsupportedLiteral { impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnsupportedLiteral { fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { let mut diag = Diag::new(dcx, level, match self.reason { - UnsupportedLiteralReason::Generic => fluent::attr_unsupported_literal_generic, - UnsupportedLiteralReason::CfgString => fluent::attr_unsupported_literal_cfg_string, - UnsupportedLiteralReason::CfgBoolean => fluent::attr_unsupported_literal_cfg_boolean, + UnsupportedLiteralReason::Generic => fluent::attr_parsing_unsupported_literal_generic, + UnsupportedLiteralReason::CfgString => { + fluent::attr_parsing_unsupported_literal_cfg_string + } + UnsupportedLiteralReason::CfgBoolean => { + fluent::attr_parsing_unsupported_literal_cfg_boolean + } UnsupportedLiteralReason::DeprecatedString => { - fluent::attr_unsupported_literal_deprecated_string + fluent::attr_parsing_unsupported_literal_deprecated_string } UnsupportedLiteralReason::DeprecatedKvPair => { - fluent::attr_unsupported_literal_deprecated_kv_pair + fluent::attr_parsing_unsupported_literal_deprecated_kv_pair } }); diag.span(self.span); @@ -219,7 +224,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnsupportedLiteral { if self.is_bytestr { diag.span_suggestion( self.start_point_span, - fluent::attr_unsupported_literal_suggestion, + fluent::attr_parsing_unsupported_literal_suggestion, "", Applicability::MaybeIncorrect, ); @@ -229,7 +234,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnsupportedLiteral { } #[derive(Diagnostic)] -#[diag(attr_invalid_repr_align_need_arg, code = E0589)] +#[diag(attr_parsing_invalid_repr_align_need_arg, code = E0589)] pub(crate) struct InvalidReprAlignNeedArg { #[primary_span] #[suggestion(code = "align(...)", applicability = "has-placeholders")] @@ -237,7 +242,7 @@ pub(crate) struct InvalidReprAlignNeedArg { } #[derive(Diagnostic)] -#[diag(attr_invalid_repr_generic, code = E0589)] +#[diag(attr_parsing_invalid_repr_generic, code = E0589)] pub(crate) struct InvalidReprGeneric<'a> { #[primary_span] pub span: Span, @@ -247,21 +252,21 @@ pub(crate) struct InvalidReprGeneric<'a> { } #[derive(Diagnostic)] -#[diag(attr_incorrect_repr_format_align_one_arg, code = E0693)] +#[diag(attr_parsing_incorrect_repr_format_align_one_arg, code = E0693)] pub(crate) struct IncorrectReprFormatAlignOneArg { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_incorrect_repr_format_expect_literal_integer, code = E0693)] +#[diag(attr_parsing_incorrect_repr_format_expect_literal_integer, code = E0693)] pub(crate) struct IncorrectReprFormatExpectInteger { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_incorrect_repr_format_generic, code = E0693)] +#[diag(attr_parsing_incorrect_repr_format_generic, code = E0693)] pub(crate) struct IncorrectReprFormatGeneric<'a> { #[primary_span] pub span: Span, @@ -274,7 +279,11 @@ pub(crate) struct IncorrectReprFormatGeneric<'a> { #[derive(Subdiagnostic)] pub(crate) enum IncorrectReprFormatGenericCause<'a> { - #[suggestion(attr_suggestion, code = "{name}({int})", applicability = "machine-applicable")] + #[suggestion( + attr_parsing_suggestion, + code = "{name}({int})", + applicability = "machine-applicable" + )] Int { #[primary_span] span: Span, @@ -286,7 +295,11 @@ pub(crate) enum IncorrectReprFormatGenericCause<'a> { int: u128, }, - #[suggestion(attr_suggestion, code = "{name}({symbol})", applicability = "machine-applicable")] + #[suggestion( + attr_parsing_suggestion, + code = "{name}({symbol})", + applicability = "machine-applicable" + )] Symbol { #[primary_span] span: Span, @@ -312,35 +325,35 @@ impl<'a> IncorrectReprFormatGenericCause<'a> { } #[derive(Diagnostic)] -#[diag(attr_rustc_promotable_pairing, code = E0717)] +#[diag(attr_parsing_rustc_promotable_pairing, code = E0717)] pub(crate) struct RustcPromotablePairing { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_rustc_const_stable_indirect_pairing)] +#[diag(attr_parsing_rustc_const_stable_indirect_pairing)] pub(crate) struct RustcConstStableIndirectPairing { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_rustc_allowed_unstable_pairing, code = E0789)] +#[diag(attr_parsing_rustc_allowed_unstable_pairing, code = E0789)] pub(crate) struct RustcAllowedUnstablePairing { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_cfg_predicate_identifier)] +#[diag(attr_parsing_cfg_predicate_identifier)] pub(crate) struct CfgPredicateIdentifier { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_deprecated_item_suggestion)] +#[diag(attr_parsing_deprecated_item_suggestion)] pub(crate) struct DeprecatedItemSuggestion { #[primary_span] pub span: Span, @@ -353,21 +366,21 @@ pub(crate) struct DeprecatedItemSuggestion { } #[derive(Diagnostic)] -#[diag(attr_expected_single_version_literal)] +#[diag(attr_parsing_expected_single_version_literal)] pub(crate) struct ExpectedSingleVersionLiteral { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_expected_version_literal)] +#[diag(attr_parsing_expected_version_literal)] pub(crate) struct ExpectedVersionLiteral { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_expects_feature_list)] +#[diag(attr_parsing_expects_feature_list)] pub(crate) struct ExpectsFeatureList { #[primary_span] pub span: Span, @@ -376,7 +389,7 @@ pub(crate) struct ExpectsFeatureList { } #[derive(Diagnostic)] -#[diag(attr_expects_features)] +#[diag(attr_parsing_expects_features)] pub(crate) struct ExpectsFeatures { #[primary_span] pub span: Span, @@ -385,21 +398,21 @@ pub(crate) struct ExpectsFeatures { } #[derive(Diagnostic)] -#[diag(attr_invalid_since)] +#[diag(attr_parsing_invalid_since)] pub(crate) struct InvalidSince { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_soft_no_args)] +#[diag(attr_parsing_soft_no_args)] pub(crate) struct SoftNoArgs { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_unknown_version_literal)] +#[diag(attr_parsing_unknown_version_literal)] pub(crate) struct UnknownVersionLiteral { #[primary_span] pub span: Span, diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index dc4eab766c9..abe4d4f20ec 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -12,7 +12,7 @@ use rustc_mir_dataflow::impls::{ EverInitializedPlaces, EverInitializedPlacesDomain, MaybeUninitializedPlaces, MaybeUninitializedPlacesDomain, }; -use rustc_mir_dataflow::{Analysis, GenKill, JoinSemiLattice, SwitchIntEdgeEffects}; +use rustc_mir_dataflow::{Analysis, GenKill, JoinSemiLattice}; use tracing::debug; use crate::{BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext, places_conflict}; @@ -101,16 +101,6 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> { // This is only reachable from `iterate_to_fixpoint`, which this analysis doesn't use. unreachable!(); } - - fn apply_switch_int_edge_effects( - &mut self, - _block: BasicBlock, - _discr: &mir::Operand<'tcx>, - _apply_edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>, - ) { - // This is only reachable from `iterate_to_fixpoint`, which this analysis doesn't use. - unreachable!(); - } } impl JoinSemiLattice for BorrowckDomain { diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 8dcc5324fdf..8e5944d6cf4 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -34,8 +34,7 @@ use rustc_middle::util::CallKind; use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex}; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::hygiene::DesugaringKind; -use rustc_span::symbol::{Ident, kw, sym}; -use rustc_span::{BytePos, Span, Symbol}; +use rustc_span::{BytePos, Ident, Span, Symbol, kw, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::error_reporting::traits::FindExprBySpan; use rustc_trait_selection::infer::InferCtxtExt; diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 45a8ef0adb6..22f7f708419 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -17,8 +17,7 @@ use rustc_middle::mir::{ }; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt}; -use rustc_span::symbol::{Symbol, kw}; -use rustc_span::{DesugaringKind, Span, sym}; +use rustc_span::{DesugaringKind, Span, Symbol, kw, sym}; use rustc_trait_selection::error_reporting::traits::FindExprBySpan; use tracing::{debug, instrument}; diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 92afad62aa4..67be6ecdeb3 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -20,8 +20,7 @@ use rustc_middle::util::{CallDesugaringKind, call_kind}; use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult}; use rustc_span::def_id::LocalDefId; use rustc_span::source_map::Spanned; -use rustc_span::symbol::sym; -use rustc_span::{DUMMY_SP, Span, Symbol}; +use rustc_span::{DUMMY_SP, Span, Symbol, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{ diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 4fb7b22f289..2484f817a06 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -15,8 +15,7 @@ use rustc_middle::mir::{ PlaceRef, ProjectionElem, }; use rustc_middle::ty::{self, InstanceKind, Ty, TyCtxt, Upcast}; -use rustc_span::symbol::{Symbol, kw}; -use rustc_span::{BytePos, DesugaringKind, Span, sym}; +use rustc_span::{BytePos, DesugaringKind, Span, Symbol, kw, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits; @@ -1474,16 +1473,27 @@ fn suggest_ampmut<'tcx>( // let x: &i32 = &'a 5; // ^^ lifetime annotation not allowed // - if let Some(assignment_rhs_span) = opt_assignment_rhs_span - && let Ok(src) = tcx.sess.source_map().span_to_snippet(assignment_rhs_span) - && let Some(stripped) = src.strip_prefix('&') + if let Some(rhs_span) = opt_assignment_rhs_span + && let Ok(rhs_str) = tcx.sess.source_map().span_to_snippet(rhs_span) + && let Some(rhs_str_no_amp) = rhs_str.strip_prefix('&') { - let is_raw_ref = stripped.trim_start().starts_with("raw "); - // We don't support raw refs yet - if is_raw_ref { - return None; + // Suggest changing `&raw const` to `&raw mut` if applicable. + if rhs_str_no_amp.trim_start().strip_prefix("raw const").is_some() { + let const_idx = rhs_str.find("const").unwrap() as u32; + let const_span = rhs_span + .with_lo(rhs_span.lo() + BytePos(const_idx)) + .with_hi(rhs_span.lo() + BytePos(const_idx + "const".len() as u32)); + + return Some(AmpMutSugg { + has_sugg: true, + span: const_span, + suggestion: "mut".to_owned(), + additional: None, + }); } - let is_mut = if let Some(rest) = stripped.trim_start().strip_prefix("mut") { + + // Figure out if rhs already is `&mut`. + let is_mut = if let Some(rest) = rhs_str_no_amp.trim_start().strip_prefix("mut") { match rest.chars().next() { // e.g. `&mut x` Some(c) if c.is_whitespace() => true, @@ -1500,9 +1510,8 @@ fn suggest_ampmut<'tcx>( // if the reference is already mutable then there is nothing we can do // here. if !is_mut { - let span = assignment_rhs_span; // shrink the span to just after the `&` in `&variable` - let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo(); + let span = rhs_span.with_lo(rhs_span.lo() + BytePos(1)).shrink_to_lo(); // FIXME(Ezrashaw): returning is bad because we still might want to // update the annotated type, see #106857. diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 6ea5c44dc01..3555009c63f 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -15,8 +15,7 @@ use rustc_middle::bug; use rustc_middle::hir::place::PlaceBase; use rustc_middle::mir::{ConstraintCategory, ReturnConstraint}; use rustc_middle::ty::{self, GenericArgs, Region, RegionVid, Ty, TyCtxt, TypeVisitor}; -use rustc_span::Span; -use rustc_span::symbol::{Ident, kw}; +use rustc_span::{Ident, Span, kw}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::error_reporting::infer::nice_region_error::{ self, HirTraitObjectVisitor, NiceRegionError, TraitObjectVisitor, find_anon_type, @@ -189,7 +188,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { /// Returns `true` if a closure is inferred to be an `FnMut` closure. fn is_closure_fn_mut(&self, fr: RegionVid) -> bool { if let Some(ty::ReLateParam(late_param)) = self.to_error_region(fr).as_deref() - && let ty::BoundRegionKind::ClosureEnv = late_param.bound_region + && let ty::LateParamRegionKind::ClosureEnv = late_param.kind && let DefiningTy::Closure(_, args) = self.regioncx.universal_regions().defining_ty { return args.as_closure().kind() == ty::ClosureKind::FnMut; @@ -848,7 +847,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { return; }; - let fn_returns = self.infcx.tcx.return_type_impl_or_dyn_traits(suitable_region.def_id); + let fn_returns = self.infcx.tcx.return_type_impl_or_dyn_traits(suitable_region.scope); let param = if let Some(param) = find_param_with_region(self.infcx.tcx, self.mir_def_id(), f, outlived_f) @@ -875,7 +874,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { Some(arg), captures, Some((param.param_ty_span, param.param_ty.to_string())), - Some(suitable_region.def_id), + Some(suitable_region.scope), ); return; } @@ -883,7 +882,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let Some((alias_tys, alias_span, lt_addition_span)) = self .infcx .tcx - .return_type_impl_or_dyn_traits_with_type_alias(suitable_region.def_id) + .return_type_impl_or_dyn_traits_with_type_alias(suitable_region.scope) else { return; }; @@ -1018,18 +1017,20 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { return; }; - let Some((ty_sub, _)) = - self.infcx.tcx.is_suitable_region(self.mir_def_id(), sub).and_then(|anon_reg| { - find_anon_type(self.infcx.tcx, self.mir_def_id(), sub, &anon_reg.bound_region) - }) + let Some((ty_sub, _)) = self + .infcx + .tcx + .is_suitable_region(self.mir_def_id(), sub) + .and_then(|_| find_anon_type(self.infcx.tcx, self.mir_def_id(), sub)) else { return; }; - let Some((ty_sup, _)) = - self.infcx.tcx.is_suitable_region(self.mir_def_id(), sup).and_then(|anon_reg| { - find_anon_type(self.infcx.tcx, self.mir_def_id(), sup, &anon_reg.bound_region) - }) + let Some((ty_sup, _)) = self + .infcx + .tcx + .is_suitable_region(self.mir_def_id(), sup) + .and_then(|_| find_anon_type(self.infcx.tcx, self.mir_def_id(), sup)) else { return; }; diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index d49dee85144..bdb880b2bce 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -11,8 +11,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_middle::ty::print::RegionHighlightMode; use rustc_middle::ty::{self, GenericArgKind, GenericArgsRef, RegionVid, Ty}; use rustc_middle::{bug, span_bug}; -use rustc_span::symbol::{Symbol, kw, sym}; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Span, Symbol, kw, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use tracing::{debug, instrument}; @@ -300,17 +299,17 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { Some(RegionName { name: kw::StaticLifetime, source: RegionNameSource::Static }) } - ty::ReLateParam(late_param) => match late_param.bound_region { - ty::BoundRegionKind::Named(region_def_id, name) => { + ty::ReLateParam(late_param) => match late_param.kind { + ty::LateParamRegionKind::Named(region_def_id, name) => { // Get the span to point to, even if we don't use the name. let span = tcx.hir().span_if_local(region_def_id).unwrap_or(DUMMY_SP); debug!( "bound region named: {:?}, is_named: {:?}", name, - late_param.bound_region.is_named() + late_param.kind.is_named() ); - if late_param.bound_region.is_named() { + if late_param.kind.is_named() { // A named region that is actually named. Some(RegionName { name, @@ -332,7 +331,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { } } - ty::BoundRegionKind::ClosureEnv => { + ty::LateParamRegionKind::ClosureEnv => { let def_ty = self.regioncx.universal_regions().defining_ty; let closure_kind = match def_ty { @@ -369,7 +368,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { }) } - ty::BoundRegionKind::Anon => None, + ty::LateParamRegionKind::Anon(_) => None, }, ty::ReBound(..) @@ -459,11 +458,8 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { ) -> RegionNameHighlight { let mut highlight = RegionHighlightMode::default(); highlight.highlighting_region_vid(self.infcx.tcx, needle_fr, counter); - let type_name = self - .infcx - .err_ctxt() - .extract_inference_diagnostics_data(ty.into(), Some(highlight)) - .name; + let type_name = + self.infcx.err_ctxt().extract_inference_diagnostics_data(ty.into(), highlight).name; debug!( "highlight_if_we_cannot_match_hir_ty: type_name={:?} needle_fr={:?}", @@ -874,7 +870,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { let type_name = self .infcx .err_ctxt() - .extract_inference_diagnostics_data(yield_ty.into(), Some(highlight)) + .extract_inference_diagnostics_data(yield_ty.into(), highlight) .name; let yield_span = match tcx.hir_node(self.mir_hir_id()) { diff --git a/compiler/rustc_borrowck/src/diagnostics/var_name.rs b/compiler/rustc_borrowck/src/diagnostics/var_name.rs index bda70241267..191c0444c74 100644 --- a/compiler/rustc_borrowck/src/diagnostics/var_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/var_name.rs @@ -1,8 +1,7 @@ use rustc_index::IndexSlice; use rustc_middle::mir::{Body, Local}; use rustc_middle::ty::{self, RegionVid, TyCtxt}; -use rustc_span::Span; -use rustc_span::symbol::Symbol; +use rustc_span::{Span, Symbol}; use tracing::debug; use crate::region_infer::RegionInferenceContext; diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 18b7984e90d..8ee1b1d0ae6 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -141,6 +141,9 @@ fn do_mir_borrowck<'tcx>( ) -> (BorrowCheckResult<'tcx>, Option<Box<BodyWithBorrowckFacts<'tcx>>>) { let def = input_body.source.def_id().expect_local(); let infcx = BorrowckInferCtxt::new(tcx, def); + if let Some(e) = input_body.tainted_by_errors { + infcx.set_tainted_by_errors(e); + } let mut local_names = IndexVec::from_elem(None, &input_body.local_decls); for var_debug_info in &input_body.var_debug_info { @@ -162,13 +165,6 @@ fn do_mir_borrowck<'tcx>( } } - let diags = &mut diags::BorrowckDiags::new(); - - // Gather the upvars of a closure, if any. - if let Some(e) = input_body.tainted_by_errors { - infcx.set_tainted_by_errors(e); - } - // Replace all regions with fresh inference variables. This // requires first making our own copy of the MIR. This copy will // be modified (in place) to contain non-lexical lifetimes. It @@ -206,6 +202,7 @@ fn do_mir_borrowck<'tcx>( polonius_output, opt_closure_req, nll_errors, + localized_outlives_constraints, } = nll::compute_regions( &infcx, free_regions, @@ -224,6 +221,7 @@ fn do_mir_borrowck<'tcx>( // We also have a `#[rustc_regions]` annotation that causes us to dump // information. + let diags = &mut diags::BorrowckDiags::new(); nll::dump_annotation(&infcx, body, ®ioncx, &opt_closure_req, &opaque_type_values, diags); let movable_coroutine = @@ -318,6 +316,16 @@ fn do_mir_borrowck<'tcx>( mbcx.report_move_errors(); + // If requested, dump polonius MIR. + polonius::dump_polonius_mir( + &infcx, + body, + ®ioncx, + &borrow_set, + localized_outlives_constraints, + &opt_closure_req, + ); + // For each non-user used mutable variable, check if it's been assigned from // a user-declared local. If so, then put that local into the used_mut set. // Note that this set is expected to be small - only upvars from closures @@ -337,35 +345,7 @@ fn do_mir_borrowck<'tcx>( mbcx.gather_used_muts(temporary_used_locals, unused_mut_locals); debug!("mbcx.used_mut: {:?}", mbcx.used_mut); - let used_mut = std::mem::take(&mut mbcx.used_mut); - for local in mbcx.body.mut_vars_and_args_iter().filter(|local| !used_mut.contains(local)) { - let local_decl = &mbcx.body.local_decls[local]; - let lint_root = match &mbcx.body.source_scopes[local_decl.source_info.scope].local_data { - ClearCrossCrate::Set(data) => data.lint_root, - _ => continue, - }; - - // Skip over locals that begin with an underscore or have no name - match mbcx.local_names[local] { - Some(name) => { - if name.as_str().starts_with('_') { - continue; - } - } - None => continue, - } - - let span = local_decl.source_info.span; - if span.desugaring_kind().is_some() { - // If the `mut` arises as part of a desugaring, we should ignore it. - continue; - } - - let mut_span = tcx.sess.source_map().span_until_non_whitespace(span); - - tcx.emit_node_span_lint(UNUSED_MUT, lint_root, span, VarNeedNotMut { span: mut_span }) - } - + mbcx.lint_unused_mut(); let tainted_by_errors = mbcx.emit_errors(); let result = BorrowCheckResult { @@ -2393,6 +2373,38 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { // `BasicBlocks` computes dominators on-demand and caches them. self.body.basic_blocks.dominators() } + + fn lint_unused_mut(&self) { + let tcx = self.infcx.tcx; + let body = self.body; + for local in body.mut_vars_and_args_iter().filter(|local| !self.used_mut.contains(local)) { + let local_decl = &body.local_decls[local]; + let lint_root = match &body.source_scopes[local_decl.source_info.scope].local_data { + ClearCrossCrate::Set(data) => data.lint_root, + _ => continue, + }; + + // Skip over locals that begin with an underscore or have no name + match self.local_names[local] { + Some(name) => { + if name.as_str().starts_with('_') { + continue; + } + } + None => continue, + } + + let span = local_decl.source_info.span; + if span.desugaring_kind().is_some() { + // If the `mut` arises as part of a desugaring, we should ignore it. + continue; + } + + let mut_span = tcx.sess.source_map().span_until_non_whitespace(span); + + tcx.emit_node_span_lint(UNUSED_MUT, lint_root, span, VarNeedNotMut { span: mut_span }) + } + } } mod diags { diff --git a/compiler/rustc_borrowck/src/member_constraints.rs b/compiler/rustc_borrowck/src/member_constraints.rs index fc621a3b828..a0adf471fd3 100644 --- a/compiler/rustc_borrowck/src/member_constraints.rs +++ b/compiler/rustc_borrowck/src/member_constraints.rs @@ -4,10 +4,9 @@ use std::ops::Index; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxIndexMap; use rustc_index::{IndexSlice, IndexVec}; -use rustc_middle::infer::MemberConstraint; use rustc_middle::ty::{self, Ty}; use rustc_span::Span; -use tracing::debug; +use tracing::instrument; /// Compactly stores a set of `R0 member of [R1...Rn]` constraints, /// indexed by the region `R0`. @@ -23,7 +22,7 @@ where /// Stores the data about each `R0 member of [R1..Rn]` constraint. /// These are organized into a linked list, so each constraint /// contains the index of the next constraint with the same `R0`. - constraints: IndexVec<NllMemberConstraintIndex, NllMemberConstraint<'tcx>>, + constraints: IndexVec<NllMemberConstraintIndex, MemberConstraint<'tcx>>, /// Stores the `R1..Rn` regions for *all* sets. For any given /// constraint, we keep two indices so that we can pull out a @@ -33,7 +32,7 @@ where /// Represents a `R0 member of [R1..Rn]` constraint #[derive(Debug)] -pub(crate) struct NllMemberConstraint<'tcx> { +pub(crate) struct MemberConstraint<'tcx> { next_constraint: Option<NllMemberConstraintIndex>, /// The span where the hidden type was instantiated. @@ -70,37 +69,34 @@ impl Default for MemberConstraintSet<'_, ty::RegionVid> { } impl<'tcx> MemberConstraintSet<'tcx, ty::RegionVid> { + pub(crate) fn is_empty(&self) -> bool { + self.constraints.is_empty() + } + /// Pushes a member constraint into the set. - /// - /// The input member constraint `m_c` is in the form produced by - /// the `rustc_middle::infer` code. - /// - /// The `to_region_vid` callback fn is used to convert the regions - /// within into `RegionVid` format -- it typically consults the - /// `UniversalRegions` data structure that is known to the caller - /// (but which this code is unaware of). - pub(crate) fn push_constraint( + #[instrument(level = "debug", skip(self))] + pub(crate) fn add_member_constraint( &mut self, - m_c: &MemberConstraint<'tcx>, - mut to_region_vid: impl FnMut(ty::Region<'tcx>) -> ty::RegionVid, + key: ty::OpaqueTypeKey<'tcx>, + hidden_ty: Ty<'tcx>, + definition_span: Span, + member_region_vid: ty::RegionVid, + choice_regions: &[ty::RegionVid], ) { - debug!("push_constraint(m_c={:?})", m_c); - let member_region_vid: ty::RegionVid = to_region_vid(m_c.member_region); let next_constraint = self.first_constraints.get(&member_region_vid).cloned(); let start_index = self.choice_regions.len(); - let end_index = start_index + m_c.choice_regions.len(); - debug!("push_constraint: member_region_vid={:?}", member_region_vid); - let constraint_index = self.constraints.push(NllMemberConstraint { + self.choice_regions.extend(choice_regions); + let end_index = self.choice_regions.len(); + let constraint_index = self.constraints.push(MemberConstraint { next_constraint, member_region_vid, - definition_span: m_c.definition_span, - hidden_ty: m_c.hidden_ty, - key: m_c.key, + definition_span, + hidden_ty, + key, start_index, end_index, }); self.first_constraints.insert(member_region_vid, constraint_index); - self.choice_regions.extend(m_c.choice_regions.iter().map(|&r| to_region_vid(r))); } } @@ -182,7 +178,7 @@ where /// R0 member of [R1..Rn] /// ``` pub(crate) fn choice_regions(&self, pci: NllMemberConstraintIndex) -> &[ty::RegionVid] { - let NllMemberConstraint { start_index, end_index, .. } = &self.constraints[pci]; + let MemberConstraint { start_index, end_index, .. } = &self.constraints[pci]; &self.choice_regions[*start_index..*end_index] } } @@ -191,9 +187,9 @@ impl<'tcx, R> Index<NllMemberConstraintIndex> for MemberConstraintSet<'tcx, R> where R: Copy + Eq, { - type Output = NllMemberConstraint<'tcx>; + type Output = MemberConstraint<'tcx>; - fn index(&self, i: NllMemberConstraintIndex) -> &NllMemberConstraint<'tcx> { + fn index(&self, i: NllMemberConstraintIndex) -> &MemberConstraint<'tcx> { &self.constraints[i] } } @@ -215,7 +211,7 @@ where /// target_list: A -> B -> C -> D -> E -> F -> (None) /// ``` fn append_list( - constraints: &mut IndexSlice<NllMemberConstraintIndex, NllMemberConstraint<'_>>, + constraints: &mut IndexSlice<NllMemberConstraintIndex, MemberConstraint<'_>>, target_list: NllMemberConstraintIndex, source_list: NllMemberConstraintIndex, ) { diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index be02e2f48df..abe27555b18 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -21,7 +21,7 @@ use rustc_mir_dataflow::impls::MaybeInitializedPlaces; use rustc_mir_dataflow::move_paths::MoveData; use rustc_mir_dataflow::points::DenseLocationMap; use rustc_session::config::MirIncludeSpans; -use rustc_span::symbol::sym; +use rustc_span::sym; use tracing::{debug, instrument}; use crate::borrow_set::BorrowSet; @@ -29,6 +29,7 @@ use crate::consumers::ConsumerOptions; use crate::diagnostics::RegionErrors; use crate::facts::{AllFacts, AllFactsExt, RustcFacts}; use crate::location::LocationTable; +use crate::polonius::LocalizedOutlivesConstraintSet; use crate::region_infer::RegionInferenceContext; use crate::type_check::{self, MirTypeckResults}; use crate::universal_regions::UniversalRegions; @@ -45,6 +46,9 @@ pub(crate) struct NllOutput<'tcx> { pub polonius_output: Option<Box<PoloniusOutput>>, pub opt_closure_req: Option<ClosureRegionRequirements<'tcx>>, pub nll_errors: RegionErrors<'tcx>, + + /// When using `-Zpolonius=next`: the localized typeck and liveness constraints. + pub localized_outlives_constraints: Option<LocalizedOutlivesConstraintSet>, } /// Rewrites the regions in the MIR to use NLL variables, also scraping out the set of universal @@ -116,7 +120,7 @@ pub(crate) fn compute_regions<'a, 'tcx>( let var_origins = infcx.get_region_var_origins(); // If requested, emit legacy polonius facts. - polonius::emit_facts( + polonius::legacy::emit_facts( &mut all_facts, infcx.tcx, location_table, @@ -124,6 +128,7 @@ pub(crate) fn compute_regions<'a, 'tcx>( borrow_set, move_data, &universal_region_relations, + &constraints, ); let mut regioncx = RegionInferenceContext::new( @@ -134,6 +139,15 @@ pub(crate) fn compute_regions<'a, 'tcx>( elements, ); + // If requested for `-Zpolonius=next`, convert NLL constraints to localized outlives + // constraints. + let localized_outlives_constraints = + if infcx.tcx.sess.opts.unstable_opts.polonius.is_next_enabled() { + Some(polonius::create_localized_constraints(&mut regioncx, body)) + } else { + None + }; + // If requested: dump NLL facts, and run legacy polonius analysis. let polonius_output = all_facts.as_ref().and_then(|all_facts| { if infcx.tcx.sess.opts.unstable_opts.nll_facts { @@ -174,6 +188,7 @@ pub(crate) fn compute_regions<'a, 'tcx>( polonius_output, opt_closure_req: closure_region_requirements, nll_errors, + localized_outlives_constraints, } } @@ -214,40 +229,7 @@ pub(super) fn dump_nll_mir<'tcx>( &0, body, |pass_where, out| { - match pass_where { - // Before the CFG, dump out the values for each region variable. - PassWhere::BeforeCFG => { - regioncx.dump_mir(tcx, out)?; - writeln!(out, "|")?; - - if let Some(closure_region_requirements) = closure_region_requirements { - writeln!(out, "| Free Region Constraints")?; - for_each_region_constraint(tcx, closure_region_requirements, &mut |msg| { - writeln!(out, "| {msg}") - })?; - writeln!(out, "|")?; - } - - if borrow_set.len() > 0 { - writeln!(out, "| Borrows")?; - for (borrow_idx, borrow_data) in borrow_set.iter_enumerated() { - writeln!( - out, - "| {:?}: issued at {:?} in {:?}", - borrow_idx, borrow_data.reserve_location, borrow_data.region - )?; - } - writeln!(out, "|")?; - } - } - - PassWhere::BeforeLocation(_) => {} - - PassWhere::AfterTerminator(_) => {} - - PassWhere::BeforeBlock(_) | PassWhere::AfterLocation(_) | PassWhere::AfterCFG => {} - } - Ok(()) + emit_nll_mir(tcx, regioncx, closure_region_requirements, borrow_set, pass_where, out) }, options, ); @@ -265,6 +247,51 @@ pub(super) fn dump_nll_mir<'tcx>( }; } +/// Produces the actual NLL MIR sections to emit during the dumping process. +pub(crate) fn emit_nll_mir<'tcx>( + tcx: TyCtxt<'tcx>, + regioncx: &RegionInferenceContext<'tcx>, + closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>, + borrow_set: &BorrowSet<'tcx>, + pass_where: PassWhere, + out: &mut dyn io::Write, +) -> io::Result<()> { + match pass_where { + // Before the CFG, dump out the values for each region variable. + PassWhere::BeforeCFG => { + regioncx.dump_mir(tcx, out)?; + writeln!(out, "|")?; + + if let Some(closure_region_requirements) = closure_region_requirements { + writeln!(out, "| Free Region Constraints")?; + for_each_region_constraint(tcx, closure_region_requirements, &mut |msg| { + writeln!(out, "| {msg}") + })?; + writeln!(out, "|")?; + } + + if borrow_set.len() > 0 { + writeln!(out, "| Borrows")?; + for (borrow_idx, borrow_data) in borrow_set.iter_enumerated() { + writeln!( + out, + "| {:?}: issued at {:?} in {:?}", + borrow_idx, borrow_data.reserve_location, borrow_data.region + )?; + } + writeln!(out, "|")?; + } + } + + PassWhere::BeforeLocation(_) => {} + + PassWhere::AfterTerminator(_) => {} + + PassWhere::BeforeBlock(_) | PassWhere::AfterLocation(_) | PassWhere::AfterCFG => {} + } + Ok(()) +} + #[allow(rustc::diagnostic_outside_of_impl)] #[allow(rustc::untranslatable_diagnostic)] pub(super) fn dump_annotation<'tcx, 'infcx>( diff --git a/compiler/rustc_borrowck/src/polonius/constraints.rs b/compiler/rustc_borrowck/src/polonius/constraints.rs new file mode 100644 index 00000000000..50f59dd0dee --- /dev/null +++ b/compiler/rustc_borrowck/src/polonius/constraints.rs @@ -0,0 +1,45 @@ +use rustc_middle::ty::RegionVid; +use rustc_mir_dataflow::points::PointIndex; + +/// A localized outlives constraint reifies the CFG location where the outlives constraint holds, +/// within the origins themselves as if they were different from point to point: from `a: b` +/// outlives constraints to `a@p: b@p`, where `p` is the point in the CFG. +/// +/// This models two sources of constraints: +/// - constraints that traverse the subsets between regions at a given point, `a@p: b@p`. These +/// depend on typeck constraints generated via assignments, calls, etc. (In practice there are +/// subtleties where a statement's effect only starts being visible at the successor point, via +/// the "result" of that statement). +/// - constraints that traverse the CFG via the same region, `a@p: a@q`, where `p` is a predecessor +/// of `q`. These depend on the liveness of the regions at these points, as well as their +/// variance. +/// +/// The `source` origin at `from` flows into the `target` origin at `to`. +/// +/// This dual of NLL's [crate::constraints::OutlivesConstraint] therefore encodes the +/// position-dependent outlives constraints used by Polonius, to model the flow-sensitive loan +/// propagation via reachability within a graph of localized constraints. +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] +pub(crate) struct LocalizedOutlivesConstraint { + pub source: RegionVid, + pub from: PointIndex, + pub target: RegionVid, + pub to: PointIndex, +} + +/// A container of [LocalizedOutlivesConstraint]s that can be turned into a traversable +/// `rustc_data_structures` graph. +#[derive(Clone, Default, Debug)] +pub(crate) struct LocalizedOutlivesConstraintSet { + pub outlives: Vec<LocalizedOutlivesConstraint>, +} + +impl LocalizedOutlivesConstraintSet { + pub(crate) fn push(&mut self, constraint: LocalizedOutlivesConstraint) { + if constraint.source == constraint.target && constraint.from == constraint.to { + // 'a@p: 'a@p is pretty uninteresting + return; + } + self.outlives.push(constraint); + } +} diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs new file mode 100644 index 00000000000..a6d80149034 --- /dev/null +++ b/compiler/rustc_borrowck/src/polonius/dump.rs @@ -0,0 +1,104 @@ +use std::io; + +use rustc_middle::mir::pretty::{PrettyPrintMirOptions, dump_mir_with_options}; +use rustc_middle::mir::{Body, ClosureRegionRequirements, PassWhere}; +use rustc_middle::ty::TyCtxt; +use rustc_session::config::MirIncludeSpans; + +use crate::borrow_set::BorrowSet; +use crate::polonius::{LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet}; +use crate::{BorrowckInferCtxt, RegionInferenceContext}; + +/// `-Zdump-mir=polonius` dumps MIR annotated with NLL and polonius specific information. +// Note: this currently duplicates most of NLL MIR, with some additions for the localized outlives +// constraints. This is ok for now as this dump will change in the near future to an HTML file to +// become more useful. +pub(crate) fn dump_polonius_mir<'tcx>( + infcx: &BorrowckInferCtxt<'tcx>, + body: &Body<'tcx>, + regioncx: &RegionInferenceContext<'tcx>, + borrow_set: &BorrowSet<'tcx>, + localized_outlives_constraints: Option<LocalizedOutlivesConstraintSet>, + closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>, +) { + let tcx = infcx.tcx; + if !tcx.sess.opts.unstable_opts.polonius.is_next_enabled() { + return; + } + + let localized_outlives_constraints = localized_outlives_constraints + .expect("missing localized constraints with `-Zpolonius=next`"); + + // We want the NLL extra comments printed by default in NLL MIR dumps (they were removed in + // #112346). Specifying `-Z mir-include-spans` on the CLI still has priority: for example, + // they're always disabled in mir-opt tests to make working with blessed dumps easier. + let options = PrettyPrintMirOptions { + include_extra_comments: matches!( + tcx.sess.opts.unstable_opts.mir_include_spans, + MirIncludeSpans::On | MirIncludeSpans::Nll + ), + }; + + dump_mir_with_options( + tcx, + false, + "polonius", + &0, + body, + |pass_where, out| { + emit_polonius_mir( + tcx, + regioncx, + closure_region_requirements, + borrow_set, + &localized_outlives_constraints, + pass_where, + out, + ) + }, + options, + ); +} + +/// Produces the actual NLL + Polonius MIR sections to emit during the dumping process. +fn emit_polonius_mir<'tcx>( + tcx: TyCtxt<'tcx>, + regioncx: &RegionInferenceContext<'tcx>, + closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>, + borrow_set: &BorrowSet<'tcx>, + localized_outlives_constraints: &LocalizedOutlivesConstraintSet, + pass_where: PassWhere, + out: &mut dyn io::Write, +) -> io::Result<()> { + // Emit the regular NLL front-matter + crate::nll::emit_nll_mir( + tcx, + regioncx, + closure_region_requirements, + borrow_set, + pass_where.clone(), + out, + )?; + + let liveness = regioncx.liveness_constraints(); + + // Add localized outlives constraints + match pass_where { + PassWhere::BeforeCFG => { + if localized_outlives_constraints.outlives.len() > 0 { + writeln!(out, "| Localized constraints")?; + + for constraint in &localized_outlives_constraints.outlives { + let LocalizedOutlivesConstraint { source, from, target, to } = constraint; + let from = liveness.location_from_point(*from); + let to = liveness.location_from_point(*to); + writeln!(out, "| {source:?} at {from:?} -> {target:?} at {to:?}")?; + } + writeln!(out, "|")?; + } + } + _ => {} + } + + Ok(()) +} diff --git a/compiler/rustc_borrowck/src/polonius/legacy/accesses.rs b/compiler/rustc_borrowck/src/polonius/legacy/accesses.rs new file mode 100644 index 00000000000..9c4aa8ea1ed --- /dev/null +++ b/compiler/rustc_borrowck/src/polonius/legacy/accesses.rs @@ -0,0 +1,85 @@ +use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor}; +use rustc_middle::mir::{Body, Local, Location, Place}; +use rustc_middle::ty::TyCtxt; +use rustc_mir_dataflow::move_paths::{LookupResult, MoveData}; +use tracing::debug; + +use crate::def_use::{self, DefUse}; +use crate::facts::AllFacts; +use crate::location::{LocationIndex, LocationTable}; +use crate::universal_regions::UniversalRegions; + +/// Emit polonius facts for variable defs, uses, drops, and path accesses. +pub(crate) fn emit_access_facts<'tcx>( + tcx: TyCtxt<'tcx>, + facts: &mut AllFacts, + body: &Body<'tcx>, + location_table: &LocationTable, + move_data: &MoveData<'tcx>, + universal_regions: &UniversalRegions<'tcx>, +) { + let mut extractor = AccessFactsExtractor { facts, move_data, location_table }; + extractor.visit_body(body); + + for (local, local_decl) in body.local_decls.iter_enumerated() { + debug!("add use_of_var_derefs_origin facts - local={:?}, type={:?}", local, local_decl.ty); + tcx.for_each_free_region(&local_decl.ty, |region| { + let region_vid = universal_regions.to_region_vid(region); + facts.use_of_var_derefs_origin.push((local, region_vid.into())); + }); + } +} + +/// MIR visitor extracting point-wise facts about accesses. +struct AccessFactsExtractor<'a, 'tcx> { + facts: &'a mut AllFacts, + move_data: &'a MoveData<'tcx>, + location_table: &'a LocationTable, +} + +impl<'tcx> AccessFactsExtractor<'_, 'tcx> { + fn location_to_index(&self, location: Location) -> LocationIndex { + self.location_table.mid_index(location) + } +} + +impl<'a, 'tcx> Visitor<'tcx> for AccessFactsExtractor<'a, 'tcx> { + fn visit_local(&mut self, local: Local, context: PlaceContext, location: Location) { + match def_use::categorize(context) { + Some(DefUse::Def) => { + debug!("AccessFactsExtractor - emit def"); + self.facts.var_defined_at.push((local, self.location_to_index(location))); + } + Some(DefUse::Use) => { + debug!("AccessFactsExtractor - emit use"); + self.facts.var_used_at.push((local, self.location_to_index(location))); + } + Some(DefUse::Drop) => { + debug!("AccessFactsExtractor - emit drop"); + self.facts.var_dropped_at.push((local, self.location_to_index(location))); + } + _ => (), + } + } + + fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) { + self.super_place(place, context, location); + + match context { + PlaceContext::NonMutatingUse(_) + | PlaceContext::MutatingUse(MutatingUseContext::Borrow) => { + let path = match self.move_data.rev_lookup.find(place.as_ref()) { + LookupResult::Exact(path) | LookupResult::Parent(Some(path)) => path, + _ => { + // There's no path access to emit. + return; + } + }; + debug!("AccessFactsExtractor - emit path access ({path:?}, {location:?})"); + self.facts.path_accessed_at_base.push((path, self.location_to_index(location))); + } + + _ => {} + } + } +} diff --git a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs index f646beeecf7..0d5b6f3a2c8 100644 --- a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs @@ -21,22 +21,22 @@ use crate::{ /// Emit `loan_invalidated_at` facts. pub(super) fn emit_loan_invalidations<'tcx>( tcx: TyCtxt<'tcx>, - all_facts: &mut AllFacts, - location_table: &LocationTable, + facts: &mut AllFacts, body: &Body<'tcx>, + location_table: &LocationTable, borrow_set: &BorrowSet<'tcx>, ) { let dominators = body.basic_blocks.dominators(); let mut visitor = - LoanInvalidationsGenerator { all_facts, borrow_set, tcx, location_table, body, dominators }; + LoanInvalidationsGenerator { facts, borrow_set, tcx, location_table, body, dominators }; visitor.visit_body(body); } struct LoanInvalidationsGenerator<'a, 'tcx> { tcx: TyCtxt<'tcx>, - all_facts: &'a mut AllFacts, - location_table: &'a LocationTable, + facts: &'a mut AllFacts, body: &'a Body<'tcx>, + location_table: &'a LocationTable, dominators: &'a Dominators<BasicBlock>, borrow_set: &'a BorrowSet<'tcx>, } @@ -151,7 +151,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> { let resume = self.location_table.start_index(resume.start_location()); for (i, data) in borrow_set.iter_enumerated() { if borrow_of_local_data(data.borrowed_place) { - self.all_facts.loan_invalidated_at.push((resume, i)); + self.facts.loan_invalidated_at.push((resume, i)); } } @@ -165,7 +165,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> { let start = self.location_table.start_index(location); for (i, data) in borrow_set.iter_enumerated() { if borrow_of_local_data(data.borrowed_place) { - self.all_facts.loan_invalidated_at.push((start, i)); + self.facts.loan_invalidated_at.push((start, i)); } } } @@ -409,7 +409,7 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { /// Generates a new `loan_invalidated_at(L, B)` fact. fn emit_loan_invalidated_at(&mut self, b: BorrowIndex, l: Location) { let lidx = self.location_table.start_index(l); - self.all_facts.loan_invalidated_at.push((lidx, b)); + self.facts.loan_invalidated_at.push((lidx, b)); } fn check_activations(&mut self, location: Location) { diff --git a/compiler/rustc_borrowck/src/polonius/loan_kills.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_kills.rs index 68e0865ab82..fdde9fa0476 100644 --- a/compiler/rustc_borrowck/src/polonius/loan_kills.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_kills.rs @@ -14,12 +14,12 @@ use crate::places_conflict; /// Emit `loan_killed_at` and `cfg_edge` facts at the same time. pub(super) fn emit_loan_kills<'tcx>( tcx: TyCtxt<'tcx>, - all_facts: &mut AllFacts, - location_table: &LocationTable, + facts: &mut AllFacts, body: &Body<'tcx>, + location_table: &LocationTable, borrow_set: &BorrowSet<'tcx>, ) { - let mut visitor = LoanKillsGenerator { borrow_set, tcx, location_table, all_facts, body }; + let mut visitor = LoanKillsGenerator { borrow_set, tcx, location_table, facts, body }; for (bb, data) in body.basic_blocks.iter_enumerated() { visitor.visit_basic_block_data(bb, data); } @@ -27,7 +27,7 @@ pub(super) fn emit_loan_kills<'tcx>( struct LoanKillsGenerator<'a, 'tcx> { tcx: TyCtxt<'tcx>, - all_facts: &'a mut AllFacts, + facts: &'a mut AllFacts, location_table: &'a LocationTable, borrow_set: &'a BorrowSet<'tcx>, body: &'a Body<'tcx>, @@ -36,12 +36,12 @@ struct LoanKillsGenerator<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for LoanKillsGenerator<'a, 'tcx> { fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { // Also record CFG facts here. - self.all_facts.cfg_edge.push(( + self.facts.cfg_edge.push(( self.location_table.start_index(location), self.location_table.mid_index(location), )); - self.all_facts.cfg_edge.push(( + self.facts.cfg_edge.push(( self.location_table.mid_index(location), self.location_table.start_index(location.successor_within_block()), )); @@ -63,15 +63,15 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanKillsGenerator<'a, 'tcx> { fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { // Also record CFG facts here. - self.all_facts.cfg_edge.push(( + self.facts.cfg_edge.push(( self.location_table.start_index(location), self.location_table.mid_index(location), )); let successor_blocks = terminator.successors(); - self.all_facts.cfg_edge.reserve(successor_blocks.size_hint().0); + self.facts.cfg_edge.reserve(successor_blocks.size_hint().0); for successor_block in successor_blocks { - self.all_facts.cfg_edge.push(( + self.facts.cfg_edge.push(( self.location_table.mid_index(location), self.location_table.start_index(successor_block.start_location()), )); @@ -128,7 +128,7 @@ impl<'tcx> LoanKillsGenerator<'_, 'tcx> { if places_conflict { let location_index = self.location_table.mid_index(location); - self.all_facts.loan_killed_at.push((borrow_index, location_index)); + self.facts.loan_killed_at.push((borrow_index, location_index)); } } } @@ -140,9 +140,9 @@ impl<'tcx> LoanKillsGenerator<'_, 'tcx> { fn record_killed_borrows_for_local(&mut self, local: Local, location: Location) { if let Some(borrow_indices) = self.borrow_set.local_map.get(&local) { let location_index = self.location_table.mid_index(location); - self.all_facts.loan_killed_at.reserve(borrow_indices.len()); + self.facts.loan_killed_at.reserve(borrow_indices.len()); for &borrow_index in borrow_indices { - self.all_facts.loan_killed_at.push((borrow_index, location_index)); + self.facts.loan_killed_at.push((borrow_index, location_index)); } } } diff --git a/compiler/rustc_borrowck/src/polonius/legacy/mod.rs b/compiler/rustc_borrowck/src/polonius/legacy/mod.rs new file mode 100644 index 00000000000..60fd2afe63e --- /dev/null +++ b/compiler/rustc_borrowck/src/polonius/legacy/mod.rs @@ -0,0 +1,223 @@ +//! Functions dedicated to fact generation for the `-Zpolonius=legacy` datalog implementation. +//! +//! Will be removed in the future, once the in-tree `-Zpolonius=next` implementation reaches feature +//! parity. + +use std::iter; + +use either::Either; +use rustc_middle::mir::{Body, Local, LocalKind, Location, START_BLOCK}; +use rustc_middle::ty::{GenericArg, TyCtxt}; +use rustc_mir_dataflow::move_paths::{InitKind, InitLocation, MoveData}; +use tracing::debug; + +use crate::borrow_set::BorrowSet; +use crate::constraints::OutlivesConstraint; +use crate::facts::{AllFacts, PoloniusRegionVid}; +use crate::location::LocationTable; +use crate::type_check::MirTypeckRegionConstraints; +use crate::type_check::free_region_relations::UniversalRegionRelations; +use crate::universal_regions::UniversalRegions; + +mod accesses; +mod loan_invalidations; +mod loan_kills; + +/// When requested, emit most of the facts needed by polonius: +/// - moves and assignments +/// - universal regions and their relations +/// - CFG points and edges +/// - loan kills +/// - loan invalidations +/// - access facts such as variable definitions, uses, drops, and path accesses +/// - outlives constraints +/// +/// The rest of the facts are emitted during typeck and liveness. +pub(crate) fn emit_facts<'tcx>( + all_facts: &mut Option<AllFacts>, + tcx: TyCtxt<'tcx>, + location_table: &LocationTable, + body: &Body<'tcx>, + borrow_set: &BorrowSet<'tcx>, + move_data: &MoveData<'tcx>, + universal_region_relations: &UniversalRegionRelations<'tcx>, + constraints: &MirTypeckRegionConstraints<'tcx>, +) { + let Some(facts) = all_facts else { + // We don't do anything if there are no facts to fill. + return; + }; + let _prof_timer = tcx.prof.generic_activity("polonius_fact_generation"); + emit_move_facts(facts, body, location_table, move_data); + emit_universal_region_facts(facts, borrow_set, universal_region_relations); + loan_kills::emit_loan_kills(tcx, facts, body, location_table, borrow_set); + loan_invalidations::emit_loan_invalidations(tcx, facts, body, location_table, borrow_set); + accesses::emit_access_facts( + tcx, + facts, + body, + location_table, + move_data, + &universal_region_relations.universal_regions, + ); + emit_outlives_facts(facts, location_table, constraints); +} + +/// Emit facts needed for move/init analysis: moves and assignments. +fn emit_move_facts( + facts: &mut AllFacts, + body: &Body<'_>, + location_table: &LocationTable, + move_data: &MoveData<'_>, +) { + facts.path_is_var.extend(move_data.rev_lookup.iter_locals_enumerated().map(|(l, r)| (r, l))); + + for (child, move_path) in move_data.move_paths.iter_enumerated() { + if let Some(parent) = move_path.parent { + facts.child_path.push((child, parent)); + } + } + + let fn_entry_start = + location_table.start_index(Location { block: START_BLOCK, statement_index: 0 }); + + // initialized_at + for init in move_data.inits.iter() { + match init.location { + InitLocation::Statement(location) => { + let block_data = &body[location.block]; + let is_terminator = location.statement_index == block_data.statements.len(); + + if is_terminator && init.kind == InitKind::NonPanicPathOnly { + // We are at the terminator of an init that has a panic path, + // and where the init should not happen on panic + + for successor in block_data.terminator().successors() { + if body[successor].is_cleanup { + continue; + } + + // The initialization happened in (or rather, when arriving at) + // the successors, but not in the unwind block. + let first_statement = Location { block: successor, statement_index: 0 }; + facts + .path_assigned_at_base + .push((init.path, location_table.start_index(first_statement))); + } + } else { + // In all other cases, the initialization just happens at the + // midpoint, like any other effect. + facts + .path_assigned_at_base + .push((init.path, location_table.mid_index(location))); + } + } + // Arguments are initialized on function entry + InitLocation::Argument(local) => { + assert!(body.local_kind(local) == LocalKind::Arg); + facts.path_assigned_at_base.push((init.path, fn_entry_start)); + } + } + } + + for (local, path) in move_data.rev_lookup.iter_locals_enumerated() { + if body.local_kind(local) != LocalKind::Arg { + // Non-arguments start out deinitialised; we simulate this with an + // initial move: + facts.path_moved_at_base.push((path, fn_entry_start)); + } + } + + // moved_out_at + // deinitialisation is assumed to always happen! + facts + .path_moved_at_base + .extend(move_data.moves.iter().map(|mo| (mo.path, location_table.mid_index(mo.source)))); +} + +/// Emit universal regions facts, and their relations. +fn emit_universal_region_facts( + facts: &mut AllFacts, + borrow_set: &BorrowSet<'_>, + universal_region_relations: &UniversalRegionRelations<'_>, +) { + // 1: universal regions are modeled in Polonius as a pair: + // - the universal region vid itself. + // - a "placeholder loan" associated to this universal region. Since they don't exist in + // the `borrow_set`, their `BorrowIndex` are synthesized as the universal region index + // added to the existing number of loans, as if they succeeded them in the set. + // + let universal_regions = &universal_region_relations.universal_regions; + facts + .universal_region + .extend(universal_regions.universal_regions_iter().map(PoloniusRegionVid::from)); + let borrow_count = borrow_set.len(); + debug!( + "emit_universal_region_facts: polonius placeholders, num_universals={}, borrow_count={}", + universal_regions.len(), + borrow_count + ); + + for universal_region in universal_regions.universal_regions_iter() { + let universal_region_idx = universal_region.index(); + let placeholder_loan_idx = borrow_count + universal_region_idx; + facts.placeholder.push((universal_region.into(), placeholder_loan_idx.into())); + } + + // 2: the universal region relations `outlives` constraints are emitted as + // `known_placeholder_subset` facts. + for (fr1, fr2) in universal_region_relations.known_outlives() { + if fr1 != fr2 { + debug!( + "emit_universal_region_facts: emitting polonius `known_placeholder_subset` \ + fr1={:?}, fr2={:?}", + fr1, fr2 + ); + facts.known_placeholder_subset.push((fr1.into(), fr2.into())); + } + } +} + +/// For every potentially drop()-touched region `region` in `local`'s type +/// (`kind`), emit a `drop_of_var_derefs_origin(local, origin)` fact. +pub(crate) fn emit_drop_facts<'tcx>( + tcx: TyCtxt<'tcx>, + local: Local, + kind: &GenericArg<'tcx>, + universal_regions: &UniversalRegions<'tcx>, + all_facts: &mut Option<AllFacts>, +) { + debug!("emit_drop_facts(local={:?}, kind={:?}", local, kind); + let Some(facts) = all_facts.as_mut() else { return }; + let _prof_timer = tcx.prof.generic_activity("polonius_fact_generation"); + tcx.for_each_free_region(kind, |drop_live_region| { + let region_vid = universal_regions.to_region_vid(drop_live_region); + facts.drop_of_var_derefs_origin.push((local, region_vid.into())); + }); +} + +/// Emit facts about the outlives constraints: the `subset` base relation, i.e. not a transitive +/// closure. +fn emit_outlives_facts<'tcx>( + facts: &mut AllFacts, + location_table: &LocationTable, + constraints: &MirTypeckRegionConstraints<'tcx>, +) { + facts.subset_base.extend(constraints.outlives_constraints.outlives().iter().flat_map( + |constraint: &OutlivesConstraint<'_>| { + if let Some(from_location) = constraint.locations.from_location() { + Either::Left(iter::once(( + constraint.sup.into(), + constraint.sub.into(), + location_table.mid_index(from_location), + ))) + } else { + Either::Right( + location_table.all_points().map(move |location| { + (constraint.sup.into(), constraint.sub.into(), location) + }), + ) + } + }, + )); +} diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs index 9fccc00bdaf..eee5e70efe3 100644 --- a/compiler/rustc_borrowck/src/polonius/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/mod.rs @@ -1,184 +1,180 @@ -//! Functions dedicated to fact generation for the `-Zpolonius=legacy` datalog implementation. +//! Polonius analysis and support code: +//! - dedicated constraints +//! - conversion from NLL constraints +//! - debugging utilities +//! - etc. +//! +//! The current implementation models the flow-sensitive borrow-checking concerns as a graph +//! containing both information about regions and information about the control flow. +//! +//! Loan propagation is seen as a reachability problem (with some subtleties) between where the loan +//! is introduced and a given point. +//! +//! Constraints arising from type-checking allow loans to flow from region to region at the same CFG +//! point. Constraints arising from liveness allow loans to flow within from point to point, between +//! live regions at these points. +//! +//! Edges can be bidirectional to encode invariant relationships, and loans can flow "back in time" +//! to traverse these constraints arising earlier in the CFG. +//! +//! When incorporating kills in the traversal, the loans reaching a given point are considered live. +//! +//! After this, the usual NLL process happens. These live loans are fed into a dataflow analysis +//! combining them with the points where loans go out of NLL scope (the frontier where they stop +//! propagating to a live region), to yield the "loans in scope" or "active loans", at a given +//! point. +//! +//! Illegal accesses are still computed by checking whether one of these resulting loans is +//! invalidated. +//! +//! More information on this simple approach can be found in the following links, and in the future +//! in the rustc dev guide: +//! - <https://smallcultfollowing.com/babysteps/blog/2023/09/22/polonius-part-1/> +//! - <https://smallcultfollowing.com/babysteps/blog/2023/09/29/polonius-part-2/> //! -//! Will be removed in the future, once the in-tree `-Zpolonius=next` implementation reaches feature -//! parity. - -use rustc_middle::mir::{Body, LocalKind, Location, START_BLOCK}; -use rustc_middle::ty::TyCtxt; -use rustc_mir_dataflow::move_paths::{InitKind, InitLocation, MoveData}; -use tracing::debug; -use crate::borrow_set::BorrowSet; -use crate::facts::{AllFacts, PoloniusRegionVid}; -use crate::location::LocationTable; -use crate::type_check::free_region_relations::UniversalRegionRelations; +mod constraints; +pub(crate) use constraints::*; +mod dump; +pub(crate) use dump::dump_polonius_mir; +pub(crate) mod legacy; + +use rustc_middle::mir::{Body, Location}; +use rustc_mir_dataflow::points::PointIndex; + +use crate::RegionInferenceContext; +use crate::constraints::OutlivesConstraint; +use crate::region_infer::values::LivenessValues; +use crate::type_check::Locations; +use crate::universal_regions::UniversalRegions; + +/// Creates a constraint set for `-Zpolonius=next` by: +/// - converting NLL typeck constraints to be localized +/// - encoding liveness constraints +pub(crate) fn create_localized_constraints<'tcx>( + regioncx: &mut RegionInferenceContext<'tcx>, + body: &Body<'tcx>, +) -> LocalizedOutlivesConstraintSet { + let mut localized_outlives_constraints = LocalizedOutlivesConstraintSet::default(); + convert_typeck_constraints( + body, + regioncx.liveness_constraints(), + regioncx.outlives_constraints(), + &mut localized_outlives_constraints, + ); + create_liveness_constraints( + body, + regioncx.liveness_constraints(), + regioncx.universal_regions(), + &mut localized_outlives_constraints, + ); -mod loan_invalidations; -mod loan_kills; + // FIXME: here, we can trace loan reachability in the constraint graph and record this as loan + // liveness for the next step in the chain, the NLL loan scope and active loans computations. -/// When requested, emit most of the facts needed by polonius: -/// - moves and assignments -/// - universal regions and their relations -/// - CFG points and edges -/// - loan kills -/// - loan invalidations -/// -/// The rest of the facts are emitted during typeck and liveness. -pub(crate) fn emit_facts<'tcx>( - all_facts: &mut Option<AllFacts>, - tcx: TyCtxt<'tcx>, - location_table: &LocationTable, - body: &Body<'tcx>, - borrow_set: &BorrowSet<'tcx>, - move_data: &MoveData<'_>, - universal_region_relations: &UniversalRegionRelations<'_>, -) { - let Some(all_facts) = all_facts else { - // We don't do anything if there are no facts to fill. - return; - }; - let _prof_timer = tcx.prof.generic_activity("polonius_fact_generation"); - emit_move_facts(all_facts, move_data, location_table, body); - emit_universal_region_facts(all_facts, borrow_set, universal_region_relations); - emit_cfg_and_loan_kills_facts(all_facts, tcx, location_table, body, borrow_set); - emit_loan_invalidations_facts(all_facts, tcx, location_table, body, borrow_set); + localized_outlives_constraints } -/// Emit facts needed for move/init analysis: moves and assignments. -fn emit_move_facts( - all_facts: &mut AllFacts, - move_data: &MoveData<'_>, - location_table: &LocationTable, - body: &Body<'_>, +/// Propagate loans throughout the subset graph at a given point (with some subtleties around the +/// location where effects start to be visible). +fn convert_typeck_constraints<'tcx>( + body: &Body<'tcx>, + liveness: &LivenessValues, + outlives_constraints: impl Iterator<Item = OutlivesConstraint<'tcx>>, + localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet, ) { - all_facts - .path_is_var - .extend(move_data.rev_lookup.iter_locals_enumerated().map(|(l, r)| (r, l))); - - for (child, move_path) in move_data.move_paths.iter_enumerated() { - if let Some(parent) = move_path.parent { - all_facts.child_path.push((child, parent)); - } - } - - let fn_entry_start = - location_table.start_index(Location { block: START_BLOCK, statement_index: 0 }); - - // initialized_at - for init in move_data.inits.iter() { - match init.location { - InitLocation::Statement(location) => { - let block_data = &body[location.block]; - let is_terminator = location.statement_index == block_data.statements.len(); - - if is_terminator && init.kind == InitKind::NonPanicPathOnly { - // We are at the terminator of an init that has a panic path, - // and where the init should not happen on panic - - for successor in block_data.terminator().successors() { - if body[successor].is_cleanup { - continue; - } - - // The initialization happened in (or rather, when arriving at) - // the successors, but not in the unwind block. - let first_statement = Location { block: successor, statement_index: 0 }; - all_facts - .path_assigned_at_base - .push((init.path, location_table.start_index(first_statement))); + for outlives_constraint in outlives_constraints { + match outlives_constraint.locations { + Locations::All(_) => { + // For now, turn logical constraints holding at all points into physical edges at + // every point in the graph. + // FIXME: encode this into *traversal* instead. + for (block, bb) in body.basic_blocks.iter_enumerated() { + let statement_count = bb.statements.len(); + for statement_index in 0..=statement_count { + let current_location = Location { block, statement_index }; + let current_point = liveness.point_from_location(current_location); + + localized_outlives_constraints.push(LocalizedOutlivesConstraint { + source: outlives_constraint.sup, + from: current_point, + target: outlives_constraint.sub, + to: current_point, + }); } - } else { - // In all other cases, the initialization just happens at the - // midpoint, like any other effect. - all_facts - .path_assigned_at_base - .push((init.path, location_table.mid_index(location))); } } - // Arguments are initialized on function entry - InitLocation::Argument(local) => { - assert!(body.local_kind(local) == LocalKind::Arg); - all_facts.path_assigned_at_base.push((init.path, fn_entry_start)); - } - } - } - for (local, path) in move_data.rev_lookup.iter_locals_enumerated() { - if body.local_kind(local) != LocalKind::Arg { - // Non-arguments start out deinitialised; we simulate this with an - // initial move: - all_facts.path_moved_at_base.push((path, fn_entry_start)); + _ => {} } } - - // moved_out_at - // deinitialisation is assumed to always happen! - all_facts - .path_moved_at_base - .extend(move_data.moves.iter().map(|mo| (mo.path, location_table.mid_index(mo.source)))); } -/// Emit universal regions facts, and their relations. -fn emit_universal_region_facts( - all_facts: &mut AllFacts, - borrow_set: &BorrowSet<'_>, - universal_region_relations: &UniversalRegionRelations<'_>, +/// Propagate loans throughout the CFG: for each statement in the MIR, create localized outlives +/// constraints for loans that are propagated to the next statements. +pub(crate) fn create_liveness_constraints<'tcx>( + body: &Body<'tcx>, + liveness: &LivenessValues, + universal_regions: &UniversalRegions<'tcx>, + localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet, ) { - // 1: universal regions are modeled in Polonius as a pair: - // - the universal region vid itself. - // - a "placeholder loan" associated to this universal region. Since they don't exist in - // the `borrow_set`, their `BorrowIndex` are synthesized as the universal region index - // added to the existing number of loans, as if they succeeded them in the set. - // - let universal_regions = &universal_region_relations.universal_regions; - all_facts - .universal_region - .extend(universal_regions.universal_regions_iter().map(PoloniusRegionVid::from)); - let borrow_count = borrow_set.len(); - debug!( - "emit_universal_region_facts: polonius placeholders, num_universals={}, borrow_count={}", - universal_regions.len(), - borrow_count - ); - - for universal_region in universal_regions.universal_regions_iter() { - let universal_region_idx = universal_region.index(); - let placeholder_loan_idx = borrow_count + universal_region_idx; - all_facts.placeholder.push((universal_region.into(), placeholder_loan_idx.into())); - } - - // 2: the universal region relations `outlives` constraints are emitted as - // `known_placeholder_subset` facts. - for (fr1, fr2) in universal_region_relations.known_outlives() { - if fr1 != fr2 { - debug!( - "emit_universal_region_facts: emitting polonius `known_placeholder_subset` \ - fr1={:?}, fr2={:?}", - fr1, fr2 - ); - all_facts.known_placeholder_subset.push((fr1.into(), fr2.into())); + for (block, bb) in body.basic_blocks.iter_enumerated() { + let statement_count = bb.statements.len(); + for statement_index in 0..=statement_count { + let current_location = Location { block, statement_index }; + let current_point = liveness.point_from_location(current_location); + + if statement_index < statement_count { + // Intra-block edges, straight line constraints from each point to its successor + // within the same block. + let next_location = Location { block, statement_index: statement_index + 1 }; + let next_point = liveness.point_from_location(next_location); + propagate_loans_between_points( + current_point, + next_point, + liveness, + universal_regions, + localized_outlives_constraints, + ); + } else { + // Inter-block edges, from the block's terminator to each successor block's entry + // point. + for successor_block in bb.terminator().successors() { + let next_location = Location { block: successor_block, statement_index: 0 }; + let next_point = liveness.point_from_location(next_location); + propagate_loans_between_points( + current_point, + next_point, + liveness, + universal_regions, + localized_outlives_constraints, + ); + } + } } } } -/// Emit facts about loan invalidations. -fn emit_loan_invalidations_facts<'tcx>( - all_facts: &mut AllFacts, - tcx: TyCtxt<'tcx>, - location_table: &LocationTable, - body: &Body<'tcx>, - borrow_set: &BorrowSet<'tcx>, -) { - loan_invalidations::emit_loan_invalidations(tcx, all_facts, location_table, body, borrow_set); -} - -/// Emit facts about CFG points and edges, as well as locations where loans are killed. -fn emit_cfg_and_loan_kills_facts<'tcx>( - all_facts: &mut AllFacts, - tcx: TyCtxt<'tcx>, - location_table: &LocationTable, - body: &Body<'tcx>, - borrow_set: &BorrowSet<'tcx>, +/// Propagate loans within a region between two points in the CFG, if that region is live at both +/// the source and target points. +fn propagate_loans_between_points( + current_point: PointIndex, + next_point: PointIndex, + _liveness: &LivenessValues, + universal_regions: &UniversalRegions<'_>, + localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet, ) { - loan_kills::emit_loan_kills(tcx, all_facts, location_table, body, borrow_set); + // Universal regions are semantically live at all points. + // Note: we always have universal regions but they're not always (or often) involved in the + // subset graph. For now, we emit all their edges unconditionally, but some of these subgraphs + // will be disconnected from the rest of the graph and thus, unnecessary. + // FIXME: only emit the edges of universal regions that existential regions can reach. + for region in universal_regions.universal_regions_iter() { + localized_outlives_constraints.push(LocalizedOutlivesConstraint { + source: region, + from: current_point, + target: region, + to: next_point, + }); + } } diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 0eecf98a6ed..907a3f16b06 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -571,7 +571,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// Given a universal region in scope on the MIR, returns the /// corresponding index. /// - /// (Panics if `r` is not a registered universal region.) + /// Panics if `r` is not a registered universal region, most notably + /// if it is a placeholder. Handling placeholders requires access to the + /// `MirTypeckRegionConstraints`. pub(crate) fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid { self.universal_regions().to_region_vid(r) } @@ -795,7 +797,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { // If the member region lives in a higher universe, we currently choose // the most conservative option by leaving it unchanged. - if !self.constraint_sccs().annotation(scc).min_universe().is_root() { return; } @@ -823,12 +824,14 @@ impl<'tcx> RegionInferenceContext<'tcx> { } debug!(?choice_regions, "after ub"); - // At this point we can pick any member of `choice_regions`, but to avoid potential - // non-determinism we will pick the *unique minimum* choice. + // At this point we can pick any member of `choice_regions` and would like to choose + // it to be a small as possible. To avoid potential non-determinism we will pick the + // smallest such choice. // // Because universal regions are only partially ordered (i.e, not every two regions are // comparable), we will ignore any region that doesn't compare to all others when picking // the minimum choice. + // // For example, consider `choice_regions = ['static, 'a, 'b, 'c, 'd, 'e]`, where // `'static: 'a, 'static: 'b, 'a: 'c, 'b: 'c, 'c: 'd, 'c: 'e`. // `['d, 'e]` are ignored because they do not compare - the same goes for `['a, 'b]`. @@ -853,6 +856,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { return; }; + // As we require `'scc: 'min_choice`, we have definitely already computed + // its `scc_values` at this point. let min_choice_scc = self.constraint_sccs.scc(min_choice); debug!(?min_choice, ?min_choice_scc); if self.scc_values.add_region(scc, min_choice_scc) { @@ -2224,13 +2229,17 @@ impl<'tcx> RegionInferenceContext<'tcx> { fn scc_representative(&self, scc: ConstraintSccIndex) -> RegionVid { self.constraint_sccs.annotation(scc).representative } + + pub(crate) fn liveness_constraints(&self) -> &LivenessValues { + &self.liveness_constraints + } } impl<'tcx> RegionDefinition<'tcx> { fn new(universe: ty::UniverseIndex, rv_origin: RegionVariableOrigin) -> Self { // Create a new region definition. Note that, for free // regions, the `external_name` field gets updated later in - // `init_universal_regions`. + // `init_free_and_bound_regions`. let origin = match rv_origin { RegionVariableOrigin::Nll(origin) => origin, diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 7164a129235..3e16a3ca157 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -134,12 +134,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { // the hidden type becomes the opaque type itself. In this case, this was an opaque // usage of the opaque type and we can ignore it. This check is mirrored in typeck's // writeback. - // FIXME(-Znext-solver): This should be unnecessary with the new solver. - if let ty::Alias(ty::Opaque, alias_ty) = ty.kind() - && alias_ty.def_id == opaque_type_key.def_id.to_def_id() - && alias_ty.args == opaque_type_key.args - { - continue; + if !infcx.next_trait_solver() { + if let ty::Alias(ty::Opaque, alias_ty) = ty.kind() + && alias_ty.def_id == opaque_type_key.def_id.to_def_id() + && alias_ty.args == opaque_type_key.args + { + continue; + } } // Sometimes two opaque types are the same only after we remap the generic parameters // back to the opaque type definition. E.g. we may have `OpaqueType<X, Y>` mapped to diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index a16bce63839..0b0757f16ab 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -199,6 +199,11 @@ impl LivenessValues { self.elements.point_from_location(location) } + #[inline] + pub(crate) fn location_from_point(&self, point: PointIndex) -> Location { + self.elements.to_location(point) + } + /// When using `-Zpolonius=next`, returns whether the `loan_idx` is live at the given `point`. pub(crate) fn is_loan_live_at(&self, loan_idx: BorrowIndex, point: PointIndex) -> bool { self.loans diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index 918efac2a20..4b7f5321388 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -77,17 +77,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { #[instrument(skip(self), level = "debug")] pub(super) fn convert_all(&mut self, query_constraints: &QueryRegionConstraints<'tcx>) { - let QueryRegionConstraints { outlives, member_constraints } = query_constraints; - - // Annoying: to invoke `self.to_region_vid`, we need access to - // `self.constraints`, but we also want to be mutating - // `self.member_constraints`. For now, just swap out the value - // we want and replace at the end. - let mut tmp = std::mem::take(&mut self.constraints.member_constraints); - for member_constraint in member_constraints { - tmp.push_constraint(member_constraint, |r| self.to_region_vid(r)); - } - self.constraints.member_constraints = tmp; + let QueryRegionConstraints { outlives } = query_constraints; for &(predicate, constraint_category) in outlives { self.convert(predicate, constraint_category); @@ -295,13 +285,8 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { match result { Ok(TypeOpOutput { output: ty, constraints, .. }) => { - if let Some(constraints) = constraints { - assert!( - constraints.member_constraints.is_empty(), - "no member constraints expected from normalizing: {:#?}", - constraints.member_constraints - ); - next_outlives_predicates.extend(constraints.outlives.iter().copied()); + if let Some(QueryRegionConstraints { outlives }) = constraints { + next_outlives_predicates.extend(outlives.iter().copied()); } ty } diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs index 20d19a53752..683293bf828 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs @@ -17,7 +17,6 @@ use crate::region_infer::values::LivenessValues; use crate::universal_regions::UniversalRegions; mod local_use_map; -mod polonius; mod trace; /// Combines liveness analysis with initialization analysis to @@ -45,8 +44,6 @@ pub(super) fn generate<'a, 'tcx>( let (relevant_live_locals, boring_locals) = compute_relevant_live_locals(typeck.tcx(), &free_regions, body); - polonius::populate_access_facts(typeck, body, move_data); - trace::trace( typeck, body, diff --git a/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs b/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs deleted file mode 100644 index e8d8ae0850b..00000000000 --- a/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs +++ /dev/null @@ -1,136 +0,0 @@ -use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor}; -use rustc_middle::mir::{Body, Local, Location, Place}; -use rustc_middle::ty::GenericArg; -use rustc_mir_dataflow::move_paths::{LookupResult, MoveData, MovePathIndex}; -use tracing::debug; - -use super::TypeChecker; -use crate::def_use::{self, DefUse}; -use crate::location::{LocationIndex, LocationTable}; - -type VarPointRelation = Vec<(Local, LocationIndex)>; -type PathPointRelation = Vec<(MovePathIndex, LocationIndex)>; - -struct UseFactsExtractor<'a, 'tcx> { - var_defined_at: &'a mut VarPointRelation, - var_used_at: &'a mut VarPointRelation, - location_table: &'a LocationTable, - var_dropped_at: &'a mut VarPointRelation, - move_data: &'a MoveData<'tcx>, - path_accessed_at_base: &'a mut PathPointRelation, -} - -// A Visitor to walk through the MIR and extract point-wise facts -impl<'tcx> UseFactsExtractor<'_, 'tcx> { - fn location_to_index(&self, location: Location) -> LocationIndex { - self.location_table.mid_index(location) - } - - fn insert_def(&mut self, local: Local, location: Location) { - debug!("UseFactsExtractor::insert_def()"); - self.var_defined_at.push((local, self.location_to_index(location))); - } - - fn insert_use(&mut self, local: Local, location: Location) { - debug!("UseFactsExtractor::insert_use()"); - self.var_used_at.push((local, self.location_to_index(location))); - } - - fn insert_drop_use(&mut self, local: Local, location: Location) { - debug!("UseFactsExtractor::insert_drop_use()"); - self.var_dropped_at.push((local, self.location_to_index(location))); - } - - fn insert_path_access(&mut self, path: MovePathIndex, location: Location) { - debug!("UseFactsExtractor::insert_path_access({:?}, {:?})", path, location); - self.path_accessed_at_base.push((path, self.location_to_index(location))); - } - - fn place_to_mpi(&self, place: &Place<'tcx>) -> Option<MovePathIndex> { - match self.move_data.rev_lookup.find(place.as_ref()) { - LookupResult::Exact(mpi) => Some(mpi), - LookupResult::Parent(mmpi) => mmpi, - } - } -} - -impl<'a, 'tcx> Visitor<'tcx> for UseFactsExtractor<'a, 'tcx> { - fn visit_local(&mut self, local: Local, context: PlaceContext, location: Location) { - match def_use::categorize(context) { - Some(DefUse::Def) => self.insert_def(local, location), - Some(DefUse::Use) => self.insert_use(local, location), - Some(DefUse::Drop) => self.insert_drop_use(local, location), - _ => (), - } - } - - fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) { - self.super_place(place, context, location); - match context { - PlaceContext::NonMutatingUse(_) => { - if let Some(mpi) = self.place_to_mpi(place) { - self.insert_path_access(mpi, location); - } - } - - PlaceContext::MutatingUse(MutatingUseContext::Borrow) => { - if let Some(mpi) = self.place_to_mpi(place) { - self.insert_path_access(mpi, location); - } - } - _ => (), - } - } -} - -pub(super) fn populate_access_facts<'a, 'tcx>( - typeck: &mut TypeChecker<'a, 'tcx>, - body: &Body<'tcx>, - move_data: &MoveData<'tcx>, -) { - if let Some(facts) = typeck.all_facts.as_mut() { - debug!("populate_access_facts()"); - let location_table = typeck.location_table; - - let mut extractor = UseFactsExtractor { - var_defined_at: &mut facts.var_defined_at, - var_used_at: &mut facts.var_used_at, - var_dropped_at: &mut facts.var_dropped_at, - path_accessed_at_base: &mut facts.path_accessed_at_base, - location_table, - move_data, - }; - extractor.visit_body(body); - - for (local, local_decl) in body.local_decls.iter_enumerated() { - debug!( - "add use_of_var_derefs_origin facts - local={:?}, type={:?}", - local, local_decl.ty - ); - let _prof_timer = typeck.infcx.tcx.prof.generic_activity("polonius_fact_generation"); - let universal_regions = &typeck.universal_regions; - typeck.infcx.tcx.for_each_free_region(&local_decl.ty, |region| { - let region_vid = universal_regions.to_region_vid(region); - facts.use_of_var_derefs_origin.push((local, region_vid.into())); - }); - } - } -} - -/// For every potentially drop()-touched region `region` in `local`'s type -/// (`kind`), emit a Polonius `use_of_var_derefs_origin(local, origin)` fact. -pub(super) fn add_drop_of_var_derefs_origin<'tcx>( - typeck: &mut TypeChecker<'_, 'tcx>, - local: Local, - kind: &GenericArg<'tcx>, -) { - debug!("add_drop_of_var_derefs_origin(local={:?}, kind={:?}", local, kind); - if let Some(facts) = typeck.all_facts.as_mut() { - let _prof_timer = typeck.infcx.tcx.prof.generic_activity("polonius_fact_generation"); - let universal_regions = &typeck.universal_regions; - typeck.infcx.tcx.for_each_free_region(kind, |drop_live_region| { - let region_vid = universal_regions.to_region_vid(drop_live_region); - facts.drop_of_var_derefs_origin.push((local, region_vid.into())); - }); - } -} diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 3ec36c16cbf..f510d193dd9 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -15,9 +15,9 @@ use rustc_trait_selection::traits::query::type_op::{DropckOutlives, TypeOp, Type use tracing::debug; use crate::location::RichLocation; +use crate::polonius; use crate::region_infer::values::{self, LiveLoans}; use crate::type_check::liveness::local_use_map::LocalUseMap; -use crate::type_check::liveness::polonius; use crate::type_check::{NormalizeLocation, TypeChecker}; /// This is the heart of the liveness computation. For each variable X @@ -590,7 +590,13 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { // the destructor and must be live at this point. for &kind in &drop_data.dropck_result.kinds { Self::make_all_regions_live(self.elements, self.typeck, kind, live_at); - polonius::add_drop_of_var_derefs_origin(self.typeck, dropped_local, &kind); + polonius::legacy::emit_drop_facts( + self.typeck.tcx(), + dropped_local, + &kind, + self.typeck.universal_regions, + self.typeck.all_facts, + ); } } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 90d327b0ad2..4d53c87e3fc 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -3,7 +3,6 @@ use std::rc::Rc; use std::{fmt, iter, mem}; -use either::Either; use rustc_abi::{FIRST_VARIANT, FieldIdx}; use rustc_data_structures::frozen::Frozen; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; @@ -40,11 +39,8 @@ use rustc_mir_dataflow::move_paths::MoveData; use rustc_mir_dataflow::points::DenseLocationMap; use rustc_span::def_id::CRATE_DEF_ID; use rustc_span::source_map::Spanned; -use rustc_span::symbol::sym; -use rustc_span::{DUMMY_SP, Span}; -use rustc_trait_selection::traits::query::type_op::custom::{ - CustomTypeOp, scrape_region_constraints, -}; +use rustc_span::{DUMMY_SP, Span, sym}; +use rustc_trait_selection::traits::query::type_op::custom::scrape_region_constraints; use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput}; use tracing::{debug, instrument, trace}; @@ -91,6 +87,7 @@ mod constraint_conversion; pub(crate) mod free_region_relations; mod input_output; pub(crate) mod liveness; +mod opaque_types; mod relate_tys; /// Type checks the given `mir` in the context of the inference @@ -107,7 +104,6 @@ mod relate_tys; /// # Parameters /// /// - `infcx` -- inference context to use -/// - `param_env` -- parameter environment to use for trait solving /// - `body` -- MIR body to type-check /// - `promoted` -- map of promoted constants within `body` /// - `universal_regions` -- the universal regions from `body`s function signature @@ -155,7 +151,7 @@ pub(crate) fn type_check<'a, 'tcx>( debug!(?normalized_inputs_and_output); - let mut checker = TypeChecker { + let mut typeck = TypeChecker { infcx, last_span: body.span, body, @@ -171,93 +167,23 @@ pub(crate) fn type_check<'a, 'tcx>( constraints: &mut constraints, }; - checker.check_user_type_annotations(); + typeck.check_user_type_annotations(); - let mut verifier = TypeVerifier { cx: &mut checker, promoted, last_span: body.span }; + let mut verifier = TypeVerifier { typeck: &mut typeck, promoted, last_span: body.span }; verifier.visit_body(body); - checker.typeck_mir(body); - checker.equate_inputs_and_outputs(body, &normalized_inputs_and_output); - checker.check_signature_annotation(body); - - liveness::generate(&mut checker, body, &elements, flow_inits, move_data); - - translate_outlives_facts(&mut checker); - let opaque_type_values = infcx.take_opaque_types(); - - let opaque_type_values = opaque_type_values - .into_iter() - .map(|(opaque_type_key, decl)| { - let _: Result<_, ErrorGuaranteed> = checker.fully_perform_op( - Locations::All(body.span), - ConstraintCategory::OpaqueType, - CustomTypeOp::new( - |ocx| { - ocx.infcx.register_member_constraints( - opaque_type_key, - decl.hidden_type.ty, - decl.hidden_type.span, - ); - Ok(()) - }, - "opaque_type_map", - ), - ); - let hidden_type = infcx.resolve_vars_if_possible(decl.hidden_type); - trace!("finalized opaque type {:?} to {:#?}", opaque_type_key, hidden_type.ty.kind()); - if hidden_type.has_non_region_infer() { - infcx.dcx().span_bug( - decl.hidden_type.span, - format!("could not resolve {:#?}", hidden_type.ty.kind()), - ); - } + typeck.typeck_mir(body); + typeck.equate_inputs_and_outputs(body, &normalized_inputs_and_output); + typeck.check_signature_annotation(body); - // Convert all regions to nll vars. - let (opaque_type_key, hidden_type) = - fold_regions(infcx.tcx, (opaque_type_key, hidden_type), |region, _| { - match region.kind() { - ty::ReVar(_) => region, - ty::RePlaceholder(placeholder) => { - checker.constraints.placeholder_region(infcx, placeholder) - } - _ => ty::Region::new_var( - infcx.tcx, - checker.universal_regions.to_region_vid(region), - ), - } - }); + liveness::generate(&mut typeck, body, &elements, flow_inits, move_data); - (opaque_type_key, hidden_type) - }) - .collect(); + let opaque_type_values = + opaque_types::take_opaques_and_register_member_constraints(&mut typeck); MirTypeckResults { constraints, universal_region_relations, opaque_type_values } } -fn translate_outlives_facts(typeck: &mut TypeChecker<'_, '_>) { - if let Some(facts) = typeck.all_facts { - let _prof_timer = typeck.infcx.tcx.prof.generic_activity("polonius_fact_generation"); - let location_table = typeck.location_table; - facts.subset_base.extend( - typeck.constraints.outlives_constraints.outlives().iter().flat_map( - |constraint: &OutlivesConstraint<'_>| { - if let Some(from_location) = constraint.locations.from_location() { - Either::Left(iter::once(( - constraint.sup.into(), - constraint.sub.into(), - location_table.mid_index(from_location), - ))) - } else { - Either::Right(location_table.all_points().map(move |location| { - (constraint.sup.into(), constraint.sub.into(), location) - })) - } - }, - ), - ); - } -} - #[track_caller] fn mirbug(tcx: TyCtxt<'_>, span: Span, msg: String) { // We sometimes see MIR failures (notably predicate failures) due to @@ -276,7 +202,7 @@ enum FieldAccessError { /// type, calling `span_mirbug` and returning an error type if there /// is a problem. struct TypeVerifier<'a, 'b, 'tcx> { - cx: &'a mut TypeChecker<'b, 'tcx>, + typeck: &'a mut TypeChecker<'b, 'tcx>, promoted: &'b IndexSlice<Promoted, Body<'tcx>>, last_span: Span, } @@ -298,9 +224,9 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { self.super_const_operand(constant, location); let ty = self.sanitize_type(constant, constant.const_.ty()); - self.cx.infcx.tcx.for_each_free_region(&ty, |live_region| { - let live_region_vid = self.cx.universal_regions.to_region_vid(live_region); - self.cx.constraints.liveness_constraints.add_location(live_region_vid, location); + self.typeck.infcx.tcx.for_each_free_region(&ty, |live_region| { + let live_region_vid = self.typeck.universal_regions.to_region_vid(live_region); + self.typeck.constraints.liveness_constraints.add_location(live_region_vid, location); }); // HACK(compiler-errors): Constants that are gathered into Body.required_consts @@ -312,14 +238,14 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { }; if let Some(annotation_index) = constant.user_ty { - if let Err(terr) = self.cx.relate_type_and_user_type( + if let Err(terr) = self.typeck.relate_type_and_user_type( constant.const_.ty(), ty::Invariant, &UserTypeProjection { base: annotation_index, projs: vec![] }, locations, ConstraintCategory::Boring, ) { - let annotation = &self.cx.user_type_annotations[annotation_index]; + let annotation = &self.typeck.user_type_annotations[annotation_index]; span_mirbug!( self, constant, @@ -348,9 +274,12 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { promoted: &Body<'tcx>, ty, san_ty| { - if let Err(terr) = - verifier.cx.eq_types(ty, san_ty, locations, ConstraintCategory::Boring) - { + if let Err(terr) = verifier.typeck.eq_types( + ty, + san_ty, + locations, + ConstraintCategory::Boring, + ) { span_mirbug!( verifier, promoted, @@ -368,21 +297,21 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { let promoted_ty = promoted_body.return_ty(); check_err(self, promoted_body, ty, promoted_ty); } else { - self.cx.ascribe_user_type( + self.typeck.ascribe_user_type( constant.const_.ty(), ty::UserType::new(ty::UserTypeKind::TypeOf(uv.def, UserArgs { args: uv.args, user_self_ty: None, })), - locations.span(self.cx.body), + locations.span(self.typeck.body), ); } } else if let Some(static_def_id) = constant.check_static_ptr(tcx) { let unnormalized_ty = tcx.type_of(static_def_id).instantiate_identity(); - let normalized_ty = self.cx.normalize(unnormalized_ty, locations); + let normalized_ty = self.typeck.normalize(unnormalized_ty, locations); let literal_ty = constant.const_.ty().builtin_deref(true).unwrap(); - if let Err(terr) = self.cx.eq_types( + if let Err(terr) = self.typeck.eq_types( literal_ty, normalized_ty, locations, @@ -394,7 +323,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { if let ty::FnDef(def_id, args) = *constant.const_.ty().kind() { let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, args); - self.cx.normalize_and_prove_instantiated_predicates( + self.typeck.normalize_and_prove_instantiated_predicates( def_id, instantiated_predicates, locations, @@ -404,7 +333,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { tcx.impl_of_method(def_id).map(|imp| tcx.def_kind(imp)), Some(DefKind::Impl { of_trait: true }) )); - self.cx.prove_predicates( + self.typeck.prove_predicates( args.types().map(|ty| ty::ClauseKind::WellFormed(ty.into())), locations, ConstraintCategory::Boring, @@ -438,7 +367,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { local_decl.ty }; - if let Err(terr) = self.cx.relate_type_and_user_type( + if let Err(terr) = self.typeck.relate_type_and_user_type( ty, ty::Invariant, user_ty, @@ -468,11 +397,11 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { fn body(&self) -> &Body<'tcx> { - self.cx.body + self.typeck.body } fn tcx(&self) -> TyCtxt<'tcx> { - self.cx.infcx.tcx + self.typeck.infcx.tcx } fn sanitize_type(&mut self, parent: &dyn fmt::Debug, ty: Ty<'tcx>) -> Ty<'tcx> { @@ -522,7 +451,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { // whether the bounds fully apply: in effect, the rule is // that if a value of some type could implement `Copy`, then // it must. - self.cx.prove_trait_ref( + self.typeck.prove_trait_ref( trait_ref, location.to_locations(), ConstraintCategory::CopyBound, @@ -537,7 +466,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { // checker on the promoted MIR, then transfer the constraints back to // the main MIR, changing the locations to the provided location. - let parent_body = mem::replace(&mut self.cx.body, promoted_body); + let parent_body = mem::replace(&mut self.typeck.body, promoted_body); // Use new sets of constraints and closure bounds so that we can // modify their locations. @@ -548,18 +477,18 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { // Don't try to add borrow_region facts for the promoted MIR let mut swap_constraints = |this: &mut Self| { - mem::swap(this.cx.all_facts, all_facts); - mem::swap(&mut this.cx.constraints.outlives_constraints, &mut constraints); - mem::swap(&mut this.cx.constraints.liveness_constraints, &mut liveness_constraints); + mem::swap(this.typeck.all_facts, all_facts); + mem::swap(&mut this.typeck.constraints.outlives_constraints, &mut constraints); + mem::swap(&mut this.typeck.constraints.liveness_constraints, &mut liveness_constraints); }; swap_constraints(self); self.visit_body(promoted_body); - self.cx.typeck_mir(promoted_body); + self.typeck.typeck_mir(promoted_body); - self.cx.body = parent_body; + self.typeck.body = parent_body; // Merge the outlives constraints back in, at the given location. swap_constraints(self); @@ -575,7 +504,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { // temporary from the user's point of view. constraint.category = ConstraintCategory::Boring; } - self.cx.constraints.outlives_constraints.push(constraint) + self.typeck.constraints.outlives_constraints.push(constraint) } // If the region is live at least one location in the promoted MIR, // then add a liveness constraint to the main MIR for this region @@ -585,7 +514,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { // unordered. #[allow(rustc::potential_query_instability)] for region in liveness_constraints.live_regions_unordered() { - self.cx.constraints.liveness_constraints.add_location(region, location); + self.typeck.constraints.liveness_constraints.add_location(region, location); } } @@ -669,13 +598,13 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { }, ProjectionElem::Field(field, fty) => { let fty = self.sanitize_type(place, fty); - let fty = self.cx.normalize(fty, location); + let fty = self.typeck.normalize(fty, location); match self.field_ty(place, base, field, location) { Ok(ty) => { - let ty = self.cx.normalize(ty, location); + let ty = self.typeck.normalize(ty, location); debug!(?fty, ?ty); - if let Err(terr) = self.cx.relate_types( + if let Err(terr) = self.typeck.relate_types( ty, self.get_ambient_variance(context), fty, @@ -707,8 +636,8 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { } ProjectionElem::OpaqueCast(ty) => { let ty = self.sanitize_type(place, ty); - let ty = self.cx.normalize(ty, location); - self.cx + let ty = self.typeck.normalize(ty, location); + self.typeck .relate_types( ty, self.get_ambient_variance(context), @@ -817,7 +746,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { }; if let Some(field) = variant.fields.get(field) { - Ok(self.cx.normalize(field.ty(tcx, args), location)) + Ok(self.typeck.normalize(field.ty(tcx, args), location)) } else { Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() }) } @@ -981,6 +910,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.body } + fn to_region_vid(&mut self, r: ty::Region<'tcx>) -> RegionVid { + if let ty::RePlaceholder(placeholder) = r.kind() { + self.constraints.placeholder_region(self.infcx, placeholder).as_var() + } else { + self.universal_regions.to_region_vid(r) + } + } + fn unsized_feature_enabled(&self) -> bool { let features = self.tcx().features(); features.unsized_locals() || features.unsized_fn_params() diff --git a/compiler/rustc_borrowck/src/type_check/opaque_types.rs b/compiler/rustc_borrowck/src/type_check/opaque_types.rs new file mode 100644 index 00000000000..edf3b1ae092 --- /dev/null +++ b/compiler/rustc_borrowck/src/type_check/opaque_types.rs @@ -0,0 +1,335 @@ +use std::iter; + +use rustc_data_structures::fx::FxIndexMap; +use rustc_middle::span_bug; +use rustc_middle::ty::fold::fold_regions; +use rustc_middle::ty::{ + self, GenericArgKind, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeSuperVisitable, + TypeVisitable, TypeVisitableExt, TypeVisitor, +}; +use tracing::{debug, trace}; + +use super::{MemberConstraintSet, TypeChecker}; + +/// Once we're done with typechecking the body, we take all the opaque types +/// defined by this function and add their 'member constraints'. +pub(super) fn take_opaques_and_register_member_constraints<'tcx>( + typeck: &mut TypeChecker<'_, 'tcx>, +) -> FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>> { + let infcx = typeck.infcx; + // Annoying: to invoke `typeck.to_region_vid`, we need access to + // `typeck.constraints`, but we also want to be mutating + // `typeck.member_constraints`. For now, just swap out the value + // we want and replace at the end. + let mut member_constraints = std::mem::take(&mut typeck.constraints.member_constraints); + let opaque_types = infcx + .take_opaque_types() + .into_iter() + .map(|(opaque_type_key, decl)| { + let hidden_type = infcx.resolve_vars_if_possible(decl.hidden_type); + register_member_constraints( + typeck, + &mut member_constraints, + opaque_type_key, + hidden_type, + ); + trace!("finalized opaque type {:?} to {:#?}", opaque_type_key, hidden_type.ty.kind()); + if hidden_type.has_non_region_infer() { + span_bug!(hidden_type.span, "could not resolve {:?}", hidden_type.ty); + } + + // Convert all regions to nll vars. + let (opaque_type_key, hidden_type) = + fold_regions(infcx.tcx, (opaque_type_key, hidden_type), |r, _| { + ty::Region::new_var(infcx.tcx, typeck.to_region_vid(r)) + }); + + (opaque_type_key, hidden_type) + }) + .collect(); + assert!(typeck.constraints.member_constraints.is_empty()); + typeck.constraints.member_constraints = member_constraints; + opaque_types +} + +/// Given the map `opaque_types` containing the opaque +/// `impl Trait` types whose underlying, hidden types are being +/// inferred, this method adds constraints to the regions +/// appearing in those underlying hidden types to ensure that they +/// at least do not refer to random scopes within the current +/// function. These constraints are not (quite) sufficient to +/// guarantee that the regions are actually legal values; that +/// final condition is imposed after region inference is done. +/// +/// # The Problem +/// +/// Let's work through an example to explain how it works. Assume +/// the current function is as follows: +/// +/// ```text +/// fn foo<'a, 'b>(..) -> (impl Bar<'a>, impl Bar<'b>) +/// ``` +/// +/// Here, we have two `impl Trait` types whose values are being +/// inferred (the `impl Bar<'a>` and the `impl +/// Bar<'b>`). Conceptually, this is sugar for a setup where we +/// define underlying opaque types (`Foo1`, `Foo2`) and then, in +/// the return type of `foo`, we *reference* those definitions: +/// +/// ```text +/// type Foo1<'x> = impl Bar<'x>; +/// type Foo2<'x> = impl Bar<'x>; +/// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. } +/// // ^^^^ ^^ +/// // | | +/// // | args +/// // def_id +/// ``` +/// +/// As indicating in the comments above, each of those references +/// is (in the compiler) basically generic parameters (`args`) +/// applied to the type of a suitable `def_id` (which identifies +/// `Foo1` or `Foo2`). +/// +/// Now, at this point in compilation, what we have done is to +/// replace each of the references (`Foo1<'a>`, `Foo2<'b>`) with +/// fresh inference variables C1 and C2. We wish to use the values +/// of these variables to infer the underlying types of `Foo1` and +/// `Foo2`. That is, this gives rise to higher-order (pattern) unification +/// constraints like: +/// +/// ```text +/// for<'a> (Foo1<'a> = C1) +/// for<'b> (Foo1<'b> = C2) +/// ``` +/// +/// For these equation to be satisfiable, the types `C1` and `C2` +/// can only refer to a limited set of regions. For example, `C1` +/// can only refer to `'static` and `'a`, and `C2` can only refer +/// to `'static` and `'b`. The job of this function is to impose that +/// constraint. +/// +/// Up to this point, C1 and C2 are basically just random type +/// inference variables, and hence they may contain arbitrary +/// regions. In fact, it is fairly likely that they do! Consider +/// this possible definition of `foo`: +/// +/// ```text +/// fn foo<'a, 'b>(x: &'a i32, y: &'b i32) -> (impl Bar<'a>, impl Bar<'b>) { +/// (&*x, &*y) +/// } +/// ``` +/// +/// Here, the values for the concrete types of the two impl +/// traits will include inference variables: +/// +/// ```text +/// &'0 i32 +/// &'1 i32 +/// ``` +/// +/// Ordinarily, the subtyping rules would ensure that these are +/// sufficiently large. But since `impl Bar<'a>` isn't a specific +/// type per se, we don't get such constraints by default. This +/// is where this function comes into play. It adds extra +/// constraints to ensure that all the regions which appear in the +/// inferred type are regions that could validly appear. +/// +/// This is actually a bit of a tricky constraint in general. We +/// want to say that each variable (e.g., `'0`) can only take on +/// values that were supplied as arguments to the opaque type +/// (e.g., `'a` for `Foo1<'a>`) or `'static`, which is always in +/// scope. We don't have a constraint quite of this kind in the current +/// region checker. +/// +/// # The Solution +/// +/// We generally prefer to make `<=` constraints, since they +/// integrate best into the region solver. To do that, we find the +/// "minimum" of all the arguments that appear in the args: that +/// is, some region which is less than all the others. In the case +/// of `Foo1<'a>`, that would be `'a` (it's the only choice, after +/// all). Then we apply that as a least bound to the variables +/// (e.g., `'a <= '0`). +/// +/// In some cases, there is no minimum. Consider this example: +/// +/// ```text +/// fn baz<'a, 'b>() -> impl Trait<'a, 'b> { ... } +/// ``` +/// +/// Here we would report a more complex "in constraint", like `'r +/// in ['a, 'b, 'static]` (where `'r` is some region appearing in +/// the hidden type). +/// +/// # Constrain regions, not the hidden concrete type +/// +/// Note that generating constraints on each region `Rc` is *not* +/// the same as generating an outlives constraint on `Tc` itself. +/// For example, if we had a function like this: +/// +/// ``` +/// # #![feature(type_alias_impl_trait)] +/// # fn main() {} +/// # trait Foo<'a> {} +/// # impl<'a, T> Foo<'a> for (&'a u32, T) {} +/// fn foo<'a, T>(x: &'a u32, y: T) -> impl Foo<'a> { +/// (x, y) +/// } +/// +/// // Equivalent to: +/// # mod dummy { use super::*; +/// type FooReturn<'a, T> = impl Foo<'a>; +/// fn foo<'a, T>(x: &'a u32, y: T) -> FooReturn<'a, T> { +/// (x, y) +/// } +/// # } +/// ``` +/// +/// then the hidden type `Tc` would be `(&'0 u32, T)` (where `'0` +/// is an inference variable). If we generated a constraint that +/// `Tc: 'a`, then this would incorrectly require that `T: 'a` -- +/// but this is not necessary, because the opaque type we +/// create will be allowed to reference `T`. So we only generate a +/// constraint that `'0: 'a`. +fn register_member_constraints<'tcx>( + typeck: &mut TypeChecker<'_, 'tcx>, + member_constraints: &mut MemberConstraintSet<'tcx, ty::RegionVid>, + opaque_type_key: OpaqueTypeKey<'tcx>, + OpaqueHiddenType { span, ty: hidden_ty }: OpaqueHiddenType<'tcx>, +) { + let tcx = typeck.tcx(); + let hidden_ty = typeck.infcx.resolve_vars_if_possible(hidden_ty); + debug!(?hidden_ty); + + let variances = tcx.variances_of(opaque_type_key.def_id); + debug!(?variances); + + // For a case like `impl Foo<'a, 'b>`, we would generate a constraint + // `'r in ['a, 'b, 'static]` for each region `'r` that appears in the + // hidden type (i.e., it must be equal to `'a`, `'b`, or `'static`). + // + // `conflict1` and `conflict2` are the two region bounds that we + // detected which were unrelated. They are used for diagnostics. + + // Create the set of choice regions: each region in the hidden + // type can be equal to any of the region parameters of the + // opaque type definition. + let fr_static = typeck.universal_regions.fr_static; + let choice_regions: Vec<_> = opaque_type_key + .args + .iter() + .enumerate() + .filter(|(i, _)| variances[*i] == ty::Invariant) + .filter_map(|(_, arg)| match arg.unpack() { + GenericArgKind::Lifetime(r) => Some(typeck.to_region_vid(r)), + GenericArgKind::Type(_) | GenericArgKind::Const(_) => None, + }) + .chain(iter::once(fr_static)) + .collect(); + + // FIXME(#42940): This should use the `FreeRegionsVisitor`, but that's + // not currently sound until we have existential regions. + hidden_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { + tcx, + op: |r| { + member_constraints.add_member_constraint( + opaque_type_key, + hidden_ty, + span, + typeck.to_region_vid(r), + &choice_regions, + ) + }, + }); +} + +/// Visitor that requires that (almost) all regions in the type visited outlive +/// `least_region`. We cannot use `push_outlives_components` because regions in +/// closure signatures are not included in their outlives components. We need to +/// ensure all regions outlive the given bound so that we don't end up with, +/// say, `ReVar` appearing in a return type and causing ICEs when other +/// functions end up with region constraints involving regions from other +/// functions. +/// +/// We also cannot use `for_each_free_region` because for closures it includes +/// the regions parameters from the enclosing item. +/// +/// We ignore any type parameters because impl trait values are assumed to +/// capture all the in-scope type parameters. +struct ConstrainOpaqueTypeRegionVisitor<'tcx, OP: FnMut(ty::Region<'tcx>)> { + tcx: TyCtxt<'tcx>, + op: OP, +} + +impl<'tcx, OP> TypeVisitor<TyCtxt<'tcx>> for ConstrainOpaqueTypeRegionVisitor<'tcx, OP> +where + OP: FnMut(ty::Region<'tcx>), +{ + fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) { + t.super_visit_with(self); + } + + fn visit_region(&mut self, r: ty::Region<'tcx>) { + match *r { + // ignore bound regions, keep visiting + ty::ReBound(_, _) => {} + _ => (self.op)(r), + } + } + + fn visit_ty(&mut self, ty: Ty<'tcx>) { + // We're only interested in types involving regions + if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) { + return; + } + + match ty.kind() { + ty::Closure(_, args) => { + // Skip lifetime parameters of the enclosing item(s) + + for upvar in args.as_closure().upvar_tys() { + upvar.visit_with(self); + } + args.as_closure().sig_as_fn_ptr_ty().visit_with(self); + } + + ty::CoroutineClosure(_, args) => { + // Skip lifetime parameters of the enclosing item(s) + + for upvar in args.as_coroutine_closure().upvar_tys() { + upvar.visit_with(self); + } + + args.as_coroutine_closure().signature_parts_ty().visit_with(self); + } + + ty::Coroutine(_, args) => { + // Skip lifetime parameters of the enclosing item(s) + // Also skip the witness type, because that has no free regions. + + for upvar in args.as_coroutine().upvar_tys() { + upvar.visit_with(self); + } + args.as_coroutine().return_ty().visit_with(self); + args.as_coroutine().yield_ty().visit_with(self); + args.as_coroutine().resume_ty().visit_with(self); + } + + ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { + // Skip lifetime parameters that are not captures. + let variances = self.tcx.variances_of(*def_id); + + for (v, s) in std::iter::zip(variances, args.iter()) { + if *v != ty::Bivariant { + s.visit_with(self); + } + } + } + + _ => { + ty.super_visit_with(self); + } + } + } +} diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs index 752b2bf1a24..59e2eee41d3 100644 --- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs +++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs @@ -13,8 +13,7 @@ use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::fold::FnMutDelegate; use rustc_middle::ty::relate::combine::{super_combine_consts, super_combine_tys}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; -use rustc_span::symbol::sym; -use rustc_span::{Span, Symbol}; +use rustc_span::{Span, Symbol, sym}; use tracing::{debug, instrument}; use crate::constraints::OutlivesConstraint; diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index bb64d646ff3..1ac45cbea38 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -33,8 +33,7 @@ use rustc_middle::ty::{ TyCtxt, TypeVisitableExt, }; use rustc_middle::{bug, span_bug}; -use rustc_span::ErrorGuaranteed; -use rustc_span::symbol::{kw, sym}; +use rustc_span::{ErrorGuaranteed, kw, sym}; use tracing::{debug, instrument}; use crate::BorrowckInferCtxt; @@ -843,8 +842,9 @@ impl<'tcx> BorrowckInferCtxt<'tcx> { { let (value, _map) = self.tcx.instantiate_bound_regions(value, |br| { debug!(?br); + let kind = ty::LateParamRegionKind::from_bound(br.var, br.kind); let liberated_region = - ty::Region::new_late_param(self.tcx, all_outlive_scope.to_def_id(), br.kind); + ty::Region::new_late_param(self.tcx, all_outlive_scope.to_def_id(), kind); let region_vid = { let name = match br.kind.get_name() { Some(name) => name, @@ -881,6 +881,10 @@ impl<'tcx> UniversalRegionIndices<'tcx> { /// reference those regions from the `ParamEnv`. It is also used /// during initialization. Relies on the `indices` map having been /// fully initialized. + /// + /// Panics if `r` is not a registered universal region, most notably + /// if it is a placeholder. Handling placeholders requires access to the + /// `MirTypeckRegionConstraints`. fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid { if let ty::ReVar(..) = *r { r.as_var() @@ -942,12 +946,13 @@ fn for_each_late_bound_region_in_item<'tcx>( return; } - for bound_var in tcx.late_bound_vars(tcx.local_def_id_to_hir_id(mir_def_id)) { - let ty::BoundVariableKind::Region(bound_region) = bound_var else { - continue; - }; - let liberated_region = - ty::Region::new_late_param(tcx, mir_def_id.to_def_id(), bound_region); - f(liberated_region); + for (idx, bound_var) in + tcx.late_bound_vars(tcx.local_def_id_to_hir_id(mir_def_id)).iter().enumerate() + { + if let ty::BoundVariableKind::Region(kind) = bound_var { + let kind = ty::LateParamRegionKind::from_bound(ty::BoundVar::from_usize(idx), kind); + let liberated_region = ty::Region::new_late_param(tcx, mir_def_id.to_def_id(), kind); + f(liberated_region); + } } } diff --git a/compiler/rustc_builtin_macros/Cargo.toml b/compiler/rustc_builtin_macros/Cargo.toml index ef48486f6f1..b50cb35b8e9 100644 --- a/compiler/rustc_builtin_macros/Cargo.toml +++ b/compiler/rustc_builtin_macros/Cargo.toml @@ -14,7 +14,7 @@ doctest = false # tidy-alphabetical-start rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_expand = { path = "../rustc_expand" } diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 87d3d288013..7a31d2a2239 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -249,9 +249,9 @@ builtin_macros_naked_functions_testing_attribute = .label = function marked with testing attribute here .naked_attribute = `#[naked]` is incompatible with testing attributes -builtin_macros_no_default_variant = no default declared - .help = make a unit variant default by placing `#[default]` above it - .suggestion = make `{$ident}` default +builtin_macros_no_default_variant = `#[derive(Default)]` on enum with no `#[default]` + .label = this enum needs a unit variant marked with `#[default]` + .suggestion = make this unit variant default by placing `#[default]` on it builtin_macros_non_abi = at least one abi must be provided as an argument to `clobber_abi` diff --git a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs index 0caad997b9d..d2b4e1ca824 100644 --- a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs +++ b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs @@ -3,8 +3,7 @@ use rustc_ast::{ self as ast, Fn, FnHeader, FnSig, Generics, ItemKind, Safety, Stmt, StmtKind, TyKind, }; use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_span::Span; -use rustc_span::symbol::{Ident, kw, sym}; +use rustc_span::{Ident, Span, kw, sym}; use thin_vec::{ThinVec, thin_vec}; use crate::errors; diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 14ac3cd74e8..6ae697d4030 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -1,17 +1,16 @@ use ast::token::IdentIsRaw; use lint::BuiltinLintDiag; -use rustc_ast::AsmMacro; use rustc_ast::ptr::P; -use rustc_ast::token::{self, Delimiter}; use rustc_ast::tokenstream::TokenStream; +use rustc_ast::{AsmMacro, token}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::PResult; use rustc_expand::base::*; use rustc_index::bit_set::GrowableBitSet; -use rustc_parse::parser::Parser; +use rustc_parse::exp; +use rustc_parse::parser::{ExpKeywordPair, Parser}; use rustc_session::lint; -use rustc_span::symbol::{Ident, Symbol, kw, sym}; -use rustc_span::{ErrorGuaranteed, InnerSpan, Span}; +use rustc_span::{ErrorGuaranteed, Ident, InnerSpan, Span, Symbol, kw}; use rustc_target::asm::InlineAsmArch; use smallvec::smallvec; use {rustc_ast as ast, rustc_parse_format as parse}; @@ -39,16 +38,16 @@ pub struct AsmArgs { /// - `Err(_)` if the current token matches the keyword, but was not expected fn eat_operand_keyword<'a>( p: &mut Parser<'a>, - symbol: Symbol, + exp: ExpKeywordPair, asm_macro: AsmMacro, ) -> PResult<'a, bool> { if matches!(asm_macro, AsmMacro::Asm) { - Ok(p.eat_keyword(symbol)) + Ok(p.eat_keyword(exp)) } else { let span = p.token.span; - if p.eat_keyword_noexpect(symbol) { + if p.eat_keyword_noexpect(exp.kw) { // in gets printed as `r#in` otherwise - let symbol = if symbol == kw::In { "in" } else { symbol.as_str() }; + let symbol = if exp.kw == kw::In { "in" } else { exp.kw.as_str() }; Err(p.dcx().create_err(errors::AsmUnsupportedOperand { span, symbol, @@ -96,13 +95,13 @@ pub fn parse_asm_args<'a>( let mut allow_templates = true; while p.token != token::Eof { - if !p.eat(&token::Comma) { + if !p.eat(exp!(Comma)) { if allow_templates { // After a template string, we always expect *only* a comma... return Err(dcx.create_err(errors::AsmExpectedComma { span: p.token.span })); } else { // ...after that delegate to `expect` to also include the other expected tokens. - return Err(p.expect(&token::Comma).err().unwrap()); + return Err(p.expect(exp!(Comma)).err().unwrap()); } } if p.token == token::Eof { @@ -110,14 +109,14 @@ pub fn parse_asm_args<'a>( } // accept trailing commas // Parse clobber_abi - if p.eat_keyword(sym::clobber_abi) { + if p.eat_keyword(exp!(ClobberAbi)) { parse_clobber_abi(p, &mut args)?; allow_templates = false; continue; } // Parse options - if p.eat_keyword(sym::options) { + if p.eat_keyword(exp!(Options)) { parse_options(p, &mut args, asm_macro)?; allow_templates = false; continue; @@ -129,7 +128,7 @@ pub fn parse_asm_args<'a>( let name = if p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq) { let (ident, _) = p.token.ident().unwrap(); p.bump(); - p.expect(&token::Eq)?; + p.expect(exp!(Eq))?; allow_templates = false; Some(ident.name) } else { @@ -137,57 +136,57 @@ pub fn parse_asm_args<'a>( }; let mut explicit_reg = false; - let op = if eat_operand_keyword(p, kw::In, asm_macro)? { + let op = if eat_operand_keyword(p, exp!(In), asm_macro)? { let reg = parse_reg(p, &mut explicit_reg)?; - if p.eat_keyword(kw::Underscore) { + if p.eat_keyword(exp!(Underscore)) { let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); return Err(err); } let expr = p.parse_expr()?; ast::InlineAsmOperand::In { reg, expr } - } else if eat_operand_keyword(p, sym::out, asm_macro)? { + } else if eat_operand_keyword(p, exp!(Out), asm_macro)? { let reg = parse_reg(p, &mut explicit_reg)?; - let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) }; + let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; ast::InlineAsmOperand::Out { reg, expr, late: false } - } else if eat_operand_keyword(p, sym::lateout, asm_macro)? { + } else if eat_operand_keyword(p, exp!(Lateout), asm_macro)? { let reg = parse_reg(p, &mut explicit_reg)?; - let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) }; + let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; ast::InlineAsmOperand::Out { reg, expr, late: true } - } else if eat_operand_keyword(p, sym::inout, asm_macro)? { + } else if eat_operand_keyword(p, exp!(Inout), asm_macro)? { let reg = parse_reg(p, &mut explicit_reg)?; - if p.eat_keyword(kw::Underscore) { + if p.eat_keyword(exp!(Underscore)) { let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); return Err(err); } let expr = p.parse_expr()?; - if p.eat(&token::FatArrow) { + if p.eat(exp!(FatArrow)) { let out_expr = - if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) }; + if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: false } } else { ast::InlineAsmOperand::InOut { reg, expr, late: false } } - } else if eat_operand_keyword(p, sym::inlateout, asm_macro)? { + } else if eat_operand_keyword(p, exp!(Inlateout), asm_macro)? { let reg = parse_reg(p, &mut explicit_reg)?; - if p.eat_keyword(kw::Underscore) { + if p.eat_keyword(exp!(Underscore)) { let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); return Err(err); } let expr = p.parse_expr()?; - if p.eat(&token::FatArrow) { + if p.eat(exp!(FatArrow)) { let out_expr = - if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) }; + if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: true } } else { ast::InlineAsmOperand::InOut { reg, expr, late: true } } - } else if eat_operand_keyword(p, sym::label, asm_macro)? { + } else if eat_operand_keyword(p, exp!(Label), asm_macro)? { let block = p.parse_block()?; ast::InlineAsmOperand::Label { block } - } else if p.eat_keyword(kw::Const) { + } else if p.eat_keyword(exp!(Const)) { let anon_const = p.parse_expr_anon_const()?; ast::InlineAsmOperand::Const { anon_const } - } else if p.eat_keyword(sym::sym) { + } else if p.eat_keyword(exp!(Sym)) { let expr = p.parse_expr()?; let ast::ExprKind::Path(qself, path) = &expr.kind else { let err = dcx.create_err(errors::AsmSymNoPath { span: expr.span }); @@ -390,31 +389,31 @@ fn parse_options<'a>( ) -> PResult<'a, ()> { let span_start = p.prev_token.span; - p.expect(&token::OpenDelim(Delimiter::Parenthesis))?; - - while !p.eat(&token::CloseDelim(Delimiter::Parenthesis)) { - const OPTIONS: [(Symbol, ast::InlineAsmOptions); ast::InlineAsmOptions::COUNT] = [ - (sym::pure, ast::InlineAsmOptions::PURE), - (sym::nomem, ast::InlineAsmOptions::NOMEM), - (sym::readonly, ast::InlineAsmOptions::READONLY), - (sym::preserves_flags, ast::InlineAsmOptions::PRESERVES_FLAGS), - (sym::noreturn, ast::InlineAsmOptions::NORETURN), - (sym::nostack, ast::InlineAsmOptions::NOSTACK), - (sym::may_unwind, ast::InlineAsmOptions::MAY_UNWIND), - (sym::att_syntax, ast::InlineAsmOptions::ATT_SYNTAX), - (kw::Raw, ast::InlineAsmOptions::RAW), + p.expect(exp!(OpenParen))?; + + while !p.eat(exp!(CloseParen)) { + const OPTIONS: [(ExpKeywordPair, ast::InlineAsmOptions); ast::InlineAsmOptions::COUNT] = [ + (exp!(Pure), ast::InlineAsmOptions::PURE), + (exp!(Nomem), ast::InlineAsmOptions::NOMEM), + (exp!(Readonly), ast::InlineAsmOptions::READONLY), + (exp!(PreservesFlags), ast::InlineAsmOptions::PRESERVES_FLAGS), + (exp!(Noreturn), ast::InlineAsmOptions::NORETURN), + (exp!(Nostack), ast::InlineAsmOptions::NOSTACK), + (exp!(MayUnwind), ast::InlineAsmOptions::MAY_UNWIND), + (exp!(AttSyntax), ast::InlineAsmOptions::ATT_SYNTAX), + (exp!(Raw), ast::InlineAsmOptions::RAW), ]; 'blk: { - for (symbol, option) in OPTIONS { + for (exp, option) in OPTIONS { let kw_matched = if asm_macro.is_supported_option(option) { - p.eat_keyword(symbol) + p.eat_keyword(exp) } else { - p.eat_keyword_noexpect(symbol) + p.eat_keyword_noexpect(exp.kw) }; if kw_matched { - try_set_option(p, args, asm_macro, symbol, option); + try_set_option(p, args, asm_macro, exp.kw, option); break 'blk; } } @@ -423,10 +422,10 @@ fn parse_options<'a>( } // Allow trailing commas - if p.eat(&token::CloseDelim(Delimiter::Parenthesis)) { + if p.eat(exp!(CloseParen)) { break; } - p.expect(&token::Comma)?; + p.expect(exp!(Comma))?; } let new_span = span_start.to(p.prev_token.span); @@ -438,14 +437,14 @@ fn parse_options<'a>( fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a, ()> { let span_start = p.prev_token.span; - p.expect(&token::OpenDelim(Delimiter::Parenthesis))?; + p.expect(exp!(OpenParen))?; - if p.eat(&token::CloseDelim(Delimiter::Parenthesis)) { + if p.eat(exp!(CloseParen)) { return Err(p.dcx().create_err(errors::NonABI { span: p.token.span })); } let mut new_abis = Vec::new(); - while !p.eat(&token::CloseDelim(Delimiter::Parenthesis)) { + while !p.eat(exp!(CloseParen)) { match p.parse_str_lit() { Ok(str_lit) => { new_abis.push((str_lit.symbol_unescaped, str_lit.span)); @@ -457,10 +456,10 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a, }; // Allow trailing commas - if p.eat(&token::CloseDelim(Delimiter::Parenthesis)) { + if p.eat(exp!(CloseParen)) { break; } - p.expect(&token::Comma)?; + p.expect(exp!(Comma))?; } let full_span = span_start.to(p.prev_token.span); @@ -483,7 +482,7 @@ fn parse_reg<'a>( p: &mut Parser<'a>, explicit_reg: &mut bool, ) -> PResult<'a, ast::InlineAsmRegOrRegClass> { - p.expect(&token::OpenDelim(Delimiter::Parenthesis))?; + p.expect(exp!(OpenParen))?; let result = match p.token.uninterpolate().kind { token::Ident(name, IdentIsRaw::No) => ast::InlineAsmRegOrRegClass::RegClass(name), token::Literal(token::Lit { kind: token::LitKind::Str, symbol, suffix: _ }) => { @@ -497,7 +496,7 @@ fn parse_reg<'a>( } }; p.bump(); - p.expect(&token::CloseDelim(Delimiter::Parenthesis))?; + p.expect(exp!(CloseParen))?; Ok(result) } diff --git a/compiler/rustc_builtin_macros/src/assert.rs b/compiler/rustc_builtin_macros/src/assert.rs index 599b180f879..c659b1cff59 100644 --- a/compiler/rustc_builtin_macros/src/assert.rs +++ b/compiler/rustc_builtin_macros/src/assert.rs @@ -7,9 +7,9 @@ use rustc_ast::{DelimArgs, Expr, ExprKind, MacCall, Path, PathSegment, UnOp, tok use rustc_ast_pretty::pprust; use rustc_errors::PResult; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult}; +use rustc_parse::exp; use rustc_parse::parser::Parser; -use rustc_span::symbol::{Ident, Symbol, sym}; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym}; use thin_vec::thin_vec; use crate::edition_panic::use_panic_2021; @@ -144,7 +144,7 @@ fn parse_assert<'a>(cx: &ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult< cx.dcx().emit_err(errors::AssertMissingComma { span: parser.token.span, comma }); parse_custom_message(&mut parser) - } else if parser.eat(&token::Comma) { + } else if parser.eat(exp!(Comma)) { parse_custom_message(&mut parser) } else { None diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs index eb07975d8af..bb9dc651cec 100644 --- a/compiler/rustc_builtin_macros/src/assert/context.rs +++ b/compiler/rustc_builtin_macros/src/assert/context.rs @@ -8,8 +8,7 @@ use rustc_ast::{ use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; use rustc_expand::base::ExtCtxt; -use rustc_span::Span; -use rustc_span::symbol::{Ident, Symbol, sym}; +use rustc_span::{Ident, Span, Symbol, sym}; use thin_vec::{ThinVec, thin_vec}; pub(super) struct Context<'cx, 'a> { diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs index 66bb11ca522..8960cf6ab59 100644 --- a/compiler/rustc_builtin_macros/src/autodiff.rs +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -20,8 +20,7 @@ mod llvm_enzyme { PatKind, TyKind, }; use rustc_expand::base::{Annotatable, ExtCtxt}; - use rustc_span::symbol::{Ident, kw, sym}; - use rustc_span::{Span, Symbol}; + use rustc_span::{Ident, Span, Symbol, kw, sym}; use thin_vec::{ThinVec, thin_vec}; use tracing::{debug, trace}; @@ -35,7 +34,7 @@ mod llvm_enzyme { FnRetTy::Default(_) => false, } } - fn first_ident(x: &MetaItemInner) -> rustc_span::symbol::Ident { + fn first_ident(x: &MetaItemInner) -> rustc_span::Ident { let segments = &x.meta_item().unwrap().path.segments; assert!(segments.len() == 1); segments[0].ident diff --git a/compiler/rustc_builtin_macros/src/cfg.rs b/compiler/rustc_builtin_macros/src/cfg.rs index 15993dbf5ec..85b8ef79c05 100644 --- a/compiler/rustc_builtin_macros/src/cfg.rs +++ b/compiler/rustc_builtin_macros/src/cfg.rs @@ -6,8 +6,9 @@ use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; use rustc_errors::PResult; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult}; +use rustc_parse::exp; use rustc_span::Span; -use {rustc_ast as ast, rustc_attr as attr}; +use {rustc_ast as ast, rustc_attr_parsing as attr}; use crate::errors; @@ -48,9 +49,9 @@ fn parse_cfg<'a>( let cfg = p.parse_meta_item_inner()?; - let _ = p.eat(&token::Comma); + let _ = p.eat(exp!(Comma)); - if !p.eat(&token::Eof) { + if !p.eat(exp!(Eof)) { return Err(cx.dcx().create_err(errors::OneCfgPattern { span })); } diff --git a/compiler/rustc_builtin_macros/src/cfg_accessible.rs b/compiler/rustc_builtin_macros/src/cfg_accessible.rs index 23578781a83..5f203dd5d11 100644 --- a/compiler/rustc_builtin_macros/src/cfg_accessible.rs +++ b/compiler/rustc_builtin_macros/src/cfg_accessible.rs @@ -4,8 +4,7 @@ use rustc_ast as ast; use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier}; use rustc_feature::AttributeTemplate; use rustc_parse::validate_attr; -use rustc_span::Span; -use rustc_span::symbol::sym; +use rustc_span::{Span, sym}; use crate::errors; diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index d46a1bd3d31..53c61831b42 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -12,8 +12,7 @@ use rustc_expand::configure; use rustc_feature::Features; use rustc_parse::parser::{ForceCollect, Parser}; use rustc_session::Session; -use rustc_span::Span; -use rustc_span::symbol::sym; +use rustc_span::{Span, sym}; use smallvec::SmallVec; use tracing::instrument; diff --git a/compiler/rustc_builtin_macros/src/concat.rs b/compiler/rustc_builtin_macros/src/concat.rs index a28801f66dd..c200539e128 100644 --- a/compiler/rustc_builtin_macros/src/concat.rs +++ b/compiler/rustc_builtin_macros/src/concat.rs @@ -2,7 +2,7 @@ use rustc_ast::tokenstream::TokenStream; use rustc_ast::{ExprKind, LitKind, UnOp}; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult}; use rustc_session::errors::report_lit_error; -use rustc_span::symbol::Symbol; +use rustc_span::Symbol; use crate::errors; use crate::util::get_exprs_from_tts; diff --git a/compiler/rustc_builtin_macros/src/concat_idents.rs b/compiler/rustc_builtin_macros/src/concat_idents.rs index b459cc3007d..a721f5b84c5 100644 --- a/compiler/rustc_builtin_macros/src/concat_idents.rs +++ b/compiler/rustc_builtin_macros/src/concat_idents.rs @@ -3,8 +3,7 @@ use rustc_ast::token::{self, Token}; use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_ast::{AttrVec, DUMMY_NODE_ID, Expr, ExprKind, Path, Ty, TyKind}; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacResult, MacroExpanderResult}; -use rustc_span::Span; -use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::{Ident, Span, Symbol}; use crate::errors; @@ -19,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/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs index 60450d085f6..2653a9f48b9 100644 --- a/compiler/rustc_builtin_macros/src/derive.rs +++ b/compiler/rustc_builtin_macros/src/derive.rs @@ -6,8 +6,7 @@ use rustc_expand::base::{ use rustc_feature::AttributeTemplate; use rustc_parse::validate_attr; use rustc_session::Session; -use rustc_span::symbol::{Ident, sym}; -use rustc_span::{ErrorGuaranteed, Span}; +use rustc_span::{ErrorGuaranteed, Ident, Span, sym}; use crate::cfg_eval::cfg_eval; use crate::errors; diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs index f79227d52a8..78f50ba8d29 100644 --- a/compiler/rustc_builtin_macros/src/deriving/clone.rs +++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs @@ -1,8 +1,7 @@ use rustc_ast::{self as ast, Generics, ItemKind, MetaItem, VariantData}; use rustc_data_structures::fx::FxHashSet; use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_span::Span; -use rustc_span::symbol::{Ident, kw, sym}; +use rustc_span::{Ident, Span, kw, sym}; use thin_vec::{ThinVec, thin_vec}; use crate::deriving::generic::ty::*; diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs index bd6f4eb8d99..5790350203a 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs @@ -1,8 +1,7 @@ use rustc_ast::{self as ast, MetaItem}; use rustc_data_structures::fx::FxHashSet; use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_span::Span; -use rustc_span::symbol::sym; +use rustc_span::{Span, sym}; use thin_vec::{ThinVec, thin_vec}; use crate::deriving::generic::ty::*; diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs index 433cbfaa6cb..1ed44c20bc6 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs @@ -1,7 +1,6 @@ use rustc_ast::MetaItem; use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_span::Span; -use rustc_span::symbol::{Ident, sym}; +use rustc_span::{Ident, Span, sym}; use thin_vec::thin_vec; use crate::deriving::generic::ty::*; diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs index ae1e5f4a219..4b93b3414c7 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs @@ -1,8 +1,7 @@ use rustc_ast::ptr::P; use rustc_ast::{BinOpKind, BorrowKind, Expr, ExprKind, MetaItem, Mutability}; use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_span::Span; -use rustc_span::symbol::sym; +use rustc_span::{Span, sym}; use thin_vec::thin_vec; use crate::deriving::generic::ty::*; diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs index 99a98325053..7958e037555 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs @@ -1,7 +1,6 @@ use rustc_ast::{ExprKind, ItemKind, MetaItem, PatKind}; use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_span::Span; -use rustc_span::symbol::{Ident, sym}; +use rustc_span::{Ident, Span, sym}; use thin_vec::thin_vec; use crate::deriving::generic::ty::*; diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs index 3bd8f899a4a..49706db0e0b 100644 --- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs +++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs @@ -6,12 +6,11 @@ use rustc_ast::{ self as ast, GenericArg, GenericBound, GenericParamKind, ItemKind, MetaItem, TraitBoundModifiers, VariantData, WherePredicate, }; -use rustc_attr as attr; +use rustc_attr_parsing as attr; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_macros::Diagnostic; -use rustc_span::symbol::{Ident, sym}; -use rustc_span::{Span, Symbol}; +use rustc_span::{Ident, Span, Symbol, sym}; use thin_vec::{ThinVec, thin_vec}; use crate::errors; diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs index 06a598cf3b0..eb01ca3941d 100644 --- a/compiler/rustc_builtin_macros/src/deriving/debug.rs +++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs @@ -1,8 +1,7 @@ use rustc_ast::{self as ast, EnumDef, MetaItem}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_session::config::FmtDebug; -use rustc_span::Span; -use rustc_span::symbol::{Ident, Symbol, sym}; +use rustc_span::{Ident, Span, Symbol, sym}; use thin_vec::{ThinVec, thin_vec}; use crate::deriving::generic::ty::*; diff --git a/compiler/rustc_builtin_macros/src/deriving/decodable.rs b/compiler/rustc_builtin_macros/src/deriving/decodable.rs index 469092e7b1c..6348560496e 100644 --- a/compiler/rustc_builtin_macros/src/deriving/decodable.rs +++ b/compiler/rustc_builtin_macros/src/deriving/decodable.rs @@ -3,8 +3,7 @@ use rustc_ast::ptr::P; use rustc_ast::{self as ast, Expr, MetaItem, Mutability}; use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_span::Span; -use rustc_span::symbol::{Ident, Symbol, sym}; +use rustc_span::{Ident, Span, Symbol, sym}; use thin_vec::{ThinVec, thin_vec}; use crate::deriving::generic::ty::*; diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs index 6b1a6effad7..3c7bebd0f19 100644 --- a/compiler/rustc_builtin_macros/src/deriving/default.rs +++ b/compiler/rustc_builtin_macros/src/deriving/default.rs @@ -4,8 +4,7 @@ use rustc_ast as ast; use rustc_ast::visit::visit_opt; use rustc_ast::{EnumDef, VariantData, attr}; use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt}; -use rustc_span::symbol::{Ident, kw, sym}; -use rustc_span::{ErrorGuaranteed, Span}; +use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym}; use smallvec::SmallVec; use thin_vec::{ThinVec, thin_vec}; @@ -43,7 +42,9 @@ pub(crate) fn expand_deriving_default( StaticStruct(_, fields) => { default_struct_substructure(cx, trait_span, substr, fields) } - StaticEnum(enum_def, _) => default_enum_substructure(cx, trait_span, enum_def), + StaticEnum(enum_def, _) => { + default_enum_substructure(cx, trait_span, enum_def, item.span()) + } _ => cx.dcx().span_bug(trait_span, "method in `derive(Default)`"), } })), @@ -97,9 +98,10 @@ fn default_enum_substructure( cx: &ExtCtxt<'_>, trait_span: Span, enum_def: &EnumDef, + item_span: Span, ) -> BlockOrExpr { let expr = match try { - let default_variant = extract_default_variant(cx, enum_def, trait_span)?; + let default_variant = extract_default_variant(cx, enum_def, trait_span, item_span)?; validate_default_attribute(cx, default_variant)?; default_variant } { @@ -147,6 +149,7 @@ fn extract_default_variant<'a>( cx: &ExtCtxt<'_>, enum_def: &'a EnumDef, trait_span: Span, + item_span: Span, ) -> Result<&'a rustc_ast::Variant, ErrorGuaranteed> { let default_variants: SmallVec<[_; 1]> = enum_def .variants @@ -164,9 +167,10 @@ fn extract_default_variant<'a>( .filter(|variant| !attr::contains_name(&variant.attrs, sym::non_exhaustive)); let suggs = possible_defaults - .map(|v| errors::NoDefaultVariantSugg { span: v.span, ident: v.ident }) + .map(|v| errors::NoDefaultVariantSugg { span: v.span.shrink_to_lo() }) .collect(); - let guar = cx.dcx().emit_err(errors::NoDefaultVariant { span: trait_span, suggs }); + let guar = + cx.dcx().emit_err(errors::NoDefaultVariant { span: trait_span, item_span, suggs }); return Err(guar); } diff --git a/compiler/rustc_builtin_macros/src/deriving/encodable.rs b/compiler/rustc_builtin_macros/src/deriving/encodable.rs index 5c7583f2a77..20aacb2caca 100644 --- a/compiler/rustc_builtin_macros/src/deriving/encodable.rs +++ b/compiler/rustc_builtin_macros/src/deriving/encodable.rs @@ -87,8 +87,7 @@ use rustc_ast::{AttrVec, ExprKind, MetaItem, Mutability}; use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_span::Span; -use rustc_span::symbol::{Ident, Symbol, sym}; +use rustc_span::{Ident, Span, Symbol, sym}; use thin_vec::{ThinVec, thin_vec}; use crate::deriving::generic::ty::*; diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 846d8784dea..f0a5e44e066 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -185,10 +185,9 @@ use rustc_ast::{ self as ast, AnonConst, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind, Generics, Mutability, PatKind, VariantData, }; -use rustc_attr as attr; +use rustc_attr_parsing as attr; use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_span::symbol::{Ident, Symbol, kw, sym}; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use thin_vec::{ThinVec, thin_vec}; use ty::{Bounds, Path, Ref, Self_, Ty}; diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs b/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs index b118cbfbd91..af6dc62db7a 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs @@ -6,8 +6,7 @@ use rustc_ast::ptr::P; use rustc_ast::{self as ast, Expr, GenericArg, GenericParamKind, Generics, SelfKind}; use rustc_expand::base::ExtCtxt; use rustc_span::source_map::respan; -use rustc_span::symbol::{Ident, Symbol, kw}; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw}; use thin_vec::ThinVec; /// A path, e.g., `::std::option::Option::<i32>` (global). Has support diff --git a/compiler/rustc_builtin_macros/src/deriving/hash.rs b/compiler/rustc_builtin_macros/src/deriving/hash.rs index 2d1f5b70f77..6e6dbe19e4d 100644 --- a/compiler/rustc_builtin_macros/src/deriving/hash.rs +++ b/compiler/rustc_builtin_macros/src/deriving/hash.rs @@ -1,7 +1,6 @@ use rustc_ast::{MetaItem, Mutability}; use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_span::Span; -use rustc_span::symbol::sym; +use rustc_span::{Span, sym}; use thin_vec::thin_vec; use crate::deriving::generic::ty::*; diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs index 681fbd1651d..ec058b41313 100644 --- a/compiler/rustc_builtin_macros/src/deriving/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -4,8 +4,7 @@ use rustc_ast as ast; use rustc_ast::ptr::P; use rustc_ast::{GenericArg, MetaItem}; use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, MultiItemModifier}; -use rustc_span::Span; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::{Span, Symbol, sym}; use thin_vec::{ThinVec, thin_vec}; macro path_local($x:ident) { diff --git a/compiler/rustc_builtin_macros/src/edition_panic.rs b/compiler/rustc_builtin_macros/src/edition_panic.rs index c4164a7db9a..b39c9861fd6 100644 --- a/compiler/rustc_builtin_macros/src/edition_panic.rs +++ b/compiler/rustc_builtin_macros/src/edition_panic.rs @@ -3,9 +3,8 @@ use rustc_ast::token::Delimiter; use rustc_ast::tokenstream::{DelimSpan, TokenStream}; use rustc_ast::*; use rustc_expand::base::*; -use rustc_span::Span; use rustc_span::edition::Edition; -use rustc_span::symbol::sym; +use rustc_span::{Span, sym}; /// This expands to either /// - `$crate::panic::panic_2015!(...)` or diff --git a/compiler/rustc_builtin_macros/src/env.rs b/compiler/rustc_builtin_macros/src/env.rs index 43e2bf1796f..8831261759c 100644 --- a/compiler/rustc_builtin_macros/src/env.rs +++ b/compiler/rustc_builtin_macros/src/env.rs @@ -10,8 +10,7 @@ use rustc_ast::token::{self, LitKind}; use rustc_ast::tokenstream::TokenStream; use rustc_ast::{AstDeref, ExprKind, GenericArg, Mutability}; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult}; -use rustc_span::Span; -use rustc_span::symbol::{Ident, Symbol, kw, sym}; +use rustc_span::{Ident, Span, Symbol, kw, sym}; use thin_vec::thin_vec; use crate::errors; diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index c9bd3371e55..1abdfdb9c65 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -4,8 +4,7 @@ use rustc_errors::{ SubdiagMessageOp, Subdiagnostic, }; use rustc_macros::{Diagnostic, Subdiagnostic}; -use rustc_span::symbol::Ident; -use rustc_span::{Span, Symbol}; +use rustc_span::{Ident, Span, Symbol}; #[derive(Diagnostic)] #[diag(builtin_macros_requires_cfg_pattern)] @@ -370,26 +369,21 @@ pub(crate) struct DerivePathArgsValue { } #[derive(Diagnostic)] -#[diag(builtin_macros_no_default_variant)] -#[help] +#[diag(builtin_macros_no_default_variant, code = E0665)] pub(crate) struct NoDefaultVariant { #[primary_span] pub(crate) span: Span, + #[label] + pub(crate) item_span: Span, #[subdiagnostic] pub(crate) suggs: Vec<NoDefaultVariantSugg>, } #[derive(Subdiagnostic)] -#[suggestion( - builtin_macros_suggestion, - code = "#[default] {ident}", - applicability = "maybe-incorrect", - style = "tool-only" -)] +#[suggestion(builtin_macros_suggestion, code = "#[default] ", applicability = "maybe-incorrect")] pub(crate) struct NoDefaultVariantSugg { #[primary_span] pub(crate) span: Span, - pub(crate) ident: Ident, } #[derive(Diagnostic)] diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 32730ac3867..528eb7725f5 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -12,9 +12,9 @@ use rustc_errors::{Applicability, Diag, MultiSpan, PResult, SingleLabelManySpans use rustc_expand::base::*; use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY; use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiag, LintId}; +use rustc_parse::exp; use rustc_parse_format as parse; -use rustc_span::symbol::{Ident, Symbol}; -use rustc_span::{BytePos, ErrorGuaranteed, InnerSpan, Span}; +use rustc_span::{BytePos, ErrorGuaranteed, Ident, InnerSpan, Span, Symbol}; use crate::errors; use crate::util::expr_to_spanned_string; @@ -94,12 +94,12 @@ fn parse_args<'a>(ecx: &ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<'a, let mut first = true; while p.token != token::Eof { - if !p.eat(&token::Comma) { + if !p.eat(exp!(Comma)) { if first { - p.clear_expected_tokens(); + p.clear_expected_token_types(); } - match p.expect(&token::Comma) { + match p.expect(exp!(Comma)) { Err(err) => { match token::TokenKind::Comma.similar_tokens() { Some(tks) if tks.contains(&p.token.kind) => { @@ -123,7 +123,7 @@ fn parse_args<'a>(ecx: &ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<'a, match p.token.ident() { Some((ident, _)) if p.look_ahead(1, |t| *t == token::Eq) => { p.bump(); - p.expect(&token::Eq)?; + p.expect(exp!(Eq))?; let expr = p.parse_expr()?; if let Some((_, prev)) = args.by_name(ident.name) { ecx.dcx().emit_err(errors::FormatDuplicateArg { diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index b4b18409a18..8388e9dcafb 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -7,8 +7,7 @@ use rustc_ast::{ Stmt, StmtKind, Ty, TyKind, }; use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_span::Span; -use rustc_span::symbol::{Ident, Symbol, kw, sym}; +use rustc_span::{Ident, Span, Symbol, kw, sym}; use thin_vec::{ThinVec, thin_vec}; use crate::errors; diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index cc4a974e757..6071d36f8eb 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -24,7 +24,7 @@ extern crate proc_macro; use rustc_expand::base::{MacroExpanderFn, ResolverExpand, SyntaxExtensionKind}; use rustc_expand::proc_macro::BangProcMacro; -use rustc_span::symbol::sym; +use rustc_span::sym; use crate::deriving::*; diff --git a/compiler/rustc_builtin_macros/src/pattern_type.rs b/compiler/rustc_builtin_macros/src/pattern_type.rs index 90b5f097b32..a600a9f316a 100644 --- a/compiler/rustc_builtin_macros/src/pattern_type.rs +++ b/compiler/rustc_builtin_macros/src/pattern_type.rs @@ -3,7 +3,8 @@ use rustc_ast::tokenstream::TokenStream; use rustc_ast::{Pat, Ty, ast}; use rustc_errors::PResult; use rustc_expand::base::{self, DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult}; -use rustc_span::{Span, sym}; +use rustc_parse::exp; +use rustc_span::Span; pub(crate) fn expand<'cx>( cx: &'cx mut ExtCtxt<'_>, @@ -24,7 +25,7 @@ fn parse_pat_ty<'a>(cx: &mut ExtCtxt<'a>, stream: TokenStream) -> PResult<'a, (P let mut parser = cx.new_parser_from_tts(stream); let ty = parser.parse_ty()?; - parser.expect_keyword(sym::is)?; + parser.expect_keyword(exp!(Is))?; let pat = parser.parse_pat_no_top_alt(None, None)?; Ok((ty, pat)) diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs index 707c36d5046..dee185ff0c9 100644 --- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs +++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs @@ -11,8 +11,7 @@ use rustc_feature::Features; use rustc_session::Session; use rustc_span::hygiene::AstPass; use rustc_span::source_map::SourceMap; -use rustc_span::symbol::{Ident, Symbol, kw, sym}; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use smallvec::smallvec; use thin_vec::{ThinVec, thin_vec}; diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index 946fbe918e6..123b96f6bca 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -16,8 +16,7 @@ use rustc_parse::parser::{ForceCollect, Parser}; use rustc_parse::{new_parser_from_file, unwrap_or_emit_fatal}; use rustc_session::lint::builtin::INCOMPLETE_INCLUDE; use rustc_span::source_map::SourceMap; -use rustc_span::symbol::Symbol; -use rustc_span::{Pos, Span}; +use rustc_span::{Pos, Span, Symbol}; use smallvec::SmallVec; use crate::errors; diff --git a/compiler/rustc_builtin_macros/src/standard_library_imports.rs b/compiler/rustc_builtin_macros/src/standard_library_imports.rs index a3fc53344ab..1a3f4d2d449 100644 --- a/compiler/rustc_builtin_macros/src/standard_library_imports.rs +++ b/compiler/rustc_builtin_macros/src/standard_library_imports.rs @@ -3,10 +3,9 @@ use rustc_expand::base::{ExtCtxt, ResolverExpand}; use rustc_expand::expand::ExpansionConfig; use rustc_feature::Features; use rustc_session::Session; -use rustc_span::DUMMY_SP; use rustc_span::edition::Edition::*; use rustc_span::hygiene::AstPass; -use rustc_span::symbol::{Ident, Symbol, kw, sym}; +use rustc_span::{DUMMY_SP, Ident, Symbol, kw, sym}; use thin_vec::thin_vec; pub fn inject( diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index 664e935ee14..3f73ddbdd29 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -9,8 +9,7 @@ use rustc_ast::{self as ast, GenericParamKind, attr}; use rustc_ast_pretty::pprust; use rustc_errors::{Applicability, Diag, Level}; use rustc_expand::base::*; -use rustc_span::symbol::{Ident, Symbol, sym}; -use rustc_span::{ErrorGuaranteed, FileNameDisplayPreference, Span}; +use rustc_span::{ErrorGuaranteed, FileNameDisplayPreference, Ident, Span, Symbol, sym}; use thin_vec::{ThinVec, thin_vec}; use tracing::debug; diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index e7ff65e08f9..46446598943 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -16,8 +16,7 @@ use rustc_lint_defs::BuiltinLintDiag; use rustc_session::Session; use rustc_session::lint::builtin::UNNAMEABLE_TEST_ITEMS; use rustc_span::hygiene::{AstPass, SyntaxContext, Transparency}; -use rustc_span::symbol::{Ident, Symbol, sym}; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym}; use rustc_target::spec::PanicStrategy; use smallvec::smallvec; use thin_vec::{ThinVec, thin_vec}; diff --git a/compiler/rustc_builtin_macros/src/trace_macros.rs b/compiler/rustc_builtin_macros/src/trace_macros.rs index e624d1da66b..8264c17b4d1 100644 --- a/compiler/rustc_builtin_macros/src/trace_macros.rs +++ b/compiler/rustc_builtin_macros/src/trace_macros.rs @@ -1,7 +1,6 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult}; -use rustc_span::Span; -use rustc_span::symbol::kw; +use rustc_span::{Span, kw}; use crate::errors; @@ -10,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, _ => { @@ -20,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_builtin_macros/src/util.rs b/compiler/rustc_builtin_macros/src/util.rs index 2a28dfaf3c4..be12d21a800 100644 --- a/compiler/rustc_builtin_macros/src/util.rs +++ b/compiler/rustc_builtin_macros/src/util.rs @@ -7,7 +7,7 @@ use rustc_expand::expand::AstFragment; use rustc_feature::AttributeTemplate; use rustc_lint_defs::BuiltinLintDiag; use rustc_lint_defs::builtin::DUPLICATE_MACRO_ATTRIBUTES; -use rustc_parse::{parser, validate_attr}; +use rustc_parse::{exp, parser, validate_attr}; use rustc_session::errors::report_lit_error; use rustc_span::{BytePos, Span, Symbol}; @@ -204,7 +204,7 @@ pub(crate) fn get_single_expr_from_tts( Ok(ret) => ret, Err(guar) => return ExpandResult::Ready(Err(guar)), }; - let _ = p.eat(&token::Comma); + let _ = p.eat(exp!(Comma)); if p.token != token::Eof { cx.dcx().emit_err(errors::OnlyOneArgument { span, name }); @@ -237,7 +237,7 @@ pub(crate) fn get_exprs_from_tts( let expr = cx.expander().fully_expand_fragment(AstFragment::Expr(expr)).make_expr(); es.push(expr); - if p.eat(&token::Comma) { + if p.eat(exp!(Comma)) { continue; } if p.token != token::Eof { diff --git a/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs b/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs index 4154a62234c..f8e3a034421 100644 --- a/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs +++ b/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs @@ -3,7 +3,7 @@ use std::ffi::c_int; #[cfg(feature = "jit")] use std::ffi::c_void; -// FIXME replace with core::ffi::c_size_t once stablized +// FIXME replace with core::ffi::c_size_t once stabilized #[allow(non_camel_case_types)] #[cfg(feature = "jit")] type size_t = usize; diff --git a/compiler/rustc_codegen_cranelift/src/discriminant.rs b/compiler/rustc_codegen_cranelift/src/discriminant.rs index 45794a42665..4d0d5dc60eb 100644 --- a/compiler/rustc_codegen_cranelift/src/discriminant.rs +++ b/compiler/rustc_codegen_cranelift/src/discriminant.rs @@ -18,6 +18,7 @@ pub(crate) fn codegen_set_discriminant<'tcx>( return; } match layout.variants { + Variants::Empty => unreachable!("we already handled uninhabited types"), Variants::Single { index } => { assert_eq!(index, variant_index); } @@ -85,6 +86,7 @@ pub(crate) fn codegen_get_discriminant<'tcx>( } let (tag_scalar, tag_field, tag_encoding) = match &layout.variants { + Variants::Empty => unreachable!("we already handled uninhabited types"), Variants::Single { index } => { let discr_val = layout .ty diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs index 4be4291021d..eaab3362c7e 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs @@ -294,7 +294,7 @@ fn dep_symbol_lookup_fn( // search path. for &cnum in crate_info.used_crates.iter().rev() { let src = &crate_info.used_crate_source[&cnum]; - match data[cnum.as_usize() - 1] { + match data[cnum] { Linkage::NotLinked | Linkage::IncludedFromDylib => {} Linkage::Static => { let name = crate_info.crate_name[&cnum]; diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 5f1b71eff6b..2e5813556aa 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -23,7 +23,7 @@ use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; use rustc_span::source_map::Spanned; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::{Symbol, sym}; pub(crate) use self::llvm::codegen_llvm_intrinsic_call; use crate::cast::clif_intcast; diff --git a/compiler/rustc_codegen_cranelift/src/main_shim.rs b/compiler/rustc_codegen_cranelift/src/main_shim.rs index e480f21b9df..e6bf0d5b47e 100644 --- a/compiler/rustc_codegen_cranelift/src/main_shim.rs +++ b/compiler/rustc_codegen_cranelift/src/main_shim.rs @@ -2,8 +2,7 @@ use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use rustc_hir::LangItem; use rustc_middle::ty::{AssocKind, GenericArg}; use rustc_session::config::{EntryFnType, sigpipe}; -use rustc_span::DUMMY_SP; -use rustc_span::symbol::Ident; +use rustc_span::{DUMMY_SP, Ident}; use crate::prelude::*; diff --git a/compiler/rustc_codegen_gcc/src/attributes.rs b/compiler/rustc_codegen_gcc/src/attributes.rs index d20e13e15b9..028a5ab5f71 100644 --- a/compiler/rustc_codegen_gcc/src/attributes.rs +++ b/compiler/rustc_codegen_gcc/src/attributes.rs @@ -2,8 +2,8 @@ use gccjit::FnAttribute; use gccjit::Function; #[cfg(feature = "master")] -use rustc_attr::InlineAttr; -use rustc_attr::InstructionSetAttr; +use rustc_attr_parsing::InlineAttr; +use rustc_attr_parsing::InstructionSetAttr; #[cfg(feature = "master")] use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::ty; diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index 88e5eefd7a1..058a874501b 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -96,7 +96,7 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri } Some((_, stability, _)) => { if let Err(reason) = - stability.compute_toggleability(&sess.target).allow_toggle() + stability.toggle_allowed(&sess.target, enable_disable == '+') { sess.dcx().emit_warn(ForbiddenCTargetFeature { feature, reason }); } else if stability.requires_nightly().is_some() { diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 69326f409bb..78ec9741f57 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -66,7 +66,7 @@ fn get_simple_intrinsic<'gcc, 'tcx>( sym::log2f64 => "log2", sym::fmaf32 => "fmaf", sym::fmaf64 => "fma", - // FIXME: calling `fma` from libc without FMA target feature uses expensive sofware emulation + // FIXME: calling `fma` from libc without FMA target feature uses expensive software emulation sym::fmuladdf32 => "fmaf", // TODO: use gcc intrinsic analogous to llvm.fmuladd.f32 sym::fmuladdf64 => "fma", // TODO: use gcc intrinsic analogous to llvm.fmuladd.f64 sym::fabsf32 => "fabsf", diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 764e84be1fe..f2efa981f97 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -35,7 +35,7 @@ extern crate tracing; extern crate rustc_abi; extern crate rustc_apfloat; extern crate rustc_ast; -extern crate rustc_attr; +extern crate rustc_attr_parsing; extern crate rustc_codegen_ssa; extern crate rustc_data_structures; extern crate rustc_errors; @@ -483,9 +483,9 @@ fn target_features_cfg( .rust_target_features() .iter() .filter(|(_, gate, _)| gate.in_cfg()) - .filter_map(|&(feature, gate, _)| { + .filter_map(|(feature, gate, _)| { if sess.is_nightly_build() || allow_unstable || gate.requires_nightly().is_none() { - Some(feature) + Some(*feature) } else { None } diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index 03a871297c4..689986d642d 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -16,7 +16,7 @@ object = { version = "0.36.3", default-features = false, features = ["std", "rea rustc-demangle = "0.1.21" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_codegen_ssa = { path = "../rustc_codegen_ssa" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 5552a241060..f8454fd9960 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -1,6 +1,6 @@ //! Set and unset common attributes on LLVM values. -use rustc_attr::{InlineAttr, InstructionSetAttr, OptimizeAttr}; +use rustc_attr_parsing::{InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_codegen_ssa::traits::*; use rustc_hir::def_id::DefId; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, PatchableFunctionEntry}; diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 66ca4e2b473..45294ea35b1 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -25,8 +25,7 @@ use rustc_session::Session; use rustc_session::config::{ self, Lto, OutputType, Passes, RemapPathScopeComponents, SplitDwarfKind, SwitchWithOptPath, }; -use rustc_span::symbol::sym; -use rustc_span::{BytePos, InnerSpan, Pos, SpanData, SyntaxContext}; +use rustc_span::{BytePos, InnerSpan, Pos, SpanData, SyntaxContext, sym}; use rustc_target::spec::{CodeModel, RelocModel, SanitizerSet, SplitDebuginfo, TlsModel}; use tracing::debug; diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index f62310bd948..d05faf5577b 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -23,7 +23,7 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::mir::mono::{Linkage, Visibility}; use rustc_middle::ty::TyCtxt; use rustc_session::config::DebugInfo; -use rustc_span::symbol::Symbol; +use rustc_span::Symbol; use rustc_target::spec::SanitizerSet; use super::ModuleLlvm; diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs index ec77f32caf4..aa9a0f34f55 100644 --- a/compiler/rustc_codegen_llvm/src/callee.rs +++ b/compiler/rustc_codegen_llvm/src/callee.rs @@ -106,7 +106,7 @@ pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'t // This is a monomorphization of a generic function. if !(cx.tcx.sess.opts.share_generics() || tcx.codegen_fn_attrs(instance_def_id).inline - == rustc_attr::InlineAttr::Never) + == rustc_attr_parsing::InlineAttr::Never) { // When not sharing generics, all instances are in the same // crate and have hidden visibility. diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs index 1f133141c18..b617f4d37f5 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs @@ -1,6 +1,4 @@ -use rustc_middle::mir::coverage::{CounterId, CovTerm, ExpressionId, SourceRegion}; - -use crate::coverageinfo::mapgen::LocalFileId; +use rustc_middle::mir::coverage::{CounterId, CovTerm, ExpressionId}; /// Must match the layout of `LLVMRustCounterKind`. #[derive(Copy, Clone, Debug)] @@ -126,30 +124,16 @@ pub(crate) struct CoverageSpan { /// Local index into the function's local-to-global file ID table. /// The value at that index is itself an index into the coverage filename /// table in the CGU's `__llvm_covmap` section. - file_id: u32, + pub(crate) file_id: u32, /// 1-based starting line of the source code span. - start_line: u32, + pub(crate) start_line: u32, /// 1-based starting column of the source code span. - start_col: u32, + pub(crate) start_col: u32, /// 1-based ending line of the source code span. - end_line: u32, + pub(crate) end_line: u32, /// 1-based ending column of the source code span. High bit must be unset. - end_col: u32, -} - -impl CoverageSpan { - pub(crate) fn from_source_region( - local_file_id: LocalFileId, - code_region: &SourceRegion, - ) -> Self { - let file_id = local_file_id.as_u32(); - let &SourceRegion { start_line, start_col, end_line, end_col } = code_region; - // Internally, LLVM uses the high bit of `end_col` to distinguish between - // code regions and gap regions, so it can't be used by the column number. - assert!(end_col & (1u32 << 31) == 0, "high bit of `end_col` must be unset: {end_col:#X}"); - Self { file_id, start_line, start_col, end_line, end_col } - } + pub(crate) end_col: u32, } /// Holds tables of the various region types in one struct. @@ -184,7 +168,7 @@ impl Regions { #[derive(Clone, Debug)] #[repr(C)] pub(crate) struct CodeRegion { - pub(crate) span: CoverageSpan, + pub(crate) cov_span: CoverageSpan, pub(crate) counter: Counter, } @@ -192,7 +176,7 @@ pub(crate) struct CodeRegion { #[derive(Clone, Debug)] #[repr(C)] pub(crate) struct BranchRegion { - pub(crate) span: CoverageSpan, + pub(crate) cov_span: CoverageSpan, pub(crate) true_counter: Counter, pub(crate) false_counter: Counter, } @@ -201,7 +185,7 @@ pub(crate) struct BranchRegion { #[derive(Clone, Debug)] #[repr(C)] pub(crate) struct MCDCBranchRegion { - pub(crate) span: CoverageSpan, + pub(crate) cov_span: CoverageSpan, pub(crate) true_counter: Counter, pub(crate) false_counter: Counter, pub(crate) mcdc_branch_params: mcdc::BranchParameters, @@ -211,6 +195,6 @@ pub(crate) struct MCDCBranchRegion { #[derive(Clone, Debug)] #[repr(C)] pub(crate) struct MCDCDecisionRegion { - pub(crate) span: CoverageSpan, + pub(crate) cov_span: CoverageSpan, pub(crate) mcdc_decision_params: mcdc::DecisionParameters, } diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs index 086cf1f44a0..2cd7fa3225a 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs @@ -40,11 +40,10 @@ pub(crate) fn create_pgo_func_name_var<'ll>( } } -pub(crate) fn write_filenames_to_buffer<'a>( - filenames: impl IntoIterator<Item = &'a str>, -) -> Vec<u8> { +pub(crate) fn write_filenames_to_buffer(filenames: &[impl AsRef<str>]) -> Vec<u8> { let (pointers, lengths) = filenames .into_iter() + .map(AsRef::as_ref) .map(|s: &str| (s.as_c_char_ptr(), s.len())) .unzip::<_, _, Vec<_>, Vec<_>>(); diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs deleted file mode 100644 index c5d1ebdfe7c..00000000000 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs +++ /dev/null @@ -1,84 +0,0 @@ -use rustc_data_structures::captures::Captures; -use rustc_middle::mir::coverage::{ - CovTerm, CoverageIdsInfo, Expression, FunctionCoverageInfo, Mapping, MappingKind, Op, - SourceRegion, -}; - -use crate::coverageinfo::ffi::{Counter, CounterExpression, ExprKind}; - -pub(crate) struct FunctionCoverage<'tcx> { - pub(crate) function_coverage_info: &'tcx FunctionCoverageInfo, - /// If `None`, the corresponding function is unused. - ids_info: Option<&'tcx CoverageIdsInfo>, -} - -impl<'tcx> FunctionCoverage<'tcx> { - pub(crate) fn new_used( - function_coverage_info: &'tcx FunctionCoverageInfo, - ids_info: &'tcx CoverageIdsInfo, - ) -> Self { - Self { function_coverage_info, ids_info: Some(ids_info) } - } - - pub(crate) fn new_unused(function_coverage_info: &'tcx FunctionCoverageInfo) -> Self { - Self { function_coverage_info, ids_info: None } - } - - /// Returns true for a used (called) function, and false for an unused function. - pub(crate) fn is_used(&self) -> bool { - self.ids_info.is_some() - } - - /// Return the source hash, generated from the HIR node structure, and used to indicate whether - /// or not the source code structure changed between different compilations. - pub(crate) fn source_hash(&self) -> u64 { - if self.is_used() { self.function_coverage_info.function_source_hash } else { 0 } - } - - /// Convert this function's coverage expression data into a form that can be - /// passed through FFI to LLVM. - pub(crate) fn counter_expressions( - &self, - ) -> impl Iterator<Item = CounterExpression> + ExactSizeIterator + Captures<'_> { - // We know that LLVM will optimize out any unused expressions before - // producing the final coverage map, so there's no need to do the same - // thing on the Rust side unless we're confident we can do much better. - // (See `CounterExpressionsMinimizer` in `CoverageMappingWriter.cpp`.) - - self.function_coverage_info.expressions.iter().map(move |&Expression { lhs, op, rhs }| { - CounterExpression { - lhs: self.counter_for_term(lhs), - kind: match op { - Op::Add => ExprKind::Add, - Op::Subtract => ExprKind::Subtract, - }, - rhs: self.counter_for_term(rhs), - } - }) - } - - /// Converts this function's coverage mappings into an intermediate form - /// that will be used by `mapgen` when preparing for FFI. - pub(crate) fn counter_regions( - &self, - ) -> impl Iterator<Item = (MappingKind, &SourceRegion)> + ExactSizeIterator { - self.function_coverage_info.mappings.iter().map(move |mapping| { - let Mapping { kind, source_region } = mapping; - let kind = - kind.map_terms(|term| if self.is_zero_term(term) { CovTerm::Zero } else { term }); - (kind, source_region) - }) - } - - fn counter_for_term(&self, term: CovTerm) -> Counter { - if self.is_zero_term(term) { Counter::ZERO } else { Counter::from_term(term) } - } - - fn is_zero_term(&self, term: CovTerm) -> bool { - match self.ids_info { - Some(ids_info) => ids_info.is_zero_term(term), - // This function is unused, so all coverage counters/expressions are zero. - None => true, - } - } -} diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index 4f2af732527..b3ad2a0e409 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -1,28 +1,28 @@ -use std::iter; +use std::sync::Arc; -use itertools::Itertools as _; +use itertools::Itertools; use rustc_abi::Align; use rustc_codegen_ssa::traits::{ BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods, }; -use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_index::IndexVec; +use rustc_middle::mir; use rustc_middle::ty::{self, TyCtxt}; -use rustc_middle::{bug, mir}; use rustc_session::RemapFileNameExt; use rustc_session::config::RemapPathScopeComponents; use rustc_span::def_id::DefIdSet; -use rustc_span::{Span, Symbol}; +use rustc_span::{SourceFile, StableSourceFileId}; use tracing::debug; use crate::common::CodegenCx; use crate::coverageinfo::llvm_cov; -use crate::coverageinfo::map_data::FunctionCoverage; use crate::coverageinfo::mapgen::covfun::prepare_covfun_record; use crate::llvm; mod covfun; +mod spans; /// Generates and exports the coverage map, which is embedded in special /// linker sections in the final binary. @@ -49,46 +49,40 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) { debug!("Generating coverage map for CodegenUnit: `{}`", cx.codegen_unit.name()); - // In order to show that unused functions have coverage counts of zero (0), LLVM requires the - // functions exist. Generate synthetic functions with a (required) single counter, and add the - // MIR `Coverage` code regions to the `function_coverage_map`, before calling - // `ctx.take_function_coverage_map()`. - if cx.codegen_unit.is_code_coverage_dead_code_cgu() { - add_unused_functions(cx); - } - // FIXME(#132395): Can this be none even when coverage is enabled? - let function_coverage_map = match cx.coverage_cx { - Some(ref cx) => cx.take_function_coverage_map(), + let instances_used = match cx.coverage_cx { + Some(ref cx) => cx.instances_used.borrow(), None => return, }; - if function_coverage_map.is_empty() { - // This CGU has no functions with coverage instrumentation. - return; - } - - let all_file_names = function_coverage_map - .iter() - .map(|(_, fn_cov)| fn_cov.function_coverage_info.body_span) - .map(|span| span_file_name(tcx, span)); - let global_file_table = GlobalFileTable::new(all_file_names); - - // Encode all filenames referenced by coverage mappings in this CGU. - let filenames_buffer = global_file_table.make_filenames_buffer(tcx); - // The `llvm-cov` tool uses this hash to associate each covfun record with - // its corresponding filenames table, since the final binary will typically - // contain multiple covmap records from different compilation units. - let filenames_hash = llvm_cov::hash_bytes(&filenames_buffer); - let mut unused_function_names = Vec::new(); + // The order of entries in this global file table needs to be deterministic, + // and ideally should also be independent of the details of stable-hashing, + // because coverage tests snapshots (`.cov-map`) can observe the order and + // would need to be re-blessed if it changes. As long as those requirements + // are satisfied, the order can be arbitrary. + let mut global_file_table = GlobalFileTable::new(); - let covfun_records = function_coverage_map - .into_iter() - .filter_map(|(instance, function_coverage)| { - prepare_covfun_record(tcx, &global_file_table, instance, &function_coverage) - }) + let mut covfun_records = instances_used + .iter() + .copied() + // Sort by symbol name, so that the global file table is built in an + // order that doesn't depend on the stable-hash-based order in which + // instances were visited during codegen. + .sorted_by_cached_key(|&instance| tcx.symbol_name(instance).name) + .filter_map(|instance| prepare_covfun_record(tcx, &mut global_file_table, instance, true)) .collect::<Vec<_>>(); + // In a single designated CGU, also prepare covfun records for functions + // in this crate that were instrumented for coverage, but are unused. + if cx.codegen_unit.is_code_coverage_dead_code_cgu() { + let mut unused_instances = gather_unused_function_instances(cx); + // Sort the unused instances by symbol name, for the same reason as the used ones. + unused_instances.sort_by_cached_key(|&instance| tcx.symbol_name(instance).name); + covfun_records.extend(unused_instances.into_iter().filter_map(|instance| { + prepare_covfun_record(tcx, &mut global_file_table, instance, false) + })); + } + // If there are no covfun records for this CGU, don't generate a covmap record. // Emitting a covmap record without any covfun records causes `llvm-cov` to // fail when generating coverage reports, and if there are no covfun records @@ -98,6 +92,15 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) { return; } + // Encode all filenames referenced by coverage mappings in this CGU. + let filenames_buffer = global_file_table.make_filenames_buffer(tcx); + // The `llvm-cov` tool uses this hash to associate each covfun record with + // its corresponding filenames table, since the final binary will typically + // contain multiple covmap records from different compilation units. + let filenames_hash = llvm_cov::hash_bytes(&filenames_buffer); + + let mut unused_function_names = vec![]; + for covfun in &covfun_records { unused_function_names.extend(covfun.mangled_function_name_if_unused()); @@ -129,54 +132,51 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) { generate_covmap_record(cx, covmap_version, &filenames_buffer); } -/// Maps "global" (per-CGU) file ID numbers to their underlying filenames. +/// Maps "global" (per-CGU) file ID numbers to their underlying source files. struct GlobalFileTable { - /// This "raw" table doesn't include the working dir, so a filename's + /// This "raw" table doesn't include the working dir, so a file's /// global ID is its index in this set **plus one**. - raw_file_table: FxIndexSet<Symbol>, + raw_file_table: FxIndexMap<StableSourceFileId, Arc<SourceFile>>, } impl GlobalFileTable { - fn new(all_file_names: impl IntoIterator<Item = Symbol>) -> Self { - // Collect all of the filenames into a set. Filenames usually come in - // contiguous runs, so we can dedup adjacent ones to save work. - let mut raw_file_table = all_file_names.into_iter().dedup().collect::<FxIndexSet<Symbol>>(); - - // Sort the file table by its actual string values, not the arbitrary - // ordering of its symbols. - raw_file_table.sort_unstable_by(|a, b| a.as_str().cmp(b.as_str())); - - Self { raw_file_table } + fn new() -> Self { + Self { raw_file_table: FxIndexMap::default() } } - fn global_file_id_for_file_name(&self, file_name: Symbol) -> GlobalFileId { - let raw_id = self.raw_file_table.get_index_of(&file_name).unwrap_or_else(|| { - bug!("file name not found in prepared global file table: {file_name}"); - }); + fn global_file_id_for_file(&mut self, file: &Arc<SourceFile>) -> GlobalFileId { + // Ensure the given file has a table entry, and get its index. + let entry = self.raw_file_table.entry(file.stable_id); + let raw_id = entry.index(); + entry.or_insert_with(|| Arc::clone(file)); + // The raw file table doesn't include an entry for the working dir // (which has ID 0), so add 1 to get the correct ID. GlobalFileId::from_usize(raw_id + 1) } fn make_filenames_buffer(&self, tcx: TyCtxt<'_>) -> Vec<u8> { + let mut table = Vec::with_capacity(self.raw_file_table.len() + 1); + // LLVM Coverage Mapping Format version 6 (zero-based encoded as 5) // requires setting the first filename to the compilation directory. // Since rustc generates coverage maps with relative paths, the // compilation directory can be combined with the relative paths // to get absolute paths, if needed. - use rustc_session::RemapFileNameExt; - use rustc_session::config::RemapPathScopeComponents; - let working_dir: &str = &tcx - .sess - .opts - .working_dir - .for_scope(tcx.sess, RemapPathScopeComponents::MACRO) - .to_string_lossy(); - - // Insert the working dir at index 0, before the other filenames. - let filenames = - iter::once(working_dir).chain(self.raw_file_table.iter().map(Symbol::as_str)); - llvm_cov::write_filenames_to_buffer(filenames) + table.push( + tcx.sess + .opts + .working_dir + .for_scope(tcx.sess, RemapPathScopeComponents::MACRO) + .to_string_lossy(), + ); + + // Add the regular entries after the base directory. + table.extend(self.raw_file_table.values().map(|file| { + file.name.for_scope(tcx.sess, RemapPathScopeComponents::MACRO).to_string_lossy() + })); + + llvm_cov::write_filenames_to_buffer(&table) } } @@ -189,7 +189,7 @@ rustc_index::newtype_index! { /// An index into a function's list of global file IDs. That underlying list /// of local-to-global mappings will be embedded in the function's record in /// the `__llvm_covfun` linker section. - pub(crate) struct LocalFileId {} + struct LocalFileId {} } /// Holds a mapping from "local" (per-function) file IDs to "global" (per-CGU) @@ -215,13 +215,6 @@ impl VirtualFileMapping { } } -fn span_file_name(tcx: TyCtxt<'_>, span: Span) -> Symbol { - let source_file = tcx.sess.source_map().lookup_source_file(span.lo()); - let name = - source_file.name.for_scope(tcx.sess, RemapPathScopeComponents::MACRO).to_string_lossy(); - Symbol::intern(&name) -} - /// Generates the contents of the covmap record for this CGU, which mostly /// consists of a header and a list of filenames. The record is then stored /// as a global variable in the `__llvm_covmap` section. @@ -264,39 +257,35 @@ fn generate_covmap_record<'ll>(cx: &CodegenCx<'ll, '_>, version: u32, filenames_ /// coverage map (in a single designated CGU) so that we still emit coverage mappings for them. /// We also end up adding their symbol names to a special global array that LLVM will include in /// its embedded coverage data. -fn add_unused_functions(cx: &CodegenCx<'_, '_>) { +fn gather_unused_function_instances<'tcx>(cx: &CodegenCx<'_, 'tcx>) -> Vec<ty::Instance<'tcx>> { assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu()); let tcx = cx.tcx; let usage = prepare_usage_sets(tcx); let is_unused_fn = |def_id: LocalDefId| -> bool { - let def_id = def_id.to_def_id(); - - // To be eligible for "unused function" mappings, a definition must: - // - Be function-like + // Usage sets expect `DefId`, so convert from `LocalDefId`. + let d: DefId = LocalDefId::to_def_id(def_id); + // To be potentially eligible for "unused function" mappings, a definition must: + // - Be eligible for coverage instrumentation // - Not participate directly in codegen (or have lost all its coverage statements) // - Not have any coverage statements inlined into codegenned functions - tcx.def_kind(def_id).is_fn_like() - && (!usage.all_mono_items.contains(&def_id) - || usage.missing_own_coverage.contains(&def_id)) - && !usage.used_via_inlining.contains(&def_id) + tcx.is_eligible_for_coverage(def_id) + && (!usage.all_mono_items.contains(&d) || usage.missing_own_coverage.contains(&d)) + && !usage.used_via_inlining.contains(&d) }; - // Scan for unused functions that were instrumented for coverage. - for def_id in tcx.mir_keys(()).iter().copied().filter(|&def_id| is_unused_fn(def_id)) { - // Get the coverage info from MIR, skipping functions that were never instrumented. - let body = tcx.optimized_mir(def_id); - let Some(function_coverage_info) = body.function_coverage_info.as_deref() else { continue }; - - // FIXME(79651): Consider trying to filter out dummy instantiations of - // unused generic functions from library crates, because they can produce - // "unused instantiation" in coverage reports even when they are actually - // used by some downstream crate in the same binary. + // FIXME(#79651): Consider trying to filter out dummy instantiations of + // unused generic functions from library crates, because they can produce + // "unused instantiation" in coverage reports even when they are actually + // used by some downstream crate in the same binary. - debug!("generating unused fn: {def_id:?}"); - add_unused_function_coverage(cx, def_id, function_coverage_info); - } + tcx.mir_keys(()) + .iter() + .copied() + .filter(|&def_id| is_unused_fn(def_id)) + .map(|def_id| make_dummy_instance(tcx, def_id)) + .collect::<Vec<_>>() } struct UsageSets<'tcx> { @@ -361,16 +350,11 @@ fn prepare_usage_sets<'tcx>(tcx: TyCtxt<'tcx>) -> UsageSets<'tcx> { UsageSets { all_mono_items, used_via_inlining, missing_own_coverage } } -fn add_unused_function_coverage<'tcx>( - cx: &CodegenCx<'_, 'tcx>, - def_id: LocalDefId, - function_coverage_info: &'tcx mir::coverage::FunctionCoverageInfo, -) { - let tcx = cx.tcx; - let def_id = def_id.to_def_id(); +fn make_dummy_instance<'tcx>(tcx: TyCtxt<'tcx>, local_def_id: LocalDefId) -> ty::Instance<'tcx> { + let def_id = local_def_id.to_def_id(); // Make a dummy instance that fills in all generics with placeholders. - let instance = ty::Instance::new( + ty::Instance::new( def_id, ty::GenericArgs::for_item(tcx, def_id, |param, _| { if let ty::GenericParamDefKind::Lifetime = param.kind { @@ -379,9 +363,5 @@ fn add_unused_function_coverage<'tcx>( tcx.mk_param_from_def(param) } }), - ); - - // An unused function's mappings will all be rewritten to map to zero. - let function_coverage = FunctionCoverage::new_unused(function_coverage_info); - cx.coverage_cx().function_coverage_map.borrow_mut().insert(instance, function_coverage); + ) } diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs index 33e7a0f2f20..5428d776f41 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs @@ -10,15 +10,16 @@ use rustc_abi::Align; use rustc_codegen_ssa::traits::{ BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods, }; -use rustc_middle::bug; -use rustc_middle::mir::coverage::MappingKind; +use rustc_middle::mir::coverage::{ + CovTerm, CoverageIdsInfo, Expression, FunctionCoverageInfo, Mapping, MappingKind, Op, +}; use rustc_middle::ty::{Instance, TyCtxt}; +use rustc_span::Span; use rustc_target::spec::HasTargetSpec; use tracing::debug; use crate::common::CodegenCx; -use crate::coverageinfo::map_data::FunctionCoverage; -use crate::coverageinfo::mapgen::{GlobalFileTable, VirtualFileMapping, span_file_name}; +use crate::coverageinfo::mapgen::{GlobalFileTable, VirtualFileMapping, spans}; use crate::coverageinfo::{ffi, llvm_cov}; use crate::llvm; @@ -45,77 +46,129 @@ impl<'tcx> CovfunRecord<'tcx> { pub(crate) fn prepare_covfun_record<'tcx>( tcx: TyCtxt<'tcx>, - global_file_table: &GlobalFileTable, + global_file_table: &mut GlobalFileTable, instance: Instance<'tcx>, - function_coverage: &FunctionCoverage<'tcx>, + is_used: bool, ) -> Option<CovfunRecord<'tcx>> { + let fn_cov_info = tcx.instance_mir(instance.def).function_coverage_info.as_deref()?; + let ids_info = tcx.coverage_ids_info(instance.def); + + let expressions = prepare_expressions(fn_cov_info, ids_info, is_used); + let mut covfun = CovfunRecord { mangled_function_name: tcx.symbol_name(instance).name, - source_hash: function_coverage.source_hash(), - is_used: function_coverage.is_used(), + source_hash: if is_used { fn_cov_info.function_source_hash } else { 0 }, + is_used, virtual_file_mapping: VirtualFileMapping::default(), - expressions: function_coverage.counter_expressions().collect::<Vec<_>>(), + expressions, regions: ffi::Regions::default(), }; - fill_region_tables(tcx, global_file_table, function_coverage, &mut covfun); + fill_region_tables(tcx, global_file_table, fn_cov_info, ids_info, &mut covfun); if covfun.regions.has_no_regions() { - if covfun.is_used { - bug!("a used function should have had coverage mapping data but did not: {covfun:?}"); - } else { - debug!(?covfun, "unused function had no coverage mapping data"); - return None; - } + debug!(?covfun, "function has no mappings to embed; skipping"); + return None; } Some(covfun) } +/// Convert the function's coverage-counter expressions into a form suitable for FFI. +fn prepare_expressions( + fn_cov_info: &FunctionCoverageInfo, + ids_info: &CoverageIdsInfo, + is_used: bool, +) -> Vec<ffi::CounterExpression> { + // If any counters or expressions were removed by MIR opts, replace their + // terms with zero. + let counter_for_term = |term| { + if !is_used || ids_info.is_zero_term(term) { + ffi::Counter::ZERO + } else { + ffi::Counter::from_term(term) + } + }; + + // We know that LLVM will optimize out any unused expressions before + // producing the final coverage map, so there's no need to do the same + // thing on the Rust side unless we're confident we can do much better. + // (See `CounterExpressionsMinimizer` in `CoverageMappingWriter.cpp`.) + fn_cov_info + .expressions + .iter() + .map(move |&Expression { lhs, op, rhs }| ffi::CounterExpression { + lhs: counter_for_term(lhs), + kind: match op { + Op::Add => ffi::ExprKind::Add, + Op::Subtract => ffi::ExprKind::Subtract, + }, + rhs: counter_for_term(rhs), + }) + .collect::<Vec<_>>() +} + /// Populates the mapping region tables in the current function's covfun record. fn fill_region_tables<'tcx>( tcx: TyCtxt<'tcx>, - global_file_table: &GlobalFileTable, - function_coverage: &FunctionCoverage<'tcx>, + global_file_table: &mut GlobalFileTable, + fn_cov_info: &'tcx FunctionCoverageInfo, + ids_info: &'tcx CoverageIdsInfo, covfun: &mut CovfunRecord<'tcx>, ) { - let counter_regions = function_coverage.counter_regions(); - if counter_regions.is_empty() { - return; - } - // Currently a function's mappings must all be in the same file as its body span. - let file_name = span_file_name(tcx, function_coverage.function_coverage_info.body_span); + let source_map = tcx.sess.source_map(); + let source_file = source_map.lookup_source_file(fn_cov_info.body_span.lo()); - // Look up the global file ID for that filename. - let global_file_id = global_file_table.global_file_id_for_file_name(file_name); + // Look up the global file ID for that file. + let global_file_id = global_file_table.global_file_id_for_file(&source_file); // Associate that global file ID with a local file ID for this function. let local_file_id = covfun.virtual_file_mapping.local_id_for_global(global_file_id); - debug!(" file id: {local_file_id:?} => {global_file_id:?} = '{file_name:?}'"); let ffi::Regions { code_regions, branch_regions, mcdc_branch_regions, mcdc_decision_regions } = &mut covfun.regions; + let make_cov_span = |span: Span| { + spans::make_coverage_span(local_file_id, source_map, fn_cov_info, &source_file, span) + }; + let discard_all = tcx.sess.coverage_discard_all_spans_in_codegen(); + // For each counter/region pair in this function+file, convert it to a // form suitable for FFI. - for (mapping_kind, region) in counter_regions { - debug!("Adding counter {mapping_kind:?} to map for {region:?}"); - let span = ffi::CoverageSpan::from_source_region(local_file_id, region); - match mapping_kind { + let is_zero_term = |term| !covfun.is_used || ids_info.is_zero_term(term); + for &Mapping { ref kind, span } in &fn_cov_info.mappings { + // If the mapping refers to counters/expressions that were removed by + // MIR opts, replace those occurrences with zero. + let kind = kind.map_terms(|term| if is_zero_term(term) { CovTerm::Zero } else { term }); + + // Convert the `Span` into coordinates that we can pass to LLVM, or + // discard the span if conversion fails. In rare, cases _all_ of a + // function's spans are discarded, and the rest of coverage codegen + // needs to handle that gracefully to avoid a repeat of #133606. + // We don't have a good test case for triggering that organically, so + // instead we set `-Zcoverage-options=discard-all-spans-in-codegen` + // to force it to occur. + let Some(cov_span) = make_cov_span(span) else { continue }; + if discard_all { + continue; + } + + match kind { MappingKind::Code(term) => { - code_regions.push(ffi::CodeRegion { span, counter: ffi::Counter::from_term(term) }); + code_regions + .push(ffi::CodeRegion { cov_span, counter: ffi::Counter::from_term(term) }); } MappingKind::Branch { true_term, false_term } => { branch_regions.push(ffi::BranchRegion { - span, + cov_span, true_counter: ffi::Counter::from_term(true_term), false_counter: ffi::Counter::from_term(false_term), }); } MappingKind::MCDCBranch { true_term, false_term, mcdc_params } => { mcdc_branch_regions.push(ffi::MCDCBranchRegion { - span, + cov_span, true_counter: ffi::Counter::from_term(true_term), false_counter: ffi::Counter::from_term(false_term), mcdc_branch_params: ffi::mcdc::BranchParameters::from(mcdc_params), @@ -123,7 +176,7 @@ fn fill_region_tables<'tcx>( } MappingKind::MCDCDecision(mcdc_decision_params) => { mcdc_decision_regions.push(ffi::MCDCDecisionRegion { - span, + cov_span, mcdc_decision_params: ffi::mcdc::DecisionParameters::from(mcdc_decision_params), }); } diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/spans.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/spans.rs new file mode 100644 index 00000000000..6d1d91340c2 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/spans.rs @@ -0,0 +1,126 @@ +use rustc_middle::mir::coverage::FunctionCoverageInfo; +use rustc_span::source_map::SourceMap; +use rustc_span::{BytePos, Pos, SourceFile, Span}; +use tracing::debug; + +use crate::coverageinfo::ffi; +use crate::coverageinfo::mapgen::LocalFileId; + +/// Converts the span into its start line and column, and end line and column. +/// +/// Line numbers and column numbers are 1-based. Unlike most column numbers emitted by +/// the compiler, these column numbers are denoted in **bytes**, because that's what +/// LLVM's `llvm-cov` tool expects to see in coverage maps. +/// +/// Returns `None` if the conversion failed for some reason. This shouldn't happen, +/// but it's hard to rule out entirely (especially in the presence of complex macros +/// or other expansions), and if it does happen then skipping a span or function is +/// better than an ICE or `llvm-cov` failure that the user might have no way to avoid. +pub(crate) fn make_coverage_span( + file_id: LocalFileId, + source_map: &SourceMap, + fn_cov_info: &FunctionCoverageInfo, + file: &SourceFile, + span: Span, +) -> Option<ffi::CoverageSpan> { + let span = ensure_non_empty_span(source_map, fn_cov_info, span)?; + + let lo = span.lo(); + let hi = span.hi(); + + // Column numbers need to be in bytes, so we can't use the more convenient + // `SourceMap` methods for looking up file coordinates. + let line_and_byte_column = |pos: BytePos| -> Option<(usize, usize)> { + let rpos = file.relative_position(pos); + let line_index = file.lookup_line(rpos)?; + let line_start = file.lines()[line_index]; + // Line numbers and column numbers are 1-based, so add 1 to each. + Some((line_index + 1, (rpos - line_start).to_usize() + 1)) + }; + + let (mut start_line, start_col) = line_and_byte_column(lo)?; + let (mut end_line, end_col) = line_and_byte_column(hi)?; + + // Apply an offset so that code in doctests has correct line numbers. + // FIXME(#79417): Currently we have no way to offset doctest _columns_. + start_line = source_map.doctest_offset_line(&file.name, start_line); + end_line = source_map.doctest_offset_line(&file.name, end_line); + + check_coverage_span(ffi::CoverageSpan { + file_id: file_id.as_u32(), + start_line: start_line as u32, + start_col: start_col as u32, + end_line: end_line as u32, + end_col: end_col as u32, + }) +} + +fn ensure_non_empty_span( + source_map: &SourceMap, + fn_cov_info: &FunctionCoverageInfo, + span: Span, +) -> Option<Span> { + if !span.is_empty() { + return Some(span); + } + + let lo = span.lo(); + let hi = span.hi(); + + // The span is empty, so try to expand it to cover an adjacent '{' or '}', + // but only within the bounds of the body span. + let try_next = hi < fn_cov_info.body_span.hi(); + let try_prev = fn_cov_info.body_span.lo() < lo; + if !(try_next || try_prev) { + return None; + } + + source_map + .span_to_source(span, |src, start, end| try { + // Adjusting span endpoints by `BytePos(1)` is normally a bug, + // but in this case we have specifically checked that the character + // we're skipping over is one of two specific ASCII characters, so + // adjusting by exactly 1 byte is correct. + if try_next && src.as_bytes()[end] == b'{' { + Some(span.with_hi(hi + BytePos(1))) + } else if try_prev && src.as_bytes()[start - 1] == b'}' { + Some(span.with_lo(lo - BytePos(1))) + } else { + None + } + }) + .ok()? +} + +/// If `llvm-cov` sees a source region that is improperly ordered (end < start), +/// it will immediately exit with a fatal error. To prevent that from happening, +/// discard regions that are improperly ordered, or might be interpreted in a +/// way that makes them improperly ordered. +fn check_coverage_span(cov_span: ffi::CoverageSpan) -> Option<ffi::CoverageSpan> { + let ffi::CoverageSpan { file_id: _, start_line, start_col, end_line, end_col } = cov_span; + + // Line/column coordinates are supposed to be 1-based. If we ever emit + // coordinates of 0, `llvm-cov` might misinterpret them. + let all_nonzero = [start_line, start_col, end_line, end_col].into_iter().all(|x| x != 0); + // Coverage mappings use the high bit of `end_col` to indicate that a + // region is actually a "gap" region, so make sure it's unset. + let end_col_has_high_bit_unset = (end_col & (1 << 31)) == 0; + // If a region is improperly ordered (end < start), `llvm-cov` will exit + // with a fatal error, which is inconvenient for users and hard to debug. + let is_ordered = (start_line, start_col) <= (end_line, end_col); + + if all_nonzero && end_col_has_high_bit_unset && is_ordered { + Some(cov_span) + } else { + debug!( + ?cov_span, + ?all_nonzero, + ?end_col_has_high_bit_unset, + ?is_ordered, + "Skipping source region that would be misinterpreted or rejected by LLVM" + ); + // If this happens in a debug build, ICE to make it easier to notice. + debug_assert!(false, "Improper source region: {cov_span:?}"); + None + } +} diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index 82b6677e203..7311cd9d230 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -5,7 +5,7 @@ use rustc_abi::Size; use rustc_codegen_ssa::traits::{ BuilderMethods, ConstCodegenMethods, CoverageInfoBuilderMethods, MiscCodegenMethods, }; -use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; +use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::ty::Instance; use rustc_middle::ty::layout::HasTyCtxt; @@ -13,18 +13,16 @@ use tracing::{debug, instrument}; use crate::builder::Builder; use crate::common::CodegenCx; -use crate::coverageinfo::map_data::FunctionCoverage; use crate::llvm; pub(crate) mod ffi; mod llvm_cov; -pub(crate) mod map_data; mod mapgen; /// Extra per-CGU context/state needed for coverage instrumentation. pub(crate) struct CguCoverageContext<'ll, 'tcx> { /// Coverage data for each instrumented function identified by DefId. - pub(crate) function_coverage_map: RefCell<FxIndexMap<Instance<'tcx>, FunctionCoverage<'tcx>>>, + pub(crate) instances_used: RefCell<FxIndexSet<Instance<'tcx>>>, pub(crate) pgo_func_name_var_map: RefCell<FxHashMap<Instance<'tcx>, &'ll llvm::Value>>, pub(crate) mcdc_condition_bitmap_map: RefCell<FxHashMap<Instance<'tcx>, Vec<&'ll llvm::Value>>>, @@ -34,17 +32,13 @@ pub(crate) struct CguCoverageContext<'ll, 'tcx> { impl<'ll, 'tcx> CguCoverageContext<'ll, 'tcx> { pub(crate) fn new() -> Self { Self { - function_coverage_map: Default::default(), + instances_used: RefCell::<FxIndexSet<_>>::default(), pgo_func_name_var_map: Default::default(), mcdc_condition_bitmap_map: Default::default(), covfun_section_name: Default::default(), } } - fn take_function_coverage_map(&self) -> FxIndexMap<Instance<'tcx>, FunctionCoverage<'tcx>> { - self.function_coverage_map.replace(FxIndexMap::default()) - } - /// LLVM use a temp value to record evaluated mcdc test vector of each decision, which is /// called condition bitmap. In order to handle nested decisions, several condition bitmaps can /// be allocated for a function body. These values are named `mcdc.addr.{i}` and are a 32-bit @@ -157,12 +151,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { // Mark the instance as used in this CGU, for coverage purposes. // This includes functions that were not partitioned into this CGU, // but were MIR-inlined into one of this CGU's functions. - coverage_cx.function_coverage_map.borrow_mut().entry(instance).or_insert_with(|| { - FunctionCoverage::new_used( - function_coverage_info, - bx.tcx.coverage_ids_info(instance.def), - ) - }); + coverage_cx.instances_used.borrow_mut().insert(instance); match *kind { CoverageKind::SpanMarker | CoverageKind::BlockMarker { .. } => unreachable!( diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs index aef8642f199..2c9f1cda13a 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs @@ -7,7 +7,7 @@ use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::bug; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerType; use rustc_session::config::{CrateType, DebugInfo}; -use rustc_span::symbol::sym; +use rustc_span::sym; use crate::builder::Builder; use crate::common::CodegenCx; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 59275254022..40248a9009a 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -16,8 +16,7 @@ use rustc_middle::ty::{ self, AdtKind, CoroutineArgsExt, Instance, PolyExistentialTraitRef, Ty, TyCtxt, Visibility, }; use rustc_session::config::{self, DebugInfo, Lto}; -use rustc_span::symbol::Symbol; -use rustc_span::{DUMMY_SP, FileName, FileNameDisplayPreference, SourceFile, hygiene}; +use rustc_span::{DUMMY_SP, FileName, FileNameDisplayPreference, SourceFile, Symbol, hygiene}; use rustc_symbol_mangling::typeid_for_trait_ref; use rustc_target::spec::DebuginfoKind; use smallvec::smallvec; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs index d374767f187..23e11748e52 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs @@ -212,21 +212,17 @@ pub(super) fn build_enum_type_di_node<'ll, 'tcx>( ), |cx, enum_type_di_node| { match enum_type_and_layout.variants { - Variants::Single { index: variant_index } => { - if enum_adt_def.variants().is_empty() { - // Uninhabited enums have Variants::Single. We don't generate - // any members for them. - return smallvec![]; - } - - build_single_variant_union_fields( - cx, - enum_adt_def, - enum_type_and_layout, - enum_type_di_node, - variant_index, - ) + Variants::Empty => { + // We don't generate any members for uninhabited types. + return smallvec![]; } + Variants::Single { index: variant_index } => build_single_variant_union_fields( + cx, + enum_adt_def, + enum_type_and_layout, + enum_type_di_node, + variant_index, + ), Variants::Multiple { tag_encoding: TagEncoding::Direct, ref variants, @@ -303,6 +299,7 @@ pub(super) fn build_coroutine_di_node<'ll, 'tcx>( ) } Variants::Single { .. } + | Variants::Empty | Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, .. } => { bug!( "Encountered coroutine with non-direct-tag layout: {:?}", diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs index 65ab22ad89e..9f6a5cc89e0 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs @@ -392,7 +392,7 @@ fn compute_discriminant_value<'ll, 'tcx>( variant_index: VariantIdx, ) -> DiscrResult { match enum_type_and_layout.layout.variants() { - &Variants::Single { .. } => DiscrResult::NoDiscriminant, + &Variants::Single { .. } | &Variants::Empty => DiscrResult::NoDiscriminant, &Variants::Multiple { tag_encoding: TagEncoding::Direct, .. } => DiscrResult::Value( enum_type_and_layout.ty.discriminant_for_variant(cx.tcx, variant_index).unwrap().val, ), diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs index 241bf167a81..11824398f24 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs @@ -358,8 +358,8 @@ fn build_discr_member_di_node<'ll, 'tcx>( let containing_scope = enum_or_coroutine_type_di_node; match enum_or_coroutine_type_and_layout.layout.variants() { - // A single-variant enum has no discriminant. - &Variants::Single { .. } => None, + // A single-variant or no-variant enum has no discriminant. + &Variants::Single { .. } | &Variants::Empty => None, &Variants::Multiple { tag_field, .. } => { let tag_base_type = tag_base_type(cx.tcx, enum_or_coroutine_type_and_layout); diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index a8fdfbed592..fae698bea2a 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -19,9 +19,8 @@ use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf}; use rustc_middle::ty::{self, GenericArgsRef, Instance, Ty, TypeVisitableExt}; use rustc_session::Session; use rustc_session::config::{self, DebugInfo}; -use rustc_span::symbol::Symbol; use rustc_span::{ - BytePos, Pos, SourceFile, SourceFileAndLine, SourceFileHash, Span, StableSourceFileId, + BytePos, Pos, SourceFile, SourceFileAndLine, SourceFileHash, Span, StableSourceFileId, Symbol, }; use smallvec::SmallVec; use tracing::debug; diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index af8562db054..0de0c6a7a89 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -17,6 +17,7 @@ #![feature(iter_intersperse)] #![feature(let_chains)] #![feature(rustdoc_internals)] +#![feature(try_blocks)] #![warn(unreachable_pub)] // tidy-alphabetical-end @@ -43,7 +44,7 @@ use rustc_middle::ty::TyCtxt; use rustc_middle::util::Providers; use rustc_session::Session; use rustc_session::config::{OptLevel, OutputFilenames, PrintKind, PrintRequest}; -use rustc_span::symbol::Symbol; +use rustc_span::Symbol; mod back { pub(crate) mod archive; diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index d62632d1431..9a2bfd95562 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -56,25 +56,6 @@ pub enum LLVMRustResult { Failure, } -// Rust version of the C struct with the same name in rustc_llvm/llvm-wrapper/RustWrapper.cpp. -#[repr(C)] -pub struct LLVMRustCOFFShortExport { - pub name: *const c_char, - pub ordinal_present: bool, - /// value of `ordinal` only important when `ordinal_present` is true - pub ordinal: u16, -} - -impl LLVMRustCOFFShortExport { - pub fn new(name: *const c_char, ordinal: Option<u16>) -> LLVMRustCOFFShortExport { - LLVMRustCOFFShortExport { - name, - ordinal_present: ordinal.is_some(), - ordinal: ordinal.unwrap_or(0), - } - } -} - /// Translation of LLVM's MachineTypes enum, defined in llvm\include\llvm\BinaryFormat\COFF.h. /// /// We include only architectures supported on Windows. @@ -2347,15 +2328,6 @@ unsafe extern "C" { ) -> &'a mut RustArchiveMember<'a>; pub fn LLVMRustArchiveMemberFree<'a>(Member: &'a mut RustArchiveMember<'a>); - pub fn LLVMRustWriteImportLibrary( - ImportName: *const c_char, - Path: *const c_char, - Exports: *const LLVMRustCOFFShortExport, - NumExports: usize, - Machine: u16, - MinGW: bool, - ) -> LLVMRustResult; - pub fn LLVMRustSetDataLayoutFromTargetMachine<'a>(M: &'a Module, TM: &'a TargetMachine); pub fn LLVMRustPositionBuilderAtStart<'a>(B: &Builder<'a>, BB: &'a BasicBlock); diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index bfec7d708cf..628c0b1c29c 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -15,7 +15,7 @@ use rustc_fs_util::path_to_c_string; use rustc_middle::bug; use rustc_session::Session; use rustc_session::config::{PrintKind, PrintRequest}; -use rustc_span::symbol::Symbol; +use rustc_span::Symbol; use rustc_target::spec::{MergeFunctions, PanicStrategy, SmallDataThresholdSupport}; use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATURES}; @@ -373,9 +373,9 @@ pub fn target_features_cfg(sess: &Session, allow_unstable: bool) -> Vec<Symbol> .rust_target_features() .iter() .filter(|(_, gate, _)| gate.in_cfg()) - .filter_map(|&(feature, gate, _)| { + .filter_map(|(feature, gate, _)| { if sess.is_nightly_build() || allow_unstable || gate.requires_nightly().is_none() { - Some(feature) + Some(*feature) } else { None } @@ -718,7 +718,7 @@ pub(crate) fn global_llvm_features( } Some((_, stability, _)) => { if let Err(reason) = - stability.compute_toggleability(&sess.target).allow_toggle() + stability.toggle_allowed(&sess.target, enable_disable == '+') { sess.dcx().emit_warn(ForbiddenCTargetFeature { feature, reason }); } else if stability.requires_nightly().is_some() { diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index 2b05e24a7ba..b0b6da869da 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -38,7 +38,7 @@ fn uncached_llvm_type<'a, 'tcx>( if let (&ty::Adt(def, _), &Variants::Single { index }) = (layout.ty.kind(), &layout.variants) { - if def.is_enum() && !def.variants().is_empty() { + if def.is_enum() { write!(&mut name, "::{}", def.variant(index).name).unwrap(); } } diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 450a95ae20c..3a9b4d36807 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -8,7 +8,9 @@ edition = "2021" ar_archive_writer = "0.4.2" arrayvec = { version = "0.7", default-features = false } bitflags = "2.4.1" -cc = "1.1.23" +# Pinned so `cargo update` bumps don't cause breakage. Please also update the +# `cc` in `rustc_llvm` if you update the `cc` here. +cc = "=1.2.5" either = "1.5.0" itertools = "0.12" pathdiff = "0.2.0" @@ -17,12 +19,13 @@ rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_fs_util = { path = "../rustc_fs_util" } rustc_hir = { path = "../rustc_hir" } +rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_incremental = { path = "../rustc_incremental" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs index 11bcd727501..ab65319e3d3 100644 --- a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs +++ b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs @@ -26,15 +26,14 @@ use std::borrow::Cow; use std::fmt; -use rustc_ast as ast; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::{DiagArgValue, IntoDiagArg}; +use rustc_hir as hir; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::mir::mono::CodegenUnitNameBuilder; use rustc_middle::ty::TyCtxt; use rustc_session::Session; -use rustc_span::symbol::sym; -use rustc_span::{Span, Symbol}; +use rustc_span::{Span, Symbol, sym}; use thin_vec::ThinVec; use tracing::debug; @@ -77,7 +76,7 @@ struct AssertModuleSource<'tcx> { } impl<'tcx> AssertModuleSource<'tcx> { - fn check_attr(&mut self, attr: &ast::Attribute) { + fn check_attr(&mut self, attr: &hir::Attribute) { let (expected_reuse, comp_kind) = if attr.has_name(sym::rustc_partition_reused) { (CguReuse::PreLto, ComparisonKind::AtLeast) } else if attr.has_name(sym::rustc_partition_codegened) { @@ -158,7 +157,7 @@ impl<'tcx> AssertModuleSource<'tcx> { ); } - fn field(&self, attr: &ast::Attribute, name: Symbol) -> Symbol { + fn field(&self, attr: &hir::Attribute, name: Symbol) -> Symbol { for item in attr.meta_item_list().unwrap_or_else(ThinVec::new) { if item.has_name(name) { if let Some(value) = item.value_str() { @@ -177,7 +176,7 @@ impl<'tcx> AssertModuleSource<'tcx> { /// Scan for a `cfg="foo"` attribute and check whether we have a /// cfg flag called `foo`. - fn check_config(&self, attr: &ast::Attribute) -> bool { + fn check_config(&self, attr: &hir::Attribute) -> bool { let config = &self.tcx.sess.psess.config; let value = self.field(attr, sym::cfg); debug!("check_config(config={:?}, value={:?})", config, value); diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index d4836eb7a1d..d9eece1d8dc 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -14,7 +14,7 @@ use object::read::macho::FatArch; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::memmap::Mmap; use rustc_session::Session; -use rustc_span::symbol::Symbol; +use rustc_span::Symbol; use tempfile::Builder as TempFileBuilder; use tracing::trace; diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 31ac8c6e66a..4bc064528f3 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -15,7 +15,7 @@ use rustc_ast::CRATE_NODE_ID; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::temp_dir::MaybeTempDir; -use rustc_errors::{DiagCtxtHandle, FatalError}; +use rustc_errors::DiagCtxtHandle; use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize}; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_metadata::fs::{METADATA_FILENAME, copy_to_stdout, emit_wrapper_file}; @@ -35,7 +35,7 @@ use rustc_session::utils::NativeLibKind; /// For all the linkers we support, and information they might /// need out of the shared crate context before we get rid of it. use rustc_session::{Session, filesearch}; -use rustc_span::symbol::Symbol; +use rustc_span::Symbol; use rustc_target::spec::crt_objects::CrtObjects; use rustc_target::spec::{ Cc, LinkOutputKind, LinkSelfContainedComponents, LinkSelfContainedDefault, LinkerFeatures, @@ -234,8 +234,6 @@ pub fn each_linked_rlib( crate_type: Option<CrateType>, f: &mut dyn FnMut(CrateNum, &Path), ) -> Result<(), errors::LinkRlibError> { - let crates = info.used_crates.iter(); - let fmts = if let Some(crate_type) = crate_type { let Some(fmts) = info.dependency_formats.get(&crate_type) else { return Err(errors::LinkRlibError::MissingFormat); @@ -261,8 +259,9 @@ pub fn each_linked_rlib( info.dependency_formats.first().unwrap().1 }; - for &cnum in crates { - match fmts.get(cnum.as_usize() - 1) { + let used_dep_crates = info.used_crates.iter(); + for &cnum in used_dep_crates { + match fmts.get(cnum) { Some(&Linkage::NotLinked | &Linkage::Dynamic | &Linkage::IncludedFromDylib) => continue, Some(_) => {} None => return Err(errors::LinkRlibError::MissingFormat), @@ -624,7 +623,7 @@ fn link_staticlib( let mut all_rust_dylibs = vec![]; for &cnum in crates { - match fmts.get(cnum.as_usize() - 1) { + match fmts.get(cnum) { Some(&Linkage::Dynamic) => {} _ => continue, } @@ -986,12 +985,12 @@ fn link_natively( let mut output = prog.stderr.clone(); output.extend_from_slice(&prog.stdout); let escaped_output = escape_linker_output(&output, flavor); - // FIXME: Add UI tests for this error. let err = errors::LinkingFailed { linker_path: &linker_path, exit_status: prog.status, - command: &cmd, + command: cmd, escaped_output, + verbose: sess.opts.verbose, }; sess.dcx().emit_err(err); // If MSVC's `link.exe` was expected but the return code @@ -1039,22 +1038,22 @@ fn link_natively( Err(e) => { let linker_not_found = e.kind() == io::ErrorKind::NotFound; - if linker_not_found { - sess.dcx().emit_err(errors::LinkerNotFound { linker_path, error: e }); + let err = if linker_not_found { + sess.dcx().emit_err(errors::LinkerNotFound { linker_path, error: e }) } else { sess.dcx().emit_err(errors::UnableToExeLinker { linker_path, error: e, command_formatted: format!("{cmd:?}"), - }); - } + }) + }; if sess.target.is_like_msvc && linker_not_found { sess.dcx().emit_note(errors::MsvcMissingLinker); sess.dcx().emit_note(errors::CheckInstalledVisualStudio); sess.dcx().emit_note(errors::InsufficientVSCodeProduct); } - FatalError.raise(); + err.raise_fatal(); } } @@ -2361,8 +2360,8 @@ fn linker_with_args( .crate_info .native_libraries .iter() - .filter_map(|(cnum, libraries)| { - (dependency_linkage[cnum.as_usize() - 1] != Linkage::Static).then_some(libraries) + .filter_map(|(&cnum, libraries)| { + (dependency_linkage[cnum] != Linkage::Static).then_some(libraries) }) .flatten() .collect::<Vec<_>>(); @@ -2754,7 +2753,7 @@ fn add_upstream_rust_crates( // (e.g. `libstd` when `-C prefer-dynamic` is used). // FIXME: `dependency_formats` can report `profiler_builtins` as `NotLinked` for some // reason, it shouldn't do that because `profiler_builtins` should indeed be linked. - let linkage = data[cnum.as_usize() - 1]; + let linkage = data[cnum]; let link_static_crate = linkage == Linkage::Static || (linkage == Linkage::IncludedFromDylib || linkage == Linkage::NotLinked) && (codegen_results.crate_info.compiler_builtins == Some(cnum) @@ -2982,7 +2981,7 @@ fn add_dynamic_crate(cmd: &mut dyn Linker, sess: &Session, cratepath: &Path) { fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool { match lib.cfg { - Some(ref cfg) => rustc_attr::cfg_matches(cfg, sess, CRATE_NODE_ID, None), + Some(ref cfg) => rustc_attr_parsing::cfg_matches(cfg, sess, CRATE_NODE_ID, None), None => true, } } diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 301b22f2be4..3c6513ca26b 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -16,7 +16,7 @@ use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo, S use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_session::config::{self, CrateType, DebugInfo, LinkerPluginLto, Lto, OptLevel, Strip}; -use rustc_span::symbol::sym; +use rustc_span::sym; use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, Lld}; use tracing::{debug, warn}; @@ -1744,15 +1744,10 @@ fn for_each_exported_symbols_include_dep<'tcx>( crate_type: CrateType, mut callback: impl FnMut(ExportedSymbol<'tcx>, SymbolExportInfo, CrateNum), ) { - for &(symbol, info) in tcx.exported_symbols(LOCAL_CRATE).iter() { - callback(symbol, info, LOCAL_CRATE); - } - let formats = tcx.dependency_formats(()); let deps = &formats[&crate_type]; - for (index, dep_format) in deps.iter().enumerate() { - let cnum = CrateNum::new(index + 1); + for (cnum, dep_format) in deps.iter_enumerated() { // For each dependency that we are linking to statically ... if *dep_format == Linkage::Static { for &(symbol, info) in tcx.exported_symbols(cnum).iter() { diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 788a8a13b3e..60ab2919352 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -311,7 +311,8 @@ fn exported_symbols_provider_local( } if !tcx.sess.opts.share_generics() { - if tcx.codegen_fn_attrs(mono_item.def_id()).inline == rustc_attr::InlineAttr::Never + if tcx.codegen_fn_attrs(mono_item.def_id()).inline + == rustc_attr_parsing::InlineAttr::Never { // this is OK, we explicitly allow sharing inline(never) across crates even // without share-generics. diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 683defcafee..b40bb4ed5d2 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -33,8 +33,7 @@ use rustc_session::config::{ self, CrateType, Lto, OutFileName, OutputFilenames, OutputType, Passes, SwitchWithOptPath, }; use rustc_span::source_map::SourceMap; -use rustc_span::symbol::sym; -use rustc_span::{FileName, InnerSpan, Span, SpanData}; +use rustc_span::{FileName, InnerSpan, Span, SpanData, sym}; use rustc_target::spec::{MergeFunctions, SanitizerSet}; use tracing::debug; diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 27c9cb0b31e..77e1fed720d 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -5,7 +5,6 @@ use std::time::{Duration, Instant}; use itertools::Itertools; use rustc_abi::FIRST_VARIANT; use rustc_ast::expand::allocator::{ALLOCATOR_METHODS, AllocatorKind, global_fn_name}; -use rustc_attr as attr; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry}; use rustc_data_structures::sync::{Lrc, par_map}; @@ -25,12 +24,12 @@ use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_session::Session; use rustc_session::config::{self, CrateType, EntryFnType, OptLevel, OutputType}; -use rustc_span::symbol::sym; -use rustc_span::{DUMMY_SP, Symbol}; +use rustc_span::{DUMMY_SP, Symbol, sym}; use rustc_trait_selection::infer::at::ToTrace; use rustc_trait_selection::infer::{BoundRegionConversionTime, TyCtxtInferExt}; use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt}; use tracing::{debug, info}; +use {rustc_ast as ast, rustc_attr_parsing as attr}; use crate::assert_module_sources::CguReuse; use crate::back::link::are_upstream_rust_objects_already_included; @@ -873,7 +872,8 @@ impl CrateInfo { crate_types.iter().map(|&c| (c, crate::back::linker::linked_symbols(tcx, c))).collect(); let local_crate_name = tcx.crate_name(LOCAL_CRATE); let crate_attrs = tcx.hir().attrs(rustc_hir::CRATE_HIR_ID); - let subsystem = attr::first_attr_value_str_by_name(crate_attrs, sym::windows_subsystem); + let subsystem = + ast::attr::first_attr_value_str_by_name(crate_attrs, sym::windows_subsystem); let windows_subsystem = subsystem.map(|subsystem| { if subsystem != sym::windows && subsystem != sym::console { tcx.dcx().emit_fatal(errors::InvalidWindowsSubsystem { subsystem }); diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index ab2d24e8d2d..cdb72aba36f 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -1,5 +1,6 @@ -use rustc_ast::{MetaItemInner, MetaItemKind, ast, attr}; -use rustc_attr::{InlineAttr, InstructionSetAttr, OptimizeAttr, list_contains_name}; +use rustc_ast::attr::list_contains_name; +use rustc_ast::{MetaItemInner, attr}; +use rustc_attr_parsing::{InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::codes::*; use rustc_errors::{DiagMessage, SubdiagMessage, struct_span_code_err}; @@ -15,8 +16,7 @@ use rustc_middle::query::Providers; use rustc_middle::ty::{self as ty, TyCtxt}; use rustc_session::parse::feature_err; use rustc_session::{Session, lint}; -use rustc_span::symbol::Ident; -use rustc_span::{Span, sym}; +use rustc_span::{Ident, Span, sym}; use rustc_target::spec::{SanitizerSet, abi}; use crate::errors; @@ -251,7 +251,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { sym::target_feature => { if !tcx.is_closure_like(did.to_def_id()) && let Some(fn_sig) = fn_sig() - && fn_sig.skip_binder().safety() == hir::Safety::Safe + && fn_sig.skip_binder().safety().is_safe() { if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc { // The `#[target_feature]` attribute is allowed on @@ -425,7 +425,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { && let [item] = items.as_slice() && let Some((sym::align, literal)) = item.singleton_lit_list() { - rustc_attr::parse_alignment(&literal.kind) + rustc_attr_parsing::parse_alignment(&literal.kind) .map_err(|msg| { struct_span_code_err!( tcx.dcx(), @@ -525,28 +525,26 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { if !attr.has_name(sym::inline) { return ia; } - match attr.meta_kind() { - Some(MetaItemKind::Word) => InlineAttr::Hint, - Some(MetaItemKind::List(ref items)) => { - inline_span = Some(attr.span); - if items.len() != 1 { - struct_span_code_err!(tcx.dcx(), attr.span, E0534, "expected one argument") - .emit(); - InlineAttr::None - } else if list_contains_name(items, sym::always) { - InlineAttr::Always - } else if list_contains_name(items, sym::never) { - InlineAttr::Never - } else { - struct_span_code_err!(tcx.dcx(), items[0].span(), E0535, "invalid argument") - .with_help("valid inline arguments are `always` and `never`") - .emit(); + if attr.is_word() { + InlineAttr::Hint + } else if let Some(ref items) = attr.meta_item_list() { + inline_span = Some(attr.span); + if items.len() != 1 { + struct_span_code_err!(tcx.dcx(), attr.span, E0534, "expected one argument").emit(); + InlineAttr::None + } else if list_contains_name(items, sym::always) { + InlineAttr::Always + } else if list_contains_name(items, sym::never) { + InlineAttr::Never + } else { + struct_span_code_err!(tcx.dcx(), items[0].span(), E0535, "invalid argument") + .with_help("valid inline arguments are `always` and `never`") + .emit(); - InlineAttr::None - } + InlineAttr::None } - Some(MetaItemKind::NameValue(_)) => ia, - None => ia, + } else { + ia } }); @@ -562,27 +560,24 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { return ia; } let err = |sp, s| struct_span_code_err!(tcx.dcx(), sp, E0722, "{}", s).emit(); - match attr.meta_kind() { - Some(MetaItemKind::Word) => { + if attr.is_word() { + err(attr.span, "expected one argument"); + ia + } else if let Some(ref items) = attr.meta_item_list() { + inline_span = Some(attr.span); + if items.len() != 1 { err(attr.span, "expected one argument"); - ia - } - Some(MetaItemKind::List(ref items)) => { - inline_span = Some(attr.span); - if items.len() != 1 { - err(attr.span, "expected one argument"); - OptimizeAttr::None - } else if list_contains_name(items, sym::size) { - OptimizeAttr::Size - } else if list_contains_name(items, sym::speed) { - OptimizeAttr::Speed - } else { - err(items[0].span(), "invalid argument"); - OptimizeAttr::None - } + OptimizeAttr::None + } else if list_contains_name(items, sym::size) { + OptimizeAttr::Size + } else if list_contains_name(items, sym::speed) { + OptimizeAttr::Speed + } else { + err(items[0].span(), "invalid argument"); + OptimizeAttr::None } - Some(MetaItemKind::NameValue(_)) => ia, - None => ia, + } else { + OptimizeAttr::None } }); @@ -730,7 +725,7 @@ fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool { false } -fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option<u16> { +fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &hir::Attribute) -> Option<u16> { use rustc_ast::{LitIntType, LitKind, MetaItemLit}; let meta_item_list = attr.meta_item_list(); let meta_item_list = meta_item_list.as_deref(); @@ -795,7 +790,7 @@ struct MixedExportNameAndNoMangleState<'a> { export_name: Option<Span>, hir_id: Option<HirId>, no_mangle: Option<Span>, - no_mangle_attr: Option<&'a ast::Attribute>, + no_mangle_attr: Option<&'a hir::Attribute>, } impl<'a> MixedExportNameAndNoMangleState<'a> { @@ -803,7 +798,7 @@ impl<'a> MixedExportNameAndNoMangleState<'a> { self.export_name = Some(span); } - fn track_no_mangle(&mut self, span: Span, hir_id: HirId, attr_name: &'a ast::Attribute) { + fn track_no_mangle(&mut self, span: Span, hir_id: HirId, attr_name: &'a hir::Attribute) { self.no_mangle = Some(span); self.hir_id = Some(hir_id); self.no_mangle_attr = Some(attr_name); @@ -824,7 +819,7 @@ impl<'a> MixedExportNameAndNoMangleState<'a> { no_mangle, errors::MixedExportNameAndNoMangle { no_mangle, - no_mangle_attr: rustc_ast_pretty::pprust::attribute_to_string(no_mangle_attr), + no_mangle_attr: rustc_hir_pretty::attribute_to_string(&tcx, no_mangle_attr), export_name, removal_span: no_mangle, }, diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/mod.rs b/compiler/rustc_codegen_ssa/src/debuginfo/mod.rs index 88d36b19da4..7c62c03d574 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/mod.rs @@ -65,8 +65,8 @@ fn tag_base_type_opt<'tcx>( }); match enum_type_and_layout.layout.variants() { - // A single-variant enum has no discriminant. - Variants::Single { .. } => None, + // A single-variant or no-variant enum has no discriminant. + Variants::Single { .. } | Variants::Empty => None, Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, tag, .. } => { // Niche tags are always normalized to unsized integers of the correct size. diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 00f8654e670..c7213bbc801 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -1,6 +1,7 @@ //! Errors emitted by codegen_ssa use std::borrow::Cow; +use std::ffi::OsString; use std::io::Error; use std::num::ParseIntError; use std::path::{Path, PathBuf}; @@ -345,21 +346,82 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for ThorinErrorWrapper { } pub(crate) struct LinkingFailed<'a> { - pub linker_path: &'a PathBuf, + pub linker_path: &'a Path, pub exit_status: ExitStatus, - pub command: &'a Command, + pub command: Command, pub escaped_output: String, + pub verbose: bool, } impl<G: EmissionGuarantee> Diagnostic<'_, G> for LinkingFailed<'_> { - fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { + fn into_diag(mut self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { let mut diag = Diag::new(dcx, level, fluent::codegen_ssa_linking_failed); diag.arg("linker_path", format!("{}", self.linker_path.display())); diag.arg("exit_status", format!("{}", self.exit_status)); let contains_undefined_ref = self.escaped_output.contains("undefined reference to"); - diag.note(format!("{:?}", self.command)).note(self.escaped_output); + if self.verbose { + diag.note(format!("{:?}", self.command)); + } else { + enum ArgGroup { + Regular(OsString), + Objects(usize), + Rlibs(PathBuf, Vec<OsString>), + } + + // Omit rust object files and fold rlibs in the error by default to make linker errors a + // bit less verbose. + let orig_args = self.command.take_args(); + let mut args: Vec<ArgGroup> = vec![]; + for arg in orig_args { + if arg.as_encoded_bytes().ends_with(b".rcgu.o") { + if let Some(ArgGroup::Objects(n)) = args.last_mut() { + *n += 1; + } else { + args.push(ArgGroup::Objects(1)); + } + } else if arg.as_encoded_bytes().ends_with(b".rlib") { + let rlib_path = Path::new(&arg); + let dir = rlib_path.parent().unwrap(); + let filename = rlib_path.file_name().unwrap().to_owned(); + if let Some(ArgGroup::Rlibs(parent, rlibs)) = args.last_mut() { + if parent == dir { + rlibs.push(filename); + } else { + args.push(ArgGroup::Rlibs(dir.to_owned(), vec![filename])); + } + } else { + args.push(ArgGroup::Rlibs(dir.to_owned(), vec![filename])); + } + } else { + args.push(ArgGroup::Regular(arg)); + } + } + self.command.args(args.into_iter().map(|arg_group| match arg_group { + ArgGroup::Regular(arg) => arg, + ArgGroup::Objects(n) => OsString::from(format!("<{n} object files omitted>")), + ArgGroup::Rlibs(dir, rlibs) => { + let mut arg = dir.into_os_string(); + arg.push("/{"); + let mut first = true; + for rlib in rlibs { + if !first { + arg.push(","); + } + first = false; + arg.push(rlib); + } + arg.push("}"); + arg + } + })); + + diag.note(format!("{:?}", self.command)); + diag.note("some arguments are omitted. use `--verbose` to show all linker arguments"); + } + + diag.note(self.escaped_output); // Trying to match an error from OS linkers // which by now we have no way to translate. diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 7dc8ab38a97..65c6067c740 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -42,7 +42,7 @@ use rustc_session::Session; use rustc_session::config::{CrateType, OutputFilenames, OutputType, RUST_CGU_EXT}; use rustc_session::cstore::{self, CrateSource}; use rustc_session::utils::NativeLibKind; -use rustc_span::symbol::Symbol; +use rustc_span::Symbol; pub mod assert_module_sources; pub mod back; diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index d4d7f16db55..843a996d2bf 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -10,8 +10,7 @@ use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::{Instance, Ty}; use rustc_middle::{bug, mir, ty}; use rustc_session::config::DebugInfo; -use rustc_span::symbol::{Symbol, kw}; -use rustc_span::{BytePos, Span, hygiene}; +use rustc_span::{BytePos, Span, Symbol, hygiene, kw}; use super::operand::{OperandRef, OperandValue}; use super::place::{PlaceRef, PlaceValue}; diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index c08758a9796..cac3cc587cb 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -1,4 +1,4 @@ -use rustc_attr::InstructionSetAttr; +use rustc_attr_parsing::InstructionSetAttr; use rustc_middle::mir::mono::{Linkage, MonoItem, MonoItemData, Visibility}; use rustc_middle::mir::{Body, InlineAsmOperand}; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf}; diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index c38484109d2..c634f864ffb 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -243,6 +243,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { return bx.cx().const_poison(cast_to); } let (tag_scalar, tag_encoding, tag_field) = match self.layout.variants { + Variants::Empty => unreachable!("we already handled uninhabited types"), Variants::Single { index } => { let discr_val = self .layout @@ -365,9 +366,9 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { return; } match self.layout.variants { - Variants::Single { index } => { - assert_eq!(index, variant_index); - } + Variants::Empty => unreachable!("we already handled uninhabited types"), + Variants::Single { index } => assert_eq!(index, variant_index), + Variants::Multiple { tag_encoding: TagEncoding::Direct, tag_field, .. } => { let ptr = self.project_field(bx, tag_field); let to = @@ -422,10 +423,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { layout.size }; - let llval = bx.inbounds_gep(bx.cx().backend_type(self.layout), self.val.llval, &[ - bx.cx().const_usize(0), - llindex, - ]); + let llval = bx.inbounds_gep(bx.cx().backend_type(layout), self.val.llval, &[llindex]); let align = self.val.align.restrict_for_offset(offset); PlaceValue::new_sized(llval, align).with_type(layout) } diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index fa600ec7166..7e80d014ea2 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -1,16 +1,15 @@ -use rustc_ast::ast; -use rustc_attr::InstructionSetAttr; +use rustc_attr_parsing::InstructionSetAttr; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::Applicability; +use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_middle::middle::codegen_fn_attrs::TargetFeature; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::parse::feature_err; -use rustc_span::Span; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::{Span, Symbol, sym}; use rustc_target::target_features; use crate::errors; @@ -19,7 +18,7 @@ use crate::errors; /// Enabled target features are added to `target_features`. pub(crate) fn from_target_feature_attr( tcx: TyCtxt<'_>, - attr: &ast::Attribute, + attr: &hir::Attribute, rust_target_features: &UnordMap<String, target_features::StabilityComputed>, target_features: &mut Vec<TargetFeature>, ) { @@ -65,7 +64,7 @@ pub(crate) fn from_target_feature_attr( // Only allow target features whose feature gates have been enabled // and which are permitted to be toggled. - if let Err(reason) = stability.allow_toggle() { + if let Err(reason) = stability.toggle_allowed(/*enable*/ true) { tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr { span: item.span(), feature, @@ -160,7 +159,7 @@ pub(crate) fn provide(providers: &mut Providers) { .target .rust_target_features() .iter() - .map(|&(a, b, _)| (a.to_string(), b.compute_toggleability(target))) + .map(|(a, b, _)| (a.to_string(), b.compute_toggleability(target))) .collect() } }, diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 4b17db2c49e..ebcf118b903 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -11,7 +11,7 @@ use rustc_middle::ty::TyCtxt; use rustc_middle::util::Providers; use rustc_session::Session; use rustc_session::config::{self, OutputFilenames, PrintRequest}; -use rustc_span::symbol::Symbol; +use rustc_span::Symbol; use super::CodegenObject; use super::write::WriteBackendMethods; diff --git a/compiler/rustc_const_eval/Cargo.toml b/compiler/rustc_const_eval/Cargo.toml index 41136019a88..7717cd2c696 100644 --- a/compiler/rustc_const_eval/Cargo.toml +++ b/compiler/rustc_const_eval/Cargo.toml @@ -9,7 +9,7 @@ either = "1" rustc_abi = { path = "../rustc_abi" } rustc_apfloat = "0.2.0" rustc_ast = { path = "../rustc_ast" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 16610ebfca2..f4257ad9671 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -6,7 +6,7 @@ use std::mem; use std::num::NonZero; use std::ops::Deref; -use rustc_attr::{ConstStability, StabilityLevel}; +use rustc_attr_parsing::{ConstStability, StabilityLevel}; use rustc_errors::{Diag, ErrorGuaranteed}; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, LangItem}; diff --git a/compiler/rustc_const_eval/src/check_consts/mod.rs b/compiler/rustc_const_eval/src/check_consts/mod.rs index 80d3c6448aa..ab68691f1b9 100644 --- a/compiler/rustc_const_eval/src/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/check_consts/mod.rs @@ -9,7 +9,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::ty::{self, PolyFnSig, TyCtxt}; use rustc_middle::{bug, mir}; use rustc_span::Symbol; -use {rustc_attr as attr, rustc_hir as hir}; +use {rustc_attr_parsing as attr, rustc_hir as hir}; pub use self::qualifs::Qualif; diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index 23f2aa4d029..afb7900c4b0 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -15,8 +15,7 @@ use rustc_middle::ty::{ suggest_constraining_type_param, }; use rustc_middle::util::{CallDesugaringKind, CallKind, call_kind}; -use rustc_span::symbol::sym; -use rustc_span::{BytePos, Pos, Span, Symbol}; +use rustc_span::{BytePos, Pos, Span, Symbol, sym}; use rustc_trait_selection::traits::SelectionContext; use tracing::debug; diff --git a/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs b/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs index 951e19b470b..16e142a85ee 100644 --- a/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs +++ b/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs @@ -1,7 +1,7 @@ use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::{self, BasicBlock, Location}; use rustc_middle::ty::TyCtxt; -use rustc_span::symbol::sym; +use rustc_span::sym; use tracing::trace; use super::ConstCx; diff --git a/compiler/rustc_const_eval/src/check_consts/resolver.rs b/compiler/rustc_const_eval/src/check_consts/resolver.rs index 763c37a41af..79df63a9e84 100644 --- a/compiler/rustc_const_eval/src/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/check_consts/resolver.rs @@ -5,7 +5,7 @@ use std::fmt; use std::marker::PhantomData; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::MixedBitSet; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::{ self, BasicBlock, CallReturnPlaces, Local, Location, Statement, StatementKind, TerminatorEdges, @@ -246,12 +246,14 @@ where } #[derive(Debug, PartialEq, Eq)] +/// The state for the `FlowSensitiveAnalysis` dataflow analysis. This domain is likely homogeneous, +/// and has a big size, so we use a bitset that can be sparse (c.f. issue #134404). pub(super) struct State { /// Describes whether a local contains qualif. - pub qualif: BitSet<Local>, + pub qualif: MixedBitSet<Local>, /// Describes whether a local's address escaped and it might become qualified as a result an /// indirect mutation. - pub borrow: BitSet<Local>, + pub borrow: MixedBitSet<Local>, } impl Clone for State { @@ -320,8 +322,8 @@ where fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { State { - qualif: BitSet::new_empty(body.local_decls.len()), - borrow: BitSet::new_empty(body.local_decls.len()), + qualif: MixedBitSet::new_empty(body.local_decls.len()), + borrow: MixedBitSet::new_empty(body.local_decls.len()), } } diff --git a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs index 817acfcca74..46dcebc46e9 100644 --- a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs @@ -1,9 +1,11 @@ use rustc_middle::mir::interpret::{AllocId, ConstAllocation, InterpResult}; use rustc_middle::mir::*; use rustc_middle::query::TyCtxtAt; +use rustc_middle::ty::Ty; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::{bug, span_bug, ty}; use rustc_span::def_id::DefId; +use rustc_target::callconv::FnAbi; use crate::interpret::{ self, HasStaticRootDefId, ImmTy, Immediate, InterpCx, PointerArithmetic, interp_ok, @@ -86,7 +88,7 @@ impl<'tcx> interpret::Machine<'tcx> for DummyMachine { fn find_mir_or_eval_fn( _ecx: &mut InterpCx<'tcx, Self>, _instance: ty::Instance<'tcx>, - _abi: rustc_abi::ExternAbi, + _abi: &FnAbi<'tcx, Ty<'tcx>>, _args: &[interpret::FnArg<'tcx, Self::Provenance>], _destination: &interpret::MPlaceTy<'tcx, Self::Provenance>, _target: Option<BasicBlock>, diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs index f5396c62433..babf99c4c1f 100644 --- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs @@ -57,9 +57,8 @@ fn is_promotable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool { Some(stab) => { if cfg!(debug_assertions) && stab.promotable { let sig = tcx.fn_sig(def_id); - assert_eq!( - sig.skip_binder().safety(), - hir::Safety::Safe, + assert!( + sig.skip_binder().safety().is_safe(), "don't mark const unsafe fns as promotable", // https://github.com/rust-lang/rust/pull/53851#issuecomment-418760682 ); diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 23683851799..ba7fbb254c6 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -2,7 +2,7 @@ use std::borrow::{Borrow, Cow}; use std::fmt; use std::hash::Hash; -use rustc_abi::{Align, ExternAbi, Size}; +use rustc_abi::{Align, Size}; use rustc_ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, IndexEntry}; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -13,8 +13,8 @@ use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, mir}; -use rustc_span::Span; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::{Span, Symbol, sym}; +use rustc_target::callconv::FnAbi; use tracing::debug; use super::error::*; @@ -340,7 +340,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { fn find_mir_or_eval_fn( ecx: &mut InterpCx<'tcx, Self>, orig_instance: ty::Instance<'tcx>, - _abi: ExternAbi, + _abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[FnArg<'tcx>], dest: &MPlaceTy<'tcx>, ret: Option<mir::BasicBlock>, diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index ed4a1a9e647..46720328ea4 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -519,7 +519,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { return M::call_extra_fn( self, extra, - caller_abi, + caller_fn_abi, args, destination, target, @@ -570,7 +570,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let Some((body, instance)) = M::find_mir_or_eval_fn( self, instance, - caller_abi, + caller_fn_abi, args, destination, target, diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs index 6faac1582ab..2f0b1cb6d1e 100644 --- a/compiler/rustc_const_eval/src/interpret/discriminant.rs +++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs @@ -44,7 +44,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } - /// Read discriminant, return the runtime value as well as the variant index. + /// Read discriminant, return the variant index. /// Can also legally be called on non-enums (e.g. through the discriminant_value intrinsic)! /// /// Will never return an uninhabited variant. @@ -65,21 +65,17 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // We use "tag" to refer to how the discriminant is encoded in memory, which can be either // straight-forward (`TagEncoding::Direct`) or with a niche (`TagEncoding::Niche`). let (tag_scalar_layout, tag_encoding, tag_field) = match op.layout().variants { + Variants::Empty => { + throw_ub!(UninhabitedEnumVariantRead(None)); + } Variants::Single { index } => { - // Do some extra checks on enums. - if ty.is_enum() { - // Hilariously, `Single` is used even for 0-variant enums. - // (See https://github.com/rust-lang/rust/issues/89765). - if ty.ty_adt_def().unwrap().variants().is_empty() { - throw_ub!(UninhabitedEnumVariantRead(index)) - } + if op.layout().is_uninhabited() { // For consistency with `write_discriminant`, and to make sure that // `project_downcast` cannot fail due to strange layouts, we declare immediate UB - // for uninhabited variants. - if op.layout().for_variant(self, index).is_uninhabited() { - throw_ub!(UninhabitedEnumVariantRead(index)) - } + // for uninhabited enums. + throw_ub!(UninhabitedEnumVariantRead(Some(index))); } + // Since the type is inhabited, there must be an index. return interp_ok(index); } Variants::Multiple { tag, ref tag_encoding, tag_field, .. } => { @@ -199,11 +195,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // `uninhabited_enum_branching` MIR pass. It also ensures consistency with // `write_discriminant`. if op.layout().for_variant(self, index).is_uninhabited() { - throw_ub!(UninhabitedEnumVariantRead(index)) + throw_ub!(UninhabitedEnumVariantRead(Some(index))) } interp_ok(index) } + /// Read discriminant, return the user-visible discriminant. + /// Can also legally be called on non-enums (e.g. through the discriminant_value intrinsic)! pub fn discriminant_for_variant( &self, ty: Ty<'tcx>, @@ -243,6 +241,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } match layout.variants { + abi::Variants::Empty => unreachable!("we already handled uninhabited types"), abi::Variants::Single { .. } => { // The tag of a `Single` enum is like the tag of the niched // variant: there's no tag as the discriminant is encoded diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index a79923e8555..1af8438534f 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -11,7 +11,7 @@ use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic}; use rustc_middle::ty::layout::{LayoutOf as _, TyAndLayout, ValidityRequirement}; use rustc_middle::ty::{GenericArgsRef, Ty, TyCtxt}; use rustc_middle::{bug, ty}; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::{Symbol, sym}; use tracing::trace; use super::memory::MemoryKind; diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 9ac2a024ccf..36e5a2ff750 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -6,7 +6,7 @@ use std::borrow::{Borrow, Cow}; use std::fmt::Debug; use std::hash::Hash; -use rustc_abi::{Align, ExternAbi, Size}; +use rustc_abi::{Align, Size}; use rustc_apfloat::{Float, FloatConvert}; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_middle::query::TyCtxtAt; @@ -15,6 +15,7 @@ use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::{mir, ty}; use rustc_span::Span; use rustc_span::def_id::DefId; +use rustc_target::callconv::FnAbi; use super::{ AllocBytes, AllocId, AllocKind, AllocRange, Allocation, CTFE_ALLOC_SALT, ConstAllocation, @@ -201,7 +202,7 @@ pub trait Machine<'tcx>: Sized { fn find_mir_or_eval_fn( ecx: &mut InterpCx<'tcx, Self>, instance: ty::Instance<'tcx>, - abi: ExternAbi, + abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[FnArg<'tcx, Self::Provenance>], destination: &MPlaceTy<'tcx, Self::Provenance>, target: Option<mir::BasicBlock>, @@ -213,7 +214,7 @@ pub trait Machine<'tcx>: Sized { fn call_extra_fn( ecx: &mut InterpCx<'tcx, Self>, fn_val: Self::ExtraFnVal, - abi: ExternAbi, + abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[FnArg<'tcx, Self::Provenance>], destination: &MPlaceTy<'tcx, Self::Provenance>, target: Option<mir::BasicBlock>, @@ -656,7 +657,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) { fn call_extra_fn( _ecx: &mut InterpCx<$tcx, Self>, fn_val: !, - _abi: ExternAbi, + _abi: &FnAbi<$tcx, Ty<$tcx>>, _args: &[FnArg<$tcx>], _destination: &MPlaceTy<$tcx, Self::Provenance>, _target: Option<mir::BasicBlock>, diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index 8b7b78c7129..5fa632fc57a 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -6,7 +6,7 @@ use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar}; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, FloatTy, ScalarInt, Ty}; use rustc_middle::{bug, mir, span_bug}; -use rustc_span::symbol::sym; +use rustc_span::sym; use tracing::trace; use super::{ImmTy, InterpCx, Machine, MemPlaceMeta, interp_ok, throw_ub}; diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index 65a93784e2c..996142d7b03 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -325,7 +325,7 @@ where let actual_to = if from_end { if from.checked_add(to).is_none_or(|to| to > len) { // This can only be reached in ConstProp and non-rustc-MIR. - throw_ub!(BoundsCheckFailed { len: len, index: from.saturating_add(to) }); + throw_ub!(BoundsCheckFailed { len, index: from.saturating_add(to) }); } len.checked_sub(to).unwrap() } else { diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 273eaf42d87..6f101395ccf 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -26,7 +26,7 @@ use rustc_middle::mir::interpret::{ }; use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Ty}; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::{Symbol, sym}; use tracing::trace; use super::machine::AllocMap; @@ -302,7 +302,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { }; } } - Variants::Single { .. } => {} + Variants::Single { .. } | Variants::Empty => {} } // Now we know we are projecting to a field, so figure out which one. @@ -344,6 +344,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { // Inside a variant PathElem::Field(def.variant(index).fields[FieldIdx::from_usize(field)].name) } + Variants::Empty => panic!("there is no field in Variants::Empty types"), Variants::Multiple { .. } => bug!("we handled variants above"), } } @@ -1010,7 +1011,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { } // Don't forget potential other variants. match &layout.variants { - Variants::Single { .. } => { + Variants::Single { .. } | Variants::Empty => { // Fully handled above. } Variants::Multiple { variants, .. } => { diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs index 76ab0bb544f..3647c109a6e 100644 --- a/compiler/rustc_const_eval/src/interpret/visitor.rs +++ b/compiler/rustc_const_eval/src/interpret/visitor.rs @@ -218,8 +218,8 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized { // recurse with the inner type self.visit_variant(v, idx, &inner)?; } - // For single-variant layouts, we already did anything there is to do. - Variants::Single { .. } => {} + // For single-variant layouts, we already did everything there is to do. + Variants::Single { .. } | Variants::Empty => {} } interp_ok(()) diff --git a/compiler/rustc_const_eval/src/util/caller_location.rs b/compiler/rustc_const_eval/src/util/caller_location.rs index 6593547cd23..6dd9447cf5a 100644 --- a/compiler/rustc_const_eval/src/util/caller_location.rs +++ b/compiler/rustc_const_eval/src/util/caller_location.rs @@ -3,7 +3,7 @@ use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self}; use rustc_middle::{bug, mir}; -use rustc_span::symbol::Symbol; +use rustc_span::Symbol; use tracing::trace; use crate::const_eval::{CanAccessMutGlobal, CompileTimeInterpCx, mk_eval_cx_to_read_const_val}; diff --git a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs index 651a797e97c..a729d9325c8 100644 --- a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs +++ b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs @@ -155,6 +155,7 @@ fn check_validity_requirement_lax<'tcx>( } match &this.variants { + Variants::Empty => return Ok(false), Variants::Single { .. } => { // All fields of this single variant have already been checked above, there is nothing // else to do. diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index 81f15ebcbf8..2f0fe64b096 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -9,7 +9,7 @@ rustc_ast = { path = "../rustc_ast" } rustc_ast_lowering = { path = "../rustc_ast_lowering" } rustc_ast_passes = { path = "../rustc_ast_passes" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_borrowck = { path = "../rustc_borrowck" } rustc_builtin_macros = { path = "../rustc_builtin_macros" } rustc_codegen_ssa = { path = "../rustc_codegen_ssa" } diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index b80736f41ad..3dc39fc131a 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}; @@ -45,7 +45,7 @@ use rustc_errors::registry::Registry; use rustc_errors::{ColorConfig, DiagCtxt, ErrCode, FatalError, PResult, markdown}; use rustc_feature::find_gated_cfg; use rustc_interface::util::{self, get_codegen_backend}; -use rustc_interface::{Linker, interface, passes}; +use rustc_interface::{Linker, create_and_enter_global_ctxt, interface, passes}; use rustc_lint::unerased_lint_store; use rustc_metadata::creader::MetadataLoader; use rustc_metadata::locator; @@ -112,7 +112,7 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ crate::DEFAULT_LOCALE_RESOURCE, rustc_ast_lowering::DEFAULT_LOCALE_RESOURCE, rustc_ast_passes::DEFAULT_LOCALE_RESOURCE, - rustc_attr::DEFAULT_LOCALE_RESOURCE, + rustc_attr_parsing::DEFAULT_LOCALE_RESOURCE, rustc_borrowck::DEFAULT_LOCALE_RESOURCE, rustc_builtin_macros::DEFAULT_LOCALE_RESOURCE, rustc_codegen_ssa::DEFAULT_LOCALE_RESOURCE, @@ -387,76 +387,69 @@ fn run_compiler( return early_exit(); } - let linker = compiler.enter(|queries| { + // Parse the crate root source code (doesn't parse submodules yet) + // Everything else is parsed during macro expansion. + let krate = passes::parse(sess); + + // If pretty printing is requested: Figure out the representation, print it and exit + if let Some(pp_mode) = sess.opts.pretty { + if pp_mode.needs_ast_map() { + create_and_enter_global_ctxt(compiler, krate, |tcx| { + tcx.ensure().early_lint_checks(()); + pretty::print(sess, pp_mode, pretty::PrintExtra::NeedsAstMap { tcx }); + passes::write_dep_info(tcx); + }); + } else { + pretty::print(sess, pp_mode, pretty::PrintExtra::AfterParsing { krate: &krate }); + } + trace!("finished pretty-printing"); + return early_exit(); + } + + if callbacks.after_crate_root_parsing(compiler, &krate) == Compilation::Stop { + return early_exit(); + } + + if sess.opts.unstable_opts.parse_crate_root_only { + return early_exit(); + } + + let linker = create_and_enter_global_ctxt(compiler, krate, |tcx| { let early_exit = || { sess.dcx().abort_if_errors(); None }; - // Parse the crate root source code (doesn't parse submodules yet) - // Everything else is parsed during macro expansion. - queries.parse(); - - // If pretty printing is requested: Figure out the representation, print it and exit - if let Some(pp_mode) = sess.opts.pretty { - if pp_mode.needs_ast_map() { - queries.global_ctxt().enter(|tcx| { - tcx.ensure().early_lint_checks(()); - pretty::print(sess, pp_mode, pretty::PrintExtra::NeedsAstMap { tcx }); - passes::write_dep_info(tcx); - }); - } else { - let krate = queries.parse(); - pretty::print(sess, pp_mode, pretty::PrintExtra::AfterParsing { - krate: &*krate.borrow(), - }); - } - trace!("finished pretty-printing"); + // Make sure name resolution and macro expansion is run. + let _ = tcx.resolver_for_lowering(); + + if let Some(metrics_dir) = &sess.opts.unstable_opts.metrics_dir { + dump_feature_usage_metrics(tcx, metrics_dir); + } + + if callbacks.after_expansion(compiler, tcx) == Compilation::Stop { return early_exit(); } - if callbacks.after_crate_root_parsing(compiler, &*queries.parse().borrow()) - == Compilation::Stop + passes::write_dep_info(tcx); + + if sess.opts.output_types.contains_key(&OutputType::DepInfo) + && sess.opts.output_types.len() == 1 { return early_exit(); } - if sess.opts.unstable_opts.parse_crate_root_only { + if sess.opts.unstable_opts.no_analysis { return early_exit(); } - queries.global_ctxt().enter(|tcx| { - // Make sure name resolution and macro expansion is run. - let _ = tcx.resolver_for_lowering(); - - if let Some(metrics_dir) = &sess.opts.unstable_opts.metrics_dir { - dump_feature_usage_metrics(tcx, metrics_dir); - } - - if callbacks.after_expansion(compiler, tcx) == Compilation::Stop { - return early_exit(); - } - - passes::write_dep_info(tcx); - - if sess.opts.output_types.contains_key(&OutputType::DepInfo) - && sess.opts.output_types.len() == 1 - { - return early_exit(); - } - - if sess.opts.unstable_opts.no_analysis { - return early_exit(); - } + tcx.ensure().analysis(()); - tcx.ensure().analysis(()); - - if callbacks.after_analysis(compiler, tcx) == Compilation::Stop { - return early_exit(); - } + if callbacks.after_analysis(compiler, tcx) == Compilation::Stop { + return early_exit(); + } - Some(Linker::codegen_and_build_linker(tcx, &*compiler.codegen_backend)) - }) + Some(Linker::codegen_and_build_linker(tcx, &*compiler.codegen_backend)) }); // Linking is done outside the `compiler.enter()` so that the @@ -467,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_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs index 5df960be307..93f3d2ab911 100644 --- a/compiler/rustc_driver_impl/src/pretty.rs +++ b/compiler/rustc_driver_impl/src/pretty.rs @@ -4,15 +4,13 @@ use std::cell::Cell; use std::fmt::Write; use rustc_ast_pretty::pprust as pprust_ast; -use rustc_errors::FatalError; use rustc_middle::bug; use rustc_middle::mir::{write_mir_graphviz, write_mir_pretty}; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::Session; use rustc_session::config::{OutFileName, PpHirMode, PpMode, PpSourceMode}; use rustc_smir::rustc_internal::pretty::write_smir_pretty; -use rustc_span::FileName; -use rustc_span::symbol::Ident; +use rustc_span::{FileName, Ident}; use tracing::debug; use {rustc_ast as ast, rustc_hir_pretty as pprust_hir}; @@ -312,9 +310,7 @@ pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) { let tcx = ex.tcx(); let mut out = String::new(); rustc_hir_analysis::check_crate(tcx); - if tcx.dcx().has_errors().is_some() { - FatalError.raise(); - } + tcx.dcx().abort_if_errors(); debug!("pretty printing THIR tree"); for did in tcx.hir().body_owners() { let _ = writeln!(out, "{:?}:\n{}\n", did, tcx.thir_tree(did)); @@ -325,9 +321,7 @@ pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) { let tcx = ex.tcx(); let mut out = String::new(); rustc_hir_analysis::check_crate(tcx); - if tcx.dcx().has_errors().is_some() { - FatalError.raise(); - } + tcx.dcx().abort_if_errors(); debug!("pretty printing THIR flat"); for did in tcx.hir().body_owners() { let _ = writeln!(out, "{:?}:\n{}\n", did, tcx.thir_flat(did)); diff --git a/compiler/rustc_error_codes/src/error_codes/E0665.md b/compiler/rustc_error_codes/src/error_codes/E0665.md index ae54d6d1579..caa94423377 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0665.md +++ b/compiler/rustc_error_codes/src/error_codes/E0665.md @@ -1,10 +1,9 @@ -#### Note: this error code is no longer emitted by the compiler. - -The `Default` trait was derived on an enum. +The `Default` trait was derived on an enum without specifying the default +variant. Erroneous code example: -```compile_fail +```compile_fail,E0665 #[derive(Default)] enum Food { Sweet, @@ -16,18 +15,30 @@ The `Default` cannot be derived on an enum for the simple reason that the compiler doesn't know which value to pick by default whereas it can for a struct as long as all its fields implement the `Default` trait as well. -If you still want to implement `Default` on your enum, you'll have to do it "by -hand": +For the case where the desired default variant has no payload, you can +annotate it with `#[default]` to derive it: ``` +#[derive(Default)] enum Food { + #[default] Sweet, Salty, } +``` + +In the case where the default variant does have a payload, you will have to +implement `Default` on your enum manually: + +``` +enum Food { + Sweet(i32), + Salty, +} impl Default for Food { fn default() -> Food { - Food::Sweet + Food::Sweet(1) } } ``` diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index 06bae57638f..66b9adbead0 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -16,6 +16,7 @@ rustc_error_messages = { path = "../rustc_error_messages" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } +rustc_lexer = { path = "../rustc_lexer" } rustc_lint_defs = { path = "../rustc_lint_defs" } rustc_macros = { path = "../rustc_macros" } rustc_serialize = { path = "../rustc_serialize" } diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 4352de3ad25..05b9cbfbc06 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -11,8 +11,7 @@ use rustc_error_messages::{FluentValue, fluent_value_from_str_list_sep_by_and}; use rustc_lint_defs::Applicability; use rustc_macros::{Decodable, Encodable}; use rustc_span::source_map::Spanned; -use rustc_span::symbol::Symbol; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Span, Symbol}; use tracing::debug; use crate::snippet::Style; diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index b4510371323..d179396398f 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -9,9 +9,8 @@ use rustc_abi::TargetDataLayoutErrors; use rustc_ast::util::parser::ExprPrecedence; use rustc_ast_pretty::pprust; use rustc_macros::Subdiagnostic; -use rustc_span::Span; use rustc_span::edition::Edition; -use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent, Symbol}; +use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol}; use rustc_target::spec::{PanicStrategy, SplitDebuginfo, StackProtector, TargetTuple}; use rustc_type_ir::{ClosureKind, FloatTy}; use {rustc_ast as ast, rustc_hir as hir}; diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index ac2f91cdeb3..977721a5b8a 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -19,6 +19,7 @@ use derive_setters::Setters; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_data_structures::sync::{DynSend, IntoDynSyncSend, Lrc}; use rustc_error_messages::{FluentArgs, SpanLabel}; +use rustc_lexer; use rustc_lint_defs::pluralize; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::SourceMap; @@ -1698,9 +1699,14 @@ impl HumanEmitter { if let Some(source_string) = line.line_index.checked_sub(1).and_then(|l| file.get_line(l)) { + // Whitespace can only be removed (aka considered leading) + // if the lexer considers it whitespace. + // non-rustc_lexer::is_whitespace() chars are reported as an + // error (ex. no-break-spaces \u{a0}), and thus can't be considered + // for removal during error reporting. let leading_whitespace = source_string .chars() - .take_while(|c| c.is_whitespace()) + .take_while(|c| rustc_lexer::is_whitespace(*c)) .map(|c| { match c { // Tabs are displayed as 4 spaces @@ -1709,7 +1715,7 @@ impl HumanEmitter { } }) .sum(); - if source_string.chars().any(|c| !c.is_whitespace()) { + if source_string.chars().any(|c| !rustc_lexer::is_whitespace(c)) { whitespace_margin = min(whitespace_margin, leading_whitespace); } } @@ -2523,7 +2529,7 @@ impl HumanEmitter { buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition); } [] => { - // FIXME: needed? Doesn't get excercised in any test. + // FIXME: needed? Doesn't get exercised in any test. self.draw_col_separator_no_space(buffer, *row_num, max_line_num_len + 1); } _ => { diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 25d41462556..58fe3ec4b85 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -1569,18 +1569,18 @@ impl DiagCtxtInner { debug!(?diagnostic); debug!(?self.emitted_diagnostics); - let already_emitted_sub = |sub: &mut Subdiag| { + let not_yet_emitted = |sub: &mut Subdiag| { debug!(?sub); if sub.level != OnceNote && sub.level != OnceHelp { - return false; + return true; } let mut hasher = StableHasher::new(); sub.hash(&mut hasher); let diagnostic_hash = hasher.finish(); debug!(?diagnostic_hash); - !self.emitted_diagnostics.insert(diagnostic_hash) + self.emitted_diagnostics.insert(diagnostic_hash) }; - diagnostic.children.extract_if(already_emitted_sub).for_each(|_| {}); + diagnostic.children.retain_mut(not_yet_emitted); if already_emitted { let msg = "duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`"; diagnostic.sub(Note, msg, MultiSpan::new()); diff --git a/compiler/rustc_errors/src/markdown/parse.rs b/compiler/rustc_errors/src/markdown/parse.rs index 8dd146c1c33..7a991a2ace7 100644 --- a/compiler/rustc_errors/src/markdown/parse.rs +++ b/compiler/rustc_errors/src/markdown/parse.rs @@ -346,7 +346,7 @@ fn parse_with_end_pat<'a>( None } -/// Resturn `(match, residual)` to end of line. The EOL is returned with the +/// Return `(match, residual)` to end of line. The EOL is returned with the /// residual. fn parse_to_newline(buf: &[u8]) -> (&[u8], &[u8]) { buf.iter().position(|ch| *ch == b'\n').map_or((buf, &[]), |pos| buf.split_at(pos)) diff --git a/compiler/rustc_expand/Cargo.toml b/compiler/rustc_expand/Cargo.toml index ce014364b0d..eb93972387d 100644 --- a/compiler/rustc_expand/Cargo.toml +++ b/compiler/rustc_expand/Cargo.toml @@ -12,7 +12,7 @@ doctest = false rustc_ast = { path = "../rustc_ast" } rustc_ast_passes = { path = "../rustc_ast_passes" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index bed500c3032..e6adbc0f0ac 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -4,13 +4,13 @@ use std::path::Component::Prefix; use std::path::{Path, PathBuf}; use std::rc::Rc; -use rustc_ast::attr::MarkedAttrs; +use rustc_ast::attr::{AttributeExt, MarkedAttrs}; use rustc_ast::ptr::P; use rustc_ast::token::Nonterminal; use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{AssocCtxt, Visitor}; use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind}; -use rustc_attr::{self as attr, Deprecation, Stability}; +use rustc_attr_parsing::{self as attr, Deprecation, Stability}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::{self, Lrc}; use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, PResult}; @@ -25,8 +25,7 @@ use rustc_span::def_id::{CrateNum, DefId, LocalDefId}; use rustc_span::edition::Edition; use rustc_span::hygiene::{AstPass, ExpnData, ExpnKind, LocalExpnId, MacroKind}; use rustc_span::source_map::SourceMap; -use rustc_span::symbol::{Ident, Symbol, kw, sym}; -use rustc_span::{DUMMY_SP, FileName, Span}; +use rustc_span::{DUMMY_SP, FileName, Ident, Span, Symbol, kw, sym}; use smallvec::{SmallVec, smallvec}; use thin_vec::ThinVec; @@ -782,10 +781,12 @@ impl SyntaxExtension { } } - fn collapse_debuginfo_by_name(attr: &Attribute) -> Result<CollapseMacroDebuginfo, Span> { + fn collapse_debuginfo_by_name( + attr: &impl AttributeExt, + ) -> Result<CollapseMacroDebuginfo, Span> { let list = attr.meta_item_list(); let Some([MetaItemInner::MetaItem(item)]) = list.as_deref() else { - return Err(attr.span); + return Err(attr.span()); }; if !item.is_word() { return Err(item.span); @@ -805,9 +806,9 @@ impl SyntaxExtension { /// | (unspecified) | no | if-ext | if-ext | yes | /// | external | no | if-ext | if-ext | yes | /// | yes | yes | yes | yes | yes | - fn get_collapse_debuginfo(sess: &Session, attrs: &[ast::Attribute], ext: bool) -> bool { + fn get_collapse_debuginfo(sess: &Session, attrs: &[impl AttributeExt], ext: bool) -> bool { let flag = sess.opts.cg.collapse_macro_debuginfo; - let attr = attr::find_by_name(attrs, sym::collapse_debuginfo) + let attr = ast::attr::find_by_name(attrs, sym::collapse_debuginfo) .and_then(|attr| { Self::collapse_debuginfo_by_name(attr) .map_err(|span| { @@ -816,7 +817,7 @@ impl SyntaxExtension { .ok() }) .unwrap_or_else(|| { - if attr::contains_name(attrs, sym::rustc_builtin_macro) { + if ast::attr::contains_name(attrs, sym::rustc_builtin_macro) { CollapseMacroDebuginfo::Yes } else { CollapseMacroDebuginfo::Unspecified @@ -842,20 +843,20 @@ impl SyntaxExtension { helper_attrs: Vec<Symbol>, edition: Edition, name: Symbol, - attrs: &[ast::Attribute], + attrs: &[impl AttributeExt], is_local: bool, ) -> SyntaxExtension { let allow_internal_unstable = - attr::allow_internal_unstable(sess, attrs).collect::<Vec<Symbol>>(); + rustc_attr_parsing::allow_internal_unstable(sess, attrs).collect::<Vec<Symbol>>(); - let allow_internal_unsafe = attr::contains_name(attrs, sym::allow_internal_unsafe); - let local_inner_macros = attr::find_by_name(attrs, sym::macro_export) + let allow_internal_unsafe = ast::attr::contains_name(attrs, sym::allow_internal_unsafe); + let local_inner_macros = ast::attr::find_by_name(attrs, sym::macro_export) .and_then(|macro_export| macro_export.meta_item_list()) - .is_some_and(|l| attr::list_contains_name(&l, sym::local_inner_macros)); + .is_some_and(|l| ast::attr::list_contains_name(&l, sym::local_inner_macros)); let collapse_debuginfo = Self::get_collapse_debuginfo(sess, attrs, !is_local); tracing::debug!(?name, ?local_inner_macros, ?collapse_debuginfo, ?allow_internal_unsafe); - let (builtin_name, helper_attrs) = attr::find_by_name(attrs, sym::rustc_builtin_macro) + let (builtin_name, helper_attrs) = ast::attr::find_by_name(attrs, sym::rustc_builtin_macro) .map(|attr| { // Override `helper_attrs` passed above if it's a built-in macro, // marking `proc_macro_derive` macros as built-in is not a realistic use case. @@ -1305,7 +1306,7 @@ pub fn resolve_path(sess: &Session, path: impl Into<PathBuf>, span: Span) -> PRe pub fn parse_macro_name_and_helper_attrs( dcx: DiagCtxtHandle<'_>, - attr: &Attribute, + attr: &impl AttributeExt, macro_type: &str, ) -> Option<(Symbol, Vec<Symbol>)> { // Once we've located the `#[proc_macro_derive]` attribute, verify @@ -1313,7 +1314,7 @@ pub fn parse_macro_name_and_helper_attrs( // `#[proc_macro_derive(Foo, attributes(A, ..))]` let list = attr.meta_item_list()?; let ([trait_attr] | [trait_attr, _]) = list.as_slice() else { - dcx.emit_err(errors::AttrNoArguments { span: attr.span }); + dcx.emit_err(errors::AttrNoArguments { span: attr.span() }); return None; }; let Some(trait_attr) = trait_attr.meta_item() else { diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index a673e2e3250..22bfda34cc0 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -4,8 +4,7 @@ use rustc_ast::{ self as ast, AttrVec, BlockCheckMode, Expr, LocalKind, MatchKind, PatKind, UnOp, attr, token, }; use rustc_span::source_map::Spanned; -use rustc_span::symbol::{Ident, Symbol, kw, sym}; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use thin_vec::{ThinVec, thin_vec}; use crate::base::ExtCtxt; diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index dc6aa110f45..91624c7554c 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -8,7 +8,7 @@ use rustc_ast::tokenstream::{ use rustc_ast::{ self as ast, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem, MetaItemInner, NodeId, }; -use rustc_attr as attr; +use rustc_attr_parsing as attr; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_feature::{ ACCEPTED_LANG_FEATURES, AttributeSafety, EnabledLangFeature, EnabledLibFeature, Features, @@ -18,8 +18,7 @@ use rustc_lint_defs::BuiltinLintDiag; use rustc_parse::validate_attr; use rustc_session::Session; use rustc_session::parse::feature_err; -use rustc_span::Span; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::{Span, Symbol, sym}; use thin_vec::ThinVec; use tracing::instrument; @@ -362,7 +361,7 @@ impl<'a> StripUnconfigured<'a> { )); let tokens = Some(LazyAttrTokenStream::new(AttrTokenStream::new(trees))); - let attr = attr::mk_attr_from_item( + let attr = ast::attr::mk_attr_from_item( &self.sess.psess.attr_id_generator, item, tokens, diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index 7bd7c305539..89bdc7b6dfa 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -4,8 +4,7 @@ use rustc_ast::ast; use rustc_errors::codes::*; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_session::Limit; -use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent}; -use rustc_span::{Span, Symbol}; +use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol}; #[derive(Diagnostic)] #[diag(expand_expr_repeat_no_syntax_vars)] diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 690e080fbfc..ec497f6f8f1 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -29,8 +29,7 @@ use rustc_session::lint::builtin::{UNUSED_ATTRIBUTES, UNUSED_DOC_COMMENTS}; use rustc_session::parse::feature_err; use rustc_session::{Limit, Session}; use rustc_span::hygiene::SyntaxContext; -use rustc_span::symbol::{Ident, sym}; -use rustc_span::{ErrorGuaranteed, FileName, LocalExpnId, Span}; +use rustc_span::{ErrorGuaranteed, FileName, Ident, LocalExpnId, Span, sym}; use smallvec::SmallVec; use crate::base::*; @@ -1913,7 +1912,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { self.cx.current_expansion.lint_node_id, BuiltinLintDiag::UnusedDocComment(attr.span), ); - } else if rustc_attr::is_builtin_attr(attr) { + } else if rustc_attr_parsing::is_builtin_attr(attr) { let attr_name = attr.ident().unwrap().name; // `#[cfg]` and `#[cfg_attr]` are special - they are // eagerly evaluated. diff --git a/compiler/rustc_expand/src/mbe.rs b/compiler/rustc_expand/src/mbe.rs index e5d098f63d6..4ff8c02bcdb 100644 --- a/compiler/rustc_expand/src/mbe.rs +++ b/compiler/rustc_expand/src/mbe.rs @@ -16,8 +16,7 @@ use metavar_expr::MetaVarExpr; use rustc_ast::token::{Delimiter, NonterminalKind, Token, TokenKind}; use rustc_ast::tokenstream::{DelimSpacing, DelimSpan}; use rustc_macros::{Decodable, Encodable}; -use rustc_span::Span; -use rustc_span::symbol::Ident; +use rustc_span::{Ident, Span}; /// Contains the sub-token-trees of a "delimited" token tree such as `(a b c)`. /// The delimiters are not represented explicitly in the `tts` vector. diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs index 77b8d228922..9f48835e15d 100644 --- a/compiler/rustc_expand/src/mbe/diagnostics.rs +++ b/compiler/rustc_expand/src/mbe/diagnostics.rs @@ -7,8 +7,7 @@ use rustc_macros::Subdiagnostic; use rustc_parse::parser::{Parser, Recovery, token_descr}; use rustc_session::parse::ParseSess; use rustc_span::source_map::SourceMap; -use rustc_span::symbol::Ident; -use rustc_span::{ErrorGuaranteed, Span}; +use rustc_span::{ErrorGuaranteed, Ident, Span}; use tracing::debug; use super::macro_rules::{NoopTracker, parser_from_cx}; diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs index 1498b9cbd5d..729dec2bfbd 100644 --- a/compiler/rustc_expand/src/mbe/macro_check.rs +++ b/compiler/rustc_expand/src/mbe/macro_check.rs @@ -115,8 +115,7 @@ use rustc_lint_defs::BuiltinLintDiag; use rustc_session::lint::builtin::{META_VARIABLE_MISUSE, MISSING_FRAGMENT_SPECIFIER}; use rustc_session::parse::ParseSess; use rustc_span::edition::Edition; -use rustc_span::symbol::{MacroRulesNormalizedIdent, kw}; -use rustc_span::{ErrorGuaranteed, Span}; +use rustc_span::{ErrorGuaranteed, MacroRulesNormalizedIdent, Span, kw}; use smallvec::SmallVec; use super::quoted::VALID_FRAGMENT_NAMES_MSG; diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index 2a8dddc1e00..d709fd79281 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -82,8 +82,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::ErrorGuaranteed; use rustc_lint_defs::pluralize; use rustc_parse::parser::{ParseNtResult, Parser, token_descr}; -use rustc_span::Span; -use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent}; +use rustc_span::{Ident, MacroRulesNormalizedIdent, Span}; use crate::mbe::macro_rules::Tracker; use crate::mbe::{KleeneOp, TokenTree}; diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index a373c753cc1..7ac9f453bae 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -3,13 +3,14 @@ use std::collections::hash_map::Entry; use std::{mem, slice}; use ast::token::IdentIsRaw; +use rustc_ast::attr::AttributeExt; use rustc_ast::token::NtPatKind::*; use rustc_ast::token::TokenKind::*; use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind}; use rustc_ast::tokenstream::{DelimSpan, TokenStream}; use rustc_ast::{self as ast, DUMMY_NODE_ID, NodeId}; use rustc_ast_pretty::pprust; -use rustc_attr::{self as attr, TransparencyError}; +use rustc_attr_parsing::{self as attr, TransparencyError}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::{Applicability, ErrorGuaranteed}; use rustc_feature::Features; @@ -20,10 +21,9 @@ use rustc_lint_defs::builtin::{ use rustc_parse::parser::{ParseNtResult, Parser, Recovery}; use rustc_session::Session; use rustc_session::parse::ParseSess; -use rustc_span::Span; use rustc_span::edition::Edition; use rustc_span::hygiene::Transparency; -use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent, kw, sym}; +use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, kw, sym}; use tracing::{debug, instrument, trace, trace_span}; use super::diagnostics; @@ -371,7 +371,7 @@ pub fn compile_declarative_macro( features: &Features, macro_def: &ast::MacroDef, ident: Ident, - attrs: &[ast::Attribute], + attrs: &[impl AttributeExt], span: Span, node_id: NodeId, edition: Edition, diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs index 810a5d30c7e..1ccb070f83a 100644 --- a/compiler/rustc_expand/src/mbe/metavar_expr.rs +++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs @@ -1,12 +1,11 @@ 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}; use rustc_macros::{Decodable, Encodable}; use rustc_session::parse::ParseSess; -use rustc_span::symbol::Ident; -use rustc_span::{Span, Symbol}; +use rustc_span::{Ident, Span, Symbol}; pub(crate) const RAW_IDENT_ERR: &str = "`${concat(..)}` currently does not support raw identifiers"; pub(crate) const UNSUPPORTED_CONCAT_ELEM_ERR: &str = "expected identifier or string literal"; @@ -39,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(); @@ -74,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) { @@ -143,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() { @@ -159,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", @@ -181,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> { @@ -204,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> { @@ -236,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> { @@ -251,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; } @@ -261,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; } @@ -271,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 36094707fac..a27d47892e4 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -1,12 +1,12 @@ 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; use rustc_session::Session; use rustc_session::parse::feature_err; -use rustc_span::Span; use rustc_span::edition::Edition; -use rustc_span::symbol::{Ident, kw, sym}; +use rustc_span::{Ident, Span, kw, sym}; use crate::errors; use crate::mbe::macro_parser::count_metavar_decls; @@ -49,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()); @@ -143,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<Item = &'a tokenstream::TokenTree>, + outer_iter: &mut TokenStreamIter<'a>, parsing_patterns: bool, sess: &Session, node_id: NodeId, @@ -163,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<dyn Iterator<Item = &tokenstream::TokenTree>>; - 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. @@ -230,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 }; @@ -313,11 +314,11 @@ fn kleene_op(token: &Token) -> Option<KleeneOp> { /// - 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<Item = &'a tokenstream::TokenTree>, +fn parse_kleene_op( + iter: &mut TokenStreamIter<'_>, span: Span, ) -> Result<Result<(KleeneOp, Span), Token>, 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())), @@ -334,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<Item = &'a tokenstream::TokenTree>, +fn parse_sep_and_kleene_op( + iter: &mut TokenStreamIter<'_>, span: Span, sess: &Session, ) -> (Option<Token>, 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/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index b77d02e630a..4fb1eadd486 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -11,8 +11,9 @@ use rustc_parse::lexer::nfc_normalize; use rustc_parse::parser::ParseNtResult; use rustc_session::parse::{ParseSess, SymbolGallery}; use rustc_span::hygiene::{LocalExpnId, Transparency}; -use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent, sym}; -use rustc_span::{Span, Symbol, SyntaxContext, with_metavar_spans}; +use rustc_span::{ + Ident, MacroRulesNormalizedIdent, Span, Symbol, SyntaxContext, sym, with_metavar_spans, +}; use smallvec::{SmallVec, smallvec}; use crate::errors::{ diff --git a/compiler/rustc_expand/src/module.rs b/compiler/rustc_expand/src/module.rs index 85ea42e78ad..9c35b26772b 100644 --- a/compiler/rustc_expand/src/module.rs +++ b/compiler/rustc_expand/src/module.rs @@ -2,13 +2,12 @@ use std::iter::once; use std::path::{self, Path, PathBuf}; use rustc_ast::ptr::P; -use rustc_ast::{AttrVec, Attribute, Inline, Item, ModSpans, token}; +use rustc_ast::{AttrVec, Attribute, Inline, Item, ModSpans}; use rustc_errors::{Diag, ErrorGuaranteed}; -use rustc_parse::{new_parser_from_file, unwrap_or_emit_fatal, validate_attr}; +use rustc_parse::{exp, new_parser_from_file, unwrap_or_emit_fatal, validate_attr}; use rustc_session::Session; use rustc_session::parse::ParseSess; -use rustc_span::Span; -use rustc_span::symbol::{Ident, sym}; +use rustc_span::{Ident, Span, sym}; use thin_vec::ThinVec; use crate::base::ModuleData; @@ -71,7 +70,7 @@ pub(crate) fn parse_external_mod( let mut parser = unwrap_or_emit_fatal(new_parser_from_file(&sess.psess, &mp.file_path, Some(span))); let (inner_attrs, items, inner_span) = - parser.parse_mod(&token::Eof).map_err(|err| ModError::ParserError(err))?; + parser.parse_mod(exp!(Eof)).map_err(|err| ModError::ParserError(err))?; attrs.extend(inner_attrs); (items, inner_span, mp.file_path) }; diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index 9e459bd81a1..e969f2d4fb5 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -4,8 +4,7 @@ use rustc_ast::token::Delimiter; use rustc_ast::visit::AssocCtxt; use rustc_ast::{self as ast, Safety}; use rustc_data_structures::fx::FxHashMap; -use rustc_span::DUMMY_SP; -use rustc_span::symbol::Ident; +use rustc_span::{DUMMY_SP, Ident}; use smallvec::{SmallVec, smallvec}; use thin_vec::ThinVec; diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 263df235b3e..7eb09a64e96 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -15,11 +15,10 @@ use rustc_data_structures::sync::Lrc; use rustc_errors::{Diag, ErrorGuaranteed, MultiSpan, PResult}; use rustc_parse::lexer::nfc_normalize; use rustc_parse::parser::Parser; -use rustc_parse::{new_parser_from_source_str, source_str_to_stream, unwrap_or_emit_fatal}; +use rustc_parse::{exp, new_parser_from_source_str, source_str_to_stream, unwrap_or_emit_fatal}; use rustc_session::parse::ParseSess; use rustc_span::def_id::CrateNum; -use rustc_span::symbol::{self, Symbol, sym}; -use rustc_span::{BytePos, FileName, Pos, SourceFile, Span}; +use rustc_span::{BytePos, FileName, Pos, SourceFile, Span, Symbol, sym}; use smallvec::{SmallVec, smallvec}; use crate::base::ExtCtxt; @@ -112,9 +111,9 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec<TokenTree<TokenStre // Estimate the capacity as `stream.len()` rounded up to the next power // of two to limit the number of required reallocations. let mut trees = Vec::with_capacity(stream.len().next_power_of_two()); - let mut cursor = stream.trees(); + let mut iter = stream.iter(); - while let Some(tree) = cursor.next() { + while let Some(tree) = iter.next() { let (Token { kind, span }, joint) = match tree.clone() { tokenstream::TokenTree::Delimited(span, _, delim, tts) => { let delimiter = pm::Delimiter::from_internal(delim); @@ -230,7 +229,7 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec<TokenTree<TokenStre })), Lifetime(name, is_raw) => { - let ident = symbol::Ident::new(name, span).without_first_quote(); + let ident = rustc_span::Ident::new(name, span).without_first_quote(); trees.extend([ TokenTree::Punct(Punct { ch: b'\'', joint: true, span }), TokenTree::Ident(Ident { sym: ident.name, is_raw: is_raw.into(), span }), @@ -474,7 +473,7 @@ impl server::FreeFunctions for Rustc<'_, '_> { unwrap_or_emit_fatal(new_parser_from_source_str(self.psess(), name, s.to_owned())); let first_span = parser.token.span.data(); - let minus_present = parser.eat(&token::BinOp(token::Minus)); + let minus_present = parser.eat(exp!(Minus)); let lit_span = parser.token.span.data(); let token::Literal(mut lit) = parser.token.kind else { diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index c5913ed27cf..5a9b8c43e74 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -1,6 +1,6 @@ //! List of the accepted feature gates. -use rustc_span::symbol::sym; +use rustc_span::sym; use super::{Feature, to_nonzero}; @@ -157,6 +157,9 @@ declare_features! ( (accepted, const_refs_to_static, "1.83.0", Some(119618)), /// Allows implementing `Copy` for closures where possible (RFC 2132). (accepted, copy_closures, "1.26.0", Some(44490)), + /// Allows function attribute `#[coverage(on/off)]`, to control coverage + /// instrumentation of that function. + (accepted, coverage_attribute, "CURRENT_RUSTC_VERSION", Some(84605)), /// Allows `crate` in paths. (accepted, crate_in_paths, "1.30.0", Some(45477)), /// Allows users to provide classes for fenced code block using `class:classname`. @@ -175,6 +178,8 @@ declare_features! ( (accepted, destructuring_assignment, "1.59.0", Some(71126)), /// Allows using the `#[diagnostic]` attribute tool namespace (accepted, diagnostic_namespace, "1.78.0", Some(111996)), + /// Controls errors in trait implementations. + (accepted, do_not_recommend, "CURRENT_RUSTC_VERSION", Some(51992)), /// Allows `#[doc(alias = "...")]`. (accepted, doc_alias, "1.48.0", Some(50146)), /// Allows `..` in tuple (struct) patterns. diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 3bf485c2eb6..63e5ebb8688 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -6,7 +6,7 @@ use AttributeDuplicates::*; use AttributeGate::*; use AttributeType::*; use rustc_data_structures::fx::FxHashMap; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::{Symbol, sym}; use crate::{Features, Stability}; @@ -480,10 +480,9 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ template!(List: "address, kcfi, memory, thread"), DuplicatesOk, EncodeCrossCrate::No, experimental!(no_sanitize) ), - gated!( + ungated!( coverage, Normal, template!(OneOf: &[sym::off, sym::on]), ErrorPreceding, EncodeCrossCrate::No, - coverage_attribute, experimental!(coverage) ), ungated!( @@ -913,12 +912,21 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!( rustc_deny_explicit_impl, AttributeType::Normal, - template!(List: "implement_via_object = (true|false)"), + template!(Word), ErrorFollowing, EncodeCrossCrate::No, "#[rustc_deny_explicit_impl] enforces that a trait can have no user-provided impls" ), rustc_attr!( + rustc_do_not_implement_via_object, + AttributeType::Normal, + template!(Word), + ErrorFollowing, + EncodeCrossCrate::No, + "#[rustc_do_not_implement_via_object] opts out of the automatic trait impl for trait objects \ + (`impl Trait for dyn Trait`)" + ), + rustc_attr!( rustc_has_incoherent_inherent_impls, AttributeType::Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::Yes, "#[rustc_has_incoherent_inherent_impls] allows the addition of incoherent inherent impls for \ @@ -1188,10 +1196,9 @@ pub static BUILTIN_ATTRIBUTE_MAP: LazyLock<FxHashMap<Symbol, &BuiltinAttribute>> map }); -pub fn is_stable_diagnostic_attribute(sym: Symbol, features: &Features) -> bool { +pub fn is_stable_diagnostic_attribute(sym: Symbol, _features: &Features) -> bool { match sym { - sym::on_unimplemented => true, - sym::do_not_recommend => features.do_not_recommend(), + sym::on_unimplemented | sym::do_not_recommend => true, _ => false, } } diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index 5d27b8f542c..6db512ace1b 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -28,7 +28,7 @@ mod tests; use std::num::NonZero; -use rustc_span::symbol::Symbol; +use rustc_span::Symbol; #[derive(Debug, Clone)] pub struct Feature { diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index e25840ba5fc..388ed9d08fa 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -1,6 +1,6 @@ //! List of the removed feature gates. -use rustc_span::symbol::sym; +use rustc_span::sym; use super::{Feature, to_nonzero}; diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 45c63b03fe9..ebb07195a28 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -3,8 +3,7 @@ use std::path::PathBuf; use rustc_data_structures::fx::FxHashSet; -use rustc_span::Span; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::{Span, Symbol, sym}; use super::{Feature, to_nonzero}; @@ -332,6 +331,7 @@ declare_features! ( (unstable, hexagon_target_feature, "1.27.0", Some(44839)), (unstable, lahfsahf_target_feature, "1.78.0", Some(44839)), (unstable, loongarch_target_feature, "1.73.0", Some(44839)), + (unstable, m68k_target_feature, "CURRENT_RUSTC_VERSION", Some(134328)), (unstable, mips_target_feature, "1.27.0", Some(44839)), (unstable, powerpc_target_feature, "1.27.0", Some(44839)), (unstable, prfchw_target_feature, "1.78.0", Some(44839)), @@ -447,9 +447,6 @@ declare_features! ( (unstable, coroutine_clone, "1.65.0", Some(95360)), /// Allows defining coroutines. (unstable, coroutines, "1.21.0", Some(43122)), - /// Allows function attribute `#[coverage(on/off)]`, to control coverage - /// instrumentation of that function. - (unstable, coverage_attribute, "1.74.0", Some(84605)), /// Allows non-builtin attributes in inner attribute position. (unstable, custom_inner_attributes, "1.30.0", Some(54726)), /// Allows custom test frameworks with `#![test_runner]` and `#[test_case]`. @@ -465,8 +462,6 @@ declare_features! ( (unstable, deprecated_suggestion, "1.61.0", Some(94785)), /// Allows deref patterns. (incomplete, deref_patterns, "1.79.0", Some(87121)), - /// Controls errors in trait implementations. - (unstable, do_not_recommend, "1.67.0", Some(51992)), /// Tells rustdoc to automatically generate `#[doc(cfg(...))]`. (unstable, doc_auto_cfg, "1.58.0", Some(43781)), /// Allows `#[doc(cfg(...))]`. diff --git a/compiler/rustc_hir/Cargo.toml b/compiler/rustc_hir/Cargo.toml index 85c6da83379..5bfc4756ec6 100644 --- a/compiler/rustc_hir/Cargo.toml +++ b/compiler/rustc_hir/Cargo.toml @@ -16,5 +16,6 @@ rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } +thin-vec = "0.2.12" tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_hir/src/arena.rs b/compiler/rustc_hir/src/arena.rs index f1f624269ae..88c0d223fd3 100644 --- a/compiler/rustc_hir/src/arena.rs +++ b/compiler/rustc_hir/src/arena.rs @@ -6,7 +6,7 @@ macro_rules! arena_types { $macro!([ // HIR types [] asm_template: rustc_ast::InlineAsmTemplatePiece, - [] attribute: rustc_ast::Attribute, + [] attribute: rustc_hir::Attribute, [] owner_info: rustc_hir::OwnerInfo<'tcx>, [] use_path: rustc_hir::UsePath<'tcx>, [] lit: rustc_hir::Lit, diff --git a/compiler/rustc_hir/src/definitions.rs b/compiler/rustc_hir/src/definitions.rs index d85f9586d42..dc527240f74 100644 --- a/compiler/rustc_hir/src/definitions.rs +++ b/compiler/rustc_hir/src/definitions.rs @@ -11,7 +11,7 @@ use rustc_data_structures::stable_hasher::{Hash64, StableHasher}; use rustc_data_structures::unord::UnordMap; use rustc_index::IndexVec; use rustc_macros::{Decodable, Encodable}; -use rustc_span::symbol::{Symbol, kw, sym}; +use rustc_span::{Symbol, kw, sym}; use tracing::{debug, instrument}; pub use crate::def_id::DefPathHash; diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 357033a9e18..8cea269f298 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1,10 +1,13 @@ use std::fmt; use rustc_abi::ExternAbi; +// ignore-tidy-filelength +use rustc_ast::attr::AttributeExt; +use rustc_ast::token::CommentKind; use rustc_ast::util::parser::{AssocOp, ExprPrecedence}; use rustc_ast::{ - self as ast, Attribute, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label, - LitKind, TraitObjectSyntax, UintTy, + self as ast, AttrId, AttrStyle, DelimArgs, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, + IntTy, Label, LitKind, MetaItemInner, MetaItemLit, TraitObjectSyntax, UintTy, }; pub use rustc_ast::{ BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity, ByRef, CaptureBy, @@ -17,10 +20,10 @@ use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::def_id::LocalDefId; use rustc_span::hygiene::MacroKind; use rustc_span::source_map::Spanned; -use rustc_span::symbol::{Ident, Symbol, kw, sym}; -use rustc_span::{BytePos, DUMMY_SP, ErrorGuaranteed, Span}; +use rustc_span::{BytePos, DUMMY_SP, ErrorGuaranteed, Ident, Span, Symbol, kw, sym}; use rustc_target::asm::InlineAsmRegOrRegClass; use smallvec::SmallVec; +use thin_vec::ThinVec; use tracing::debug; use crate::LangItem; @@ -937,6 +940,250 @@ pub struct ParentedNode<'tcx> { pub node: Node<'tcx>, } +/// Arguments passed to an attribute macro. +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable)] +pub enum AttrArgs { + /// No arguments: `#[attr]`. + Empty, + /// Delimited arguments: `#[attr()/[]/{}]`. + Delimited(DelimArgs), + /// Arguments of a key-value attribute: `#[attr = "value"]`. + Eq { + /// Span of the `=` token. + eq_span: Span, + /// The "value". + expr: MetaItemLit, + }, +} + +#[derive(Clone, Debug, Encodable, Decodable)] +pub enum AttrKind { + /// A normal attribute. + Normal(Box<AttrItem>), + + /// A doc comment (e.g. `/// ...`, `//! ...`, `/** ... */`, `/*! ... */`). + /// Doc attributes (e.g. `#[doc="..."]`) are represented with the `Normal` + /// variant (which is much less compact and thus more expensive). + DocComment(CommentKind, Symbol), +} + +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable)] +pub struct AttrPath { + pub segments: Box<[Ident]>, + pub span: Span, +} + +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable)] +pub struct AttrItem { + pub unsafety: Safety, + // Not lowered to hir::Path because we have no NodeId to resolve to. + pub path: AttrPath, + pub args: AttrArgs, +} + +#[derive(Clone, Debug, Encodable, Decodable)] +pub struct Attribute { + pub kind: AttrKind, + pub id: AttrId, + /// Denotes if the attribute decorates the following construct (outer) + /// or the construct this attribute is contained within (inner). + pub style: AttrStyle, + pub span: Span, +} + +impl Attribute { + pub fn get_normal_item(&self) -> &AttrItem { + match &self.kind { + AttrKind::Normal(normal) => &normal, + AttrKind::DocComment(..) => panic!("unexpected doc comment"), + } + } + + pub fn unwrap_normal_item(self) -> AttrItem { + match self.kind { + AttrKind::Normal(normal) => *normal, + AttrKind::DocComment(..) => panic!("unexpected doc comment"), + } + } + + pub fn value_lit(&self) -> Option<&MetaItemLit> { + match &self.kind { + AttrKind::Normal(n) => match n.as_ref() { + AttrItem { args: AttrArgs::Eq { expr, .. }, .. } => Some(expr), + _ => None, + }, + _ => None, + } + } +} + +impl AttributeExt for Attribute { + fn id(&self) -> AttrId { + self.id + } + + fn meta_item_list(&self) -> Option<ThinVec<ast::MetaItemInner>> { + match &self.kind { + AttrKind::Normal(n) => match n.as_ref() { + AttrItem { args: AttrArgs::Delimited(d), .. } => { + ast::MetaItemKind::list_from_tokens(d.tokens.clone()) + } + _ => None, + }, + _ => None, + } + } + + fn value_str(&self) -> Option<Symbol> { + self.value_lit().and_then(|x| x.value_str()) + } + + fn value_span(&self) -> Option<Span> { + self.value_lit().map(|i| i.span) + } + + /// For a single-segment attribute, returns its name; otherwise, returns `None`. + fn ident(&self) -> Option<Ident> { + match &self.kind { + AttrKind::Normal(n) => { + if let [ident] = n.path.segments.as_ref() { + Some(*ident) + } else { + None + } + } + AttrKind::DocComment(..) => None, + } + } + + fn path_matches(&self, name: &[Symbol]) -> bool { + match &self.kind { + AttrKind::Normal(n) => { + n.path.segments.len() == name.len() + && n.path.segments.iter().zip(name).all(|(s, n)| s.name == *n) + } + AttrKind::DocComment(..) => false, + } + } + + fn is_doc_comment(&self) -> bool { + matches!(self.kind, AttrKind::DocComment(..)) + } + + fn span(&self) -> Span { + self.span + } + + fn is_word(&self) -> bool { + match &self.kind { + AttrKind::Normal(n) => { + matches!(n.args, AttrArgs::Empty) + } + AttrKind::DocComment(..) => false, + } + } + + fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>> { + match &self.kind { + AttrKind::Normal(n) => Some(n.path.segments.iter().copied().collect()), + AttrKind::DocComment(..) => None, + } + } + + fn doc_str(&self) -> Option<Symbol> { + match &self.kind { + AttrKind::DocComment(.., data) => Some(*data), + AttrKind::Normal(_) if self.has_name(sym::doc) => self.value_str(), + _ => None, + } + } + fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> { + match &self.kind { + AttrKind::DocComment(kind, data) => Some((*data, *kind)), + AttrKind::Normal(_) if self.name_or_empty() == sym::doc => { + self.value_str().map(|s| (s, CommentKind::Line)) + } + _ => None, + } + } + + fn style(&self) -> AttrStyle { + self.style + } +} + +// FIXME(fn_delegation): use function delegation instead of manually forwarding +impl Attribute { + pub fn id(&self) -> AttrId { + AttributeExt::id(self) + } + + pub fn name_or_empty(&self) -> Symbol { + AttributeExt::name_or_empty(self) + } + + pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> { + AttributeExt::meta_item_list(self) + } + + pub fn value_str(&self) -> Option<Symbol> { + AttributeExt::value_str(self) + } + + pub fn value_span(&self) -> Option<Span> { + AttributeExt::value_span(self) + } + + pub fn ident(&self) -> Option<Ident> { + AttributeExt::ident(self) + } + + pub fn path_matches(&self, name: &[Symbol]) -> bool { + AttributeExt::path_matches(self, name) + } + + pub fn is_doc_comment(&self) -> bool { + AttributeExt::is_doc_comment(self) + } + + #[inline] + pub fn has_name(&self, name: Symbol) -> bool { + AttributeExt::has_name(self, name) + } + + pub fn span(&self) -> Span { + AttributeExt::span(self) + } + + pub fn is_word(&self) -> bool { + AttributeExt::is_word(self) + } + + pub fn path(&self) -> SmallVec<[Symbol; 1]> { + AttributeExt::path(self) + } + + pub fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>> { + AttributeExt::ident_path(self) + } + + pub fn doc_str(&self) -> Option<Symbol> { + AttributeExt::doc_str(self) + } + + pub fn is_proc_macro_attr(&self) -> bool { + AttributeExt::is_proc_macro_attr(self) + } + + pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> { + AttributeExt::doc_str_and_comment_kind(self) + } + + pub fn style(&self) -> AttrStyle { + AttributeExt::style(self) + } +} + /// Attributes owned by a HIR owner. #[derive(Debug)] pub struct AttributeMap<'tcx> { @@ -1696,11 +1943,15 @@ pub struct Expr<'hir> { impl Expr<'_> { pub fn precedence(&self) -> ExprPrecedence { - match self.kind { - ExprKind::Closure { .. } => ExprPrecedence::Closure, + match &self.kind { + ExprKind::Closure(closure) => { + match closure.fn_decl.output { + FnRetTy::DefaultReturn(_) => ExprPrecedence::Jump, + FnRetTy::Return(_) => ExprPrecedence::Unambiguous, + } + } ExprKind::Break(..) - | ExprKind::Continue(..) | ExprKind::Ret(..) | ExprKind::Yield(..) | ExprKind::Become(..) => ExprPrecedence::Jump, @@ -1726,6 +1977,7 @@ impl Expr<'_> { | ExprKind::Block(..) | ExprKind::Call(..) | ExprKind::ConstBlock(_) + | ExprKind::Continue(..) | ExprKind::Field(..) | ExprKind::If(..) | ExprKind::Index(..) @@ -1743,7 +1995,7 @@ impl Expr<'_> { | ExprKind::UnsafeBinderCast(..) | ExprKind::Err(_) => ExprPrecedence::Unambiguous, - ExprKind::DropTemps(ref expr, ..) => expr.precedence(), + ExprKind::DropTemps(expr, ..) => expr.precedence(), } } @@ -2025,6 +2277,22 @@ pub fn is_range_literal(expr: &Expr<'_>) -> bool { } } +/// Checks if the specified expression needs parentheses for prefix +/// or postfix suggestions to be valid. +/// For example, `a + b` requires parentheses to suggest `&(a + b)`, +/// but just `a` does not. +/// Similarly, `(a + b).c()` also requires parentheses. +/// This should not be used for other types of suggestions. +pub fn expr_needs_parens(expr: &Expr<'_>) -> bool { + match expr.kind { + // parenthesize if needed (Issue #46756) + ExprKind::Cast(_, _) | ExprKind::Binary(_, _, _) => true, + // parenthesize borrows of range literals (Issue #54505) + _ if is_range_literal(expr) => true, + _ => false, + } +} + #[derive(Debug, Clone, Copy, HashStable_Generic)] pub enum ExprKind<'hir> { /// Allow anonymous constants from an inline `const` block @@ -3418,6 +3686,19 @@ impl Safety { Self::Safe => "", } } + + #[inline] + pub fn is_unsafe(self) -> bool { + !self.is_safe() + } + + #[inline] + pub fn is_safe(self) -> bool { + match self { + Self::Unsafe => false, + Self::Safe => true, + } + } } impl fmt::Display for Safety { @@ -3462,7 +3743,7 @@ impl FnHeader { } pub fn is_unsafe(&self) -> bool { - matches!(&self.safety, Safety::Unsafe) + self.safety.is_unsafe() } } diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 6d481f7536a..387a195cb29 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -64,11 +64,10 @@ //! This order consistency is required in a few places in rustc, for //! example coroutine inference, and possibly also HIR borrowck. +use rustc_ast::Label; use rustc_ast::visit::{VisitorResult, try_visit, visit_opt, walk_list}; -use rustc_ast::{Attribute, Label}; -use rustc_span::Span; use rustc_span::def_id::LocalDefId; -use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::{Ident, Span, Symbol}; use crate::hir::*; diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 15cb331d07a..fae3b778d7b 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -7,12 +7,11 @@ //! * Traits that represent operators; e.g., `Add`, `Sub`, `Index`. //! * Functions called by the compiler itself. -use rustc_ast as ast; +use rustc_ast::attr::AttributeExt; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; -use rustc_span::Span; -use rustc_span::symbol::{Symbol, kw, sym}; +use rustc_span::{Span, Symbol, kw, sym}; use crate::def_id::DefId; use crate::{MethodKind, Target}; @@ -153,11 +152,11 @@ impl<CTX> HashStable<CTX> for LangItem { /// Extracts the first `lang = "$name"` out of a list of attributes. /// The `#[panic_handler]` attribute is also extracted out when found. -pub fn extract(attrs: &[ast::Attribute]) -> Option<(Symbol, Span)> { +pub fn extract(attrs: &[impl AttributeExt]) -> Option<(Symbol, Span)> { attrs.iter().find_map(|attr| { Some(match attr { - _ if attr.has_name(sym::lang) => (attr.value_str()?, attr.span), - _ if attr.has_name(sym::panic_handler) => (sym::panic_impl, attr.span), + _ if attr.has_name(sym::lang) => (attr.value_str()?, attr.span()), + _ if attr.has_name(sym::panic_handler) => (sym::panic_impl, attr.span()), _ => return None, }) }) diff --git a/compiler/rustc_hir/src/pat_util.rs b/compiler/rustc_hir/src/pat_util.rs index 2ebbb4a06b6..bb853985c7d 100644 --- a/compiler/rustc_hir/src/pat_util.rs +++ b/compiler/rustc_hir/src/pat_util.rs @@ -1,7 +1,6 @@ use std::iter::Enumerate; -use rustc_span::Span; -use rustc_span::symbol::Ident; +use rustc_span::{Ident, Span}; use crate::def::{CtorOf, DefKind, Res}; use crate::def_id::{DefId, DefIdSet}; diff --git a/compiler/rustc_hir/src/stable_hash_impls.rs b/compiler/rustc_hir/src/stable_hash_impls.rs index fe169e989ec..db0d0fcf3b9 100644 --- a/compiler/rustc_hir/src/stable_hash_impls.rs +++ b/compiler/rustc_hir/src/stable_hash_impls.rs @@ -2,7 +2,8 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHas use rustc_span::def_id::DefPathHash; use crate::hir::{ - AttributeMap, BodyId, Crate, ForeignItemId, ImplItemId, ItemId, OwnerNodes, TraitItemId, + Attribute, AttributeMap, BodyId, Crate, ForeignItemId, ImplItemId, ItemId, OwnerNodes, + TraitItemId, }; use crate::hir_id::{HirId, ItemLocalId}; @@ -12,6 +13,7 @@ use crate::hir_id::{HirId, ItemLocalId}; pub trait HashStableContext: rustc_ast::HashStableContext + rustc_target::HashStableContext { + fn hash_attr(&mut self, _: &Attribute, hasher: &mut StableHasher); } impl<HirCtx: crate::HashStableContext> ToStableHashKey<HirCtx> for HirId { @@ -113,3 +115,9 @@ impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for Crate<'_> { opt_hir_hash.unwrap().hash_stable(hcx, hasher) } } + +impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for Attribute { + fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { + hcx.hash_attr(self, hasher) + } +} diff --git a/compiler/rustc_hir/src/weak_lang_items.rs b/compiler/rustc_hir/src/weak_lang_items.rs index 337859cd1fb..b4e548effd4 100644 --- a/compiler/rustc_hir/src/weak_lang_items.rs +++ b/compiler/rustc_hir/src/weak_lang_items.rs @@ -1,6 +1,6 @@ //! Validity checking for weak lang items -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::{Symbol, sym}; use crate::LangItem; diff --git a/compiler/rustc_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml index 581ef2272d1..196d7d99e93 100644 --- a/compiler/rustc_hir_analysis/Cargo.toml +++ b/compiler/rustc_hir_analysis/Cargo.toml @@ -13,7 +13,7 @@ itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index a2df0ba265c..0c3ed9b5c60 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -249,6 +249,12 @@ hir_analysis_invalid_receiver_ty_help = hir_analysis_invalid_receiver_ty_help_no_arbitrary_self_types = consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one of the previous types except `Self`) +hir_analysis_invalid_receiver_ty_help_nonnull_note = + `NonNull` does not implement `Receiver` because it has methods that may shadow the referent; consider wrapping your `NonNull` in a newtype wrapper for which you implement `Receiver` + +hir_analysis_invalid_receiver_ty_help_weak_note = + `Weak` does not implement `Receiver` because it has methods that may shadow the referent; consider wrapping your `Weak` in a newtype wrapper for which you implement `Receiver` + hir_analysis_invalid_receiver_ty_no_arbitrary_self_types = invalid `self` parameter type: `{$receiver_ty}` .note = type of `self` must be `Self` or a type that dereferences to it diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 022a6d457ec..5548a6a6ef7 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -6,7 +6,7 @@ use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::MultiSpan; use rustc_errors::codes::*; use rustc_hir::def::{CtorKind, DefKind}; -use rustc_hir::{Node, Safety, intravisit}; +use rustc_hir::{Node, intravisit}; use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::{Obligation, ObligationCauseCode}; use rustc_lint_defs::builtin::{ @@ -31,7 +31,7 @@ use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_type_ir::fold::TypeFoldable; use tracing::{debug, instrument}; use ty::TypingMode; -use {rustc_attr as attr, rustc_hir as hir}; +use {rustc_attr_parsing as attr, rustc_hir as hir}; use super::compare_impl_item::check_type_bounds; use super::*; @@ -161,7 +161,7 @@ fn check_unsafe_fields(tcx: TyCtxt<'_>, item_def_id: LocalDefId) { }; let typing_env = ty::TypingEnv::non_body_analysis(tcx, item_def_id); for field in def.all_fields() { - if field.safety != Safety::Unsafe { + if !field.safety.is_unsafe() { continue; } let Ok(field_ty) = tcx.try_normalize_erasing_regions(typing_env, field.ty(tcx, args)) diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index e176fc58999..a6b504de3da 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -430,12 +430,12 @@ fn compare_method_predicate_entailment<'tcx>( Ok(()) } -struct RemapLateBound<'a, 'tcx> { +struct RemapLateParam<'a, 'tcx> { tcx: TyCtxt<'tcx>, - mapping: &'a FxIndexMap<ty::BoundRegionKind, ty::BoundRegionKind>, + mapping: &'a FxIndexMap<ty::LateParamRegionKind, ty::LateParamRegionKind>, } -impl<'tcx> TypeFolder<TyCtxt<'tcx>> for RemapLateBound<'_, 'tcx> { +impl<'tcx> TypeFolder<TyCtxt<'tcx>> for RemapLateParam<'_, 'tcx> { fn cx(&self) -> TyCtxt<'tcx> { self.tcx } @@ -445,7 +445,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for RemapLateBound<'_, 'tcx> { ty::Region::new_late_param( self.tcx, fr.scope, - self.mapping.get(&fr.bound_region).copied().unwrap_or(fr.bound_region), + self.mapping.get(&fr.kind).copied().unwrap_or(fr.kind), ) } else { r diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs index 6eac4ac3baf..2b14594ea1b 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs @@ -289,11 +289,16 @@ fn report_mismatched_rpitit_signature<'tcx>( tcx.fn_sig(trait_m_def_id).skip_binder().bound_vars(), tcx.fn_sig(impl_m_def_id).skip_binder().bound_vars(), ) - .filter_map(|(impl_bv, trait_bv)| { + .enumerate() + .filter_map(|(idx, (impl_bv, trait_bv))| { if let ty::BoundVariableKind::Region(impl_bv) = impl_bv && let ty::BoundVariableKind::Region(trait_bv) = trait_bv { - Some((impl_bv, trait_bv)) + let var = ty::BoundVar::from_usize(idx); + Some(( + ty::LateParamRegionKind::from_bound(var, impl_bv), + ty::LateParamRegionKind::from_bound(var, trait_bv), + )) } else { None } @@ -301,7 +306,7 @@ fn report_mismatched_rpitit_signature<'tcx>( .collect(); let mut return_ty = - trait_m_sig.output().fold_with(&mut super::RemapLateBound { tcx, mapping: &mapping }); + trait_m_sig.output().fold_with(&mut super::RemapLateParam { tcx, mapping: &mapping }); if tcx.asyncness(impl_m_def_id).is_async() && tcx.asyncness(trait_m_def_id).is_async() { let ty::Alias(ty::Projection, future_ty) = return_ty.kind() else { diff --git a/compiler/rustc_hir_analysis/src/check/entry.rs b/compiler/rustc_hir_analysis/src/check/entry.rs index f3dd13c84b9..245085b332c 100644 --- a/compiler/rustc_hir_analysis/src/check/entry.rs +++ b/compiler/rustc_hir_analysis/src/check/entry.rs @@ -7,9 +7,8 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::span_bug; use rustc_middle::ty::{self, Ty, TyCtxt, TypingMode}; use rustc_session::config::EntryFnType; -use rustc_span::Span; use rustc_span::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; -use rustc_span::symbol::sym; +use rustc_span::{Span, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode}; diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 2e6b511412b..39479401910 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -8,8 +8,7 @@ use rustc_middle::bug; use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::def_id::LocalDefId; -use rustc_span::symbol::sym; -use rustc_span::{Span, Symbol}; +use rustc_span::{Span, Symbol, sym}; use crate::check::check_function_signature; use crate::errors::{ diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 61e203a1ff6..0b0c92a726d 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -89,8 +89,7 @@ use rustc_middle::ty::{self, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypingMode use rustc_middle::{bug, span_bug}; use rustc_session::parse::feature_err; use rustc_span::def_id::CRATE_DEF_ID; -use rustc_span::symbol::{Ident, kw, sym}; -use rustc_span::{BytePos, DUMMY_SP, Span, Symbol}; +use rustc_span::{BytePos, DUMMY_SP, Ident, Span, Symbol, kw, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::error_reporting::infer::ObligationCauseExt as _; use rustc_trait_selection::error_reporting::traits::suggestions::ReturnsVisitor; diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index b9cb48cafdc..ca6729a5bbd 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -129,7 +129,7 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h let mut prev_cx = visitor.cx; visitor.enter_scope(Scope { - id: blk.hir_id.local_id, + local_id: blk.hir_id.local_id, data: ScopeData::Remainder(FirstStatementIndex::new(i)), }); visitor.cx.var_parent = visitor.cx.parent; @@ -154,7 +154,7 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h // the first such subscope, which has the block itself as a // parent. visitor.enter_scope(Scope { - id: blk.hir_id.local_id, + local_id: blk.hir_id.local_id, data: ScopeData::Remainder(FirstStatementIndex::new(i)), }); visitor.cx.var_parent = visitor.cx.parent; @@ -184,7 +184,7 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h visitor .scope_tree .backwards_incompatible_scope - .insert(local_id, Scope { id: local_id, data: ScopeData::Node }); + .insert(local_id, Scope { local_id, data: ScopeData::Node }); } visitor.visit_expr(tail_expr); } @@ -221,7 +221,7 @@ fn resolve_arm<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, arm: &'tcx hir } fn resolve_pat<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, pat: &'tcx hir::Pat<'tcx>) { - visitor.record_child_scope(Scope { id: pat.hir_id.local_id, data: ScopeData::Node }); + visitor.record_child_scope(Scope { local_id: pat.hir_id.local_id, data: ScopeData::Node }); // If this is a binding then record the lifetime of that binding. if let PatKind::Binding(..) = pat.kind { @@ -485,7 +485,7 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h } else { ScopeData::IfThen }; - visitor.enter_scope(Scope { id: then.hir_id.local_id, data }); + visitor.enter_scope(Scope { local_id: then.hir_id.local_id, data }); visitor.cx.var_parent = visitor.cx.parent; visitor.visit_expr(cond); visitor.visit_expr(then); @@ -500,7 +500,7 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h } else { ScopeData::IfThen }; - visitor.enter_scope(Scope { id: then.hir_id.local_id, data }); + visitor.enter_scope(Scope { local_id: then.hir_id.local_id, data }); visitor.cx.var_parent = visitor.cx.parent; visitor.visit_expr(cond); visitor.visit_expr(then); @@ -516,7 +516,7 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h if let hir::ExprKind::Yield(_, source) = &expr.kind { // Mark this expr's scope and all parent scopes as containing `yield`. - let mut scope = Scope { id: expr.hir_id.local_id, data: ScopeData::Node }; + let mut scope = Scope { local_id: expr.hir_id.local_id, data: ScopeData::Node }; loop { let span = match expr.kind { hir::ExprKind::Yield(expr, hir::YieldSource::Await { .. }) => { @@ -803,9 +803,9 @@ impl<'tcx> RegionResolutionVisitor<'tcx> { // account for the destruction scope representing the scope of // the destructors that run immediately after it completes. if self.terminating_scopes.contains(&id) { - self.enter_scope(Scope { id, data: ScopeData::Destruction }); + self.enter_scope(Scope { local_id: id, data: ScopeData::Destruction }); } - self.enter_scope(Scope { id, data: ScopeData::Node }); + self.enter_scope(Scope { local_id: id, data: ScopeData::Node }); } fn enter_body(&mut self, hir_id: hir::HirId, f: impl FnOnce(&mut Self)) { @@ -822,8 +822,8 @@ impl<'tcx> RegionResolutionVisitor<'tcx> { let outer_pessimistic_yield = mem::replace(&mut self.pessimistic_yield, false); self.terminating_scopes.insert(hir_id.local_id); - self.enter_scope(Scope { id: hir_id.local_id, data: ScopeData::CallSite }); - self.enter_scope(Scope { id: hir_id.local_id, data: ScopeData::Arguments }); + self.enter_scope(Scope { local_id: hir_id.local_id, data: ScopeData::CallSite }); + self.enter_scope(Scope { local_id: hir_id.local_id, data: ScopeData::Arguments }); f(self); diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index e6ef29de965..3cddc9642ba 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -23,8 +23,7 @@ use rustc_middle::ty::{ }; use rustc_middle::{bug, span_bug}; use rustc_session::parse::feature_err; -use rustc_span::symbol::{Ident, sym}; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Ident, Span, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::regions::InferCtxtRegionExt; use rustc_trait_selection::traits::misc::{ @@ -44,6 +43,7 @@ use {rustc_ast as ast, rustc_hir as hir}; use crate::autoderef::Autoderef; use crate::collect::CollectItemTypesVisitor; use crate::constrained_generic_params::{Parameter, identify_constrained_generic_params}; +use crate::errors::InvalidReceiverTyHint; use crate::{errors, fluent_generated as fluent}; pub(super) struct WfCheckingCtxt<'a, 'tcx> { @@ -1749,7 +1749,18 @@ fn check_method_receiver<'tcx>( { match receiver_validity_err { ReceiverValidityError::DoesNotDeref if arbitrary_self_types_level.is_some() => { - tcx.dcx().emit_err(errors::InvalidReceiverTy { span, receiver_ty }) + let hint = match receiver_ty + .builtin_deref(false) + .unwrap_or(receiver_ty) + .ty_adt_def() + .and_then(|adt_def| tcx.get_diagnostic_name(adt_def.did())) + { + Some(sym::RcWeak | sym::ArcWeak) => Some(InvalidReceiverTyHint::Weak), + Some(sym::NonNull) => Some(InvalidReceiverTyHint::NonNull), + _ => None, + }; + + tcx.dcx().emit_err(errors::InvalidReceiverTy { span, receiver_ty, hint }) } ReceiverValidityError::DoesNotDeref => { tcx.dcx().emit_err(errors::InvalidReceiverTyNoArbitrarySelfTypes { @@ -2328,8 +2339,11 @@ fn lint_redundant_lifetimes<'tcx>( ); // If we are in a function, add its late-bound lifetimes too. if matches!(def_kind, DefKind::Fn | DefKind::AssocFn) { - for var in tcx.fn_sig(owner_id).instantiate_identity().bound_vars() { + for (idx, var) in + tcx.fn_sig(owner_id).instantiate_identity().bound_vars().iter().enumerate() + { let ty::BoundVariableKind::Region(kind) = var else { continue }; + let kind = ty::LateParamRegionKind::from_bound(ty::BoundVar::from_usize(idx), kind); lifetimes.push(ty::Region::new_late_param(tcx, owner_id.to_def_id(), kind)); } } diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 2eea65125b0..3b98f358b1e 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -673,37 +673,6 @@ fn visit_implementation_of_pointer_like(checker: &Checker<'_>) -> Result<(), Err let impl_span = tcx.def_span(checker.impl_def_id); let self_ty = tcx.impl_trait_ref(checker.impl_def_id).unwrap().instantiate_identity().self_ty(); - // If an ADT is repr(transparent)... - if let ty::Adt(def, args) = *self_ty.kind() - && def.repr().transparent() - { - // FIXME(compiler-errors): This should and could be deduplicated into a query. - // Find the nontrivial field. - let adt_typing_env = ty::TypingEnv::non_body_analysis(tcx, def.did()); - let nontrivial_field = def.all_fields().find(|field_def| { - let field_ty = tcx.type_of(field_def.did).instantiate_identity(); - !tcx.layout_of(adt_typing_env.as_query_input(field_ty)) - .is_ok_and(|layout| layout.layout.is_1zst()) - }); - - if let Some(nontrivial_field) = nontrivial_field { - // Check that the nontrivial field implements `PointerLike`. - let nontrivial_field = nontrivial_field.ty(tcx, args); - let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env); - let ocx = ObligationCtxt::new(&infcx); - ocx.register_bound( - ObligationCause::misc(impl_span, checker.impl_def_id), - param_env, - nontrivial_field, - tcx.lang_items().pointer_like().unwrap(), - ); - // FIXME(dyn-star): We should regionck this implementation. - if ocx.select_all_or_error().is_empty() { - return Ok(()); - } - } - } - let is_permitted_primitive = match *self_ty.kind() { ty::Adt(def, _) => def.is_box(), ty::Uint(..) | ty::Int(..) | ty::RawPtr(..) | ty::Ref(..) | ty::FnPtr(..) => true, @@ -717,6 +686,74 @@ fn visit_implementation_of_pointer_like(checker: &Checker<'_>) -> Result<(), Err return Ok(()); } + let why_disqualified = match *self_ty.kind() { + // If an ADT is repr(transparent) + ty::Adt(self_ty_def, args) => { + if self_ty_def.repr().transparent() { + // FIXME(compiler-errors): This should and could be deduplicated into a query. + // Find the nontrivial field. + let adt_typing_env = ty::TypingEnv::non_body_analysis(tcx, self_ty_def.did()); + let nontrivial_field = self_ty_def.all_fields().find(|field_def| { + let field_ty = tcx.type_of(field_def.did).instantiate_identity(); + !tcx.layout_of(adt_typing_env.as_query_input(field_ty)) + .is_ok_and(|layout| layout.layout.is_1zst()) + }); + + if let Some(nontrivial_field) = nontrivial_field { + // Check that the nontrivial field implements `PointerLike`. + let nontrivial_field_ty = nontrivial_field.ty(tcx, args); + let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env); + let ocx = ObligationCtxt::new(&infcx); + ocx.register_bound( + ObligationCause::misc(impl_span, checker.impl_def_id), + param_env, + nontrivial_field_ty, + tcx.lang_items().pointer_like().unwrap(), + ); + // FIXME(dyn-star): We should regionck this implementation. + if ocx.select_all_or_error().is_empty() { + return Ok(()); + } else { + format!( + "the field `{field_name}` of {descr} `{self_ty}` \ + does not implement `PointerLike`", + field_name = nontrivial_field.name, + descr = self_ty_def.descr() + ) + } + } else { + format!( + "the {descr} `{self_ty}` is `repr(transparent)`, \ + but does not have a non-trivial field (it is zero-sized)", + descr = self_ty_def.descr() + ) + } + } else if self_ty_def.is_box() { + // If we got here, then the `layout.is_pointer_like()` check failed + // and this box is not a thin pointer. + + String::from("boxes of dynamically-sized types are too large to be `PointerLike`") + } else { + format!( + "the {descr} `{self_ty}` is not `repr(transparent)`", + descr = self_ty_def.descr() + ) + } + } + ty::Ref(..) => { + // If we got here, then the `layout.is_pointer_like()` check failed + // and this reference is not a thin pointer. + String::from("references to dynamically-sized types are too large to be `PointerLike`") + } + ty::Dynamic(..) | ty::Foreign(..) => { + String::from("types of dynamic or unknown size may not implement `PointerLike`") + } + _ => { + // This is a white lie; it is true everywhere outside the standard library. + format!("only user-defined sized types are eligible for `impl PointerLike`") + } + }; + Err(tcx .dcx() .struct_span_err( @@ -724,5 +761,6 @@ fn visit_implementation_of_pointer_like(checker: &Checker<'_>) -> Result<(), Err "implementation must be applied to type that has the same ABI as a pointer, \ or is `repr(transparent)` and whose field is `PointerLike`", ) + .with_note(why_disqualified) .emit()) } diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs index 2afc2aec1ba..8f6f5b5f222 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs @@ -13,8 +13,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::bug; use rustc_middle::ty::fast_reject::{SimplifiedType, TreatParams, simplify_type}; use rustc_middle::ty::{self, CrateInherentImpls, Ty, TyCtxt}; -use rustc_span::ErrorGuaranteed; -use rustc_span::symbol::sym; +use rustc_span::{ErrorGuaranteed, sym}; use crate::errors; diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs index 3aad4bafeb5..1be4aa2f63a 100644 --- a/compiler/rustc_hir_analysis/src/coherence/mod.rs +++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs @@ -206,7 +206,9 @@ fn check_object_overlap<'tcx>( // so this is valid. } else { let mut supertrait_def_ids = tcx.supertrait_def_ids(component_def_id); - if supertrait_def_ids.any(|d| d == trait_def_id) { + if supertrait_def_ids + .any(|d| d == trait_def_id && tcx.trait_def(d).implement_via_object) + { let span = tcx.def_span(impl_def_id); return Err(struct_span_code_err!( tcx.dcx(), diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 7fb024cce5a..ada70117b62 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -38,8 +38,7 @@ use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::util::{Discr, IntTypeExt}; use rustc_middle::ty::{self, AdtKind, Const, IsSuggestable, Ty, TyCtxt, TypingMode}; use rustc_middle::{bug, span_bug}; -use rustc_span::symbol::{Ident, Symbol, kw, sym}; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::ObligationCtxt; @@ -1262,49 +1261,8 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { no_dups.then_some(list) }); - let mut deny_explicit_impl = false; - let mut implement_via_object = true; - if let Some(attr) = tcx.get_attr(def_id, sym::rustc_deny_explicit_impl) { - deny_explicit_impl = true; - let mut seen_attr = false; - for meta in attr.meta_item_list().iter().flatten() { - if let Some(meta) = meta.meta_item() - && meta.name_or_empty() == sym::implement_via_object - && let Some(lit) = meta.name_value_literal() - { - if seen_attr { - tcx.dcx().span_err(meta.span, "duplicated `implement_via_object` meta item"); - } - seen_attr = true; - - match lit.symbol { - kw::True => { - implement_via_object = true; - } - kw::False => { - implement_via_object = false; - } - _ => { - tcx.dcx().span_err( - meta.span, - format!( - "unknown literal passed to `implement_via_object` attribute: {}", - lit.symbol - ), - ); - } - } - } else { - tcx.dcx().span_err( - meta.span(), - format!("unknown meta item passed to `rustc_deny_explicit_impl` {meta:?}"), - ); - } - } - if !seen_attr { - tcx.dcx().span_err(attr.span, "missing `implement_via_object` meta item"); - } - } + let deny_explicit_impl = tcx.has_attr(def_id, sym::rustc_deny_explicit_impl); + let implement_via_object = !tcx.has_attr(def_id, sym::rustc_do_not_implement_via_object); ty::TraitDef { def_id: def_id.to_def_id(), @@ -1340,7 +1298,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn .. }) | Item(hir::Item { kind: ItemKind::Fn(sig, generics, _), .. }) => { - infer_return_ty_for_fn_sig(sig, generics, def_id, &icx) + lower_fn_sig_recovering_infer_ret_ty(&icx, sig, generics, def_id) } ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), generics, .. }) => { @@ -1357,7 +1315,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn None, ) } else { - infer_return_ty_for_fn_sig(sig, generics, def_id, &icx) + lower_fn_sig_recovering_infer_ret_ty(&icx, sig, generics, def_id) } } @@ -1407,99 +1365,108 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn ty::EarlyBinder::bind(output) } -fn infer_return_ty_for_fn_sig<'tcx>( - sig: &hir::FnSig<'tcx>, - generics: &hir::Generics<'_>, +fn lower_fn_sig_recovering_infer_ret_ty<'tcx>( + icx: &ItemCtxt<'tcx>, + sig: &'tcx hir::FnSig<'tcx>, + generics: &'tcx hir::Generics<'tcx>, def_id: LocalDefId, +) -> ty::PolyFnSig<'tcx> { + if let Some(infer_ret_ty) = sig.decl.output.get_infer_ret_ty() { + return recover_infer_ret_ty(icx, infer_ret_ty, generics, def_id); + } + + icx.lowerer().lower_fn_ty( + icx.tcx().local_def_id_to_hir_id(def_id), + sig.header.safety, + sig.header.abi, + sig.decl, + Some(generics), + None, + ) +} + +fn recover_infer_ret_ty<'tcx>( icx: &ItemCtxt<'tcx>, + infer_ret_ty: &'tcx hir::Ty<'tcx>, + generics: &'tcx hir::Generics<'tcx>, + def_id: LocalDefId, ) -> ty::PolyFnSig<'tcx> { let tcx = icx.tcx; let hir_id = tcx.local_def_id_to_hir_id(def_id); - match sig.decl.output.get_infer_ret_ty() { - Some(ty) => { - let fn_sig = tcx.typeck(def_id).liberated_fn_sigs()[hir_id]; - // Typeck doesn't expect erased regions to be returned from `type_of`. - // This is a heuristic approach. If the scope has region parameters, - // we should change fn_sig's lifetime from `ReErased` to `ReError`, - // otherwise to `ReStatic`. - let has_region_params = generics.params.iter().any(|param| match param.kind { - GenericParamKind::Lifetime { .. } => true, - _ => false, - }); - let fn_sig = fold_regions(tcx, fn_sig, |r, _| match *r { - ty::ReErased => { - if has_region_params { - ty::Region::new_error_with_message( - tcx, - DUMMY_SP, - "erased region is not allowed here in return type", - ) - } else { - tcx.lifetimes.re_static - } - } - _ => r, - }); + let fn_sig = tcx.typeck(def_id).liberated_fn_sigs()[hir_id]; - let mut visitor = HirPlaceholderCollector::default(); - visitor.visit_ty(ty); - - let mut diag = bad_placeholder(icx.lowerer(), visitor.0, "return type"); - let ret_ty = fn_sig.output(); - // Don't leak types into signatures unless they're nameable! - // For example, if a function returns itself, we don't want that - // recursive function definition to leak out into the fn sig. - let mut recovered_ret_ty = None; - - if let Some(suggestable_ret_ty) = ret_ty.make_suggestable(tcx, false, None) { - diag.span_suggestion( - ty.span, - "replace with the correct return type", - suggestable_ret_ty, - Applicability::MachineApplicable, - ); - recovered_ret_ty = Some(suggestable_ret_ty); - } else if let Some(sugg) = suggest_impl_trait( - &tcx.infer_ctxt().build(TypingMode::non_body_analysis()), - tcx.param_env(def_id), - ret_ty, - ) { - diag.span_suggestion( - ty.span, - "replace with an appropriate return type", - sugg, - Applicability::MachineApplicable, - ); - } else if ret_ty.is_closure() { - diag.help("consider using an `Fn`, `FnMut`, or `FnOnce` trait bound"); - } - // Also note how `Fn` traits work just in case! - if ret_ty.is_closure() { - diag.note( - "for more information on `Fn` traits and closure types, see \ - https://doc.rust-lang.org/book/ch13-01-closures.html", - ); + // Typeck doesn't expect erased regions to be returned from `type_of`. + // This is a heuristic approach. If the scope has region parameters, + // we should change fn_sig's lifetime from `ReErased` to `ReError`, + // otherwise to `ReStatic`. + let has_region_params = generics.params.iter().any(|param| match param.kind { + GenericParamKind::Lifetime { .. } => true, + _ => false, + }); + let fn_sig = fold_regions(tcx, fn_sig, |r, _| match *r { + ty::ReErased => { + if has_region_params { + ty::Region::new_error_with_message( + tcx, + DUMMY_SP, + "erased region is not allowed here in return type", + ) + } else { + tcx.lifetimes.re_static } - - let guar = diag.emit(); - ty::Binder::dummy(tcx.mk_fn_sig( - fn_sig.inputs().iter().copied(), - recovered_ret_ty.unwrap_or_else(|| Ty::new_error(tcx, guar)), - fn_sig.c_variadic, - fn_sig.safety, - fn_sig.abi, - )) } - None => icx.lowerer().lower_fn_ty( - hir_id, - sig.header.safety, - sig.header.abi, - sig.decl, - Some(generics), - None, - ), + _ => r, + }); + + let mut visitor = HirPlaceholderCollector::default(); + visitor.visit_ty(infer_ret_ty); + + let mut diag = bad_placeholder(icx.lowerer(), visitor.0, "return type"); + let ret_ty = fn_sig.output(); + + // Don't leak types into signatures unless they're nameable! + // For example, if a function returns itself, we don't want that + // recursive function definition to leak out into the fn sig. + let mut recovered_ret_ty = None; + if let Some(suggestable_ret_ty) = ret_ty.make_suggestable(tcx, false, None) { + diag.span_suggestion( + infer_ret_ty.span, + "replace with the correct return type", + suggestable_ret_ty, + Applicability::MachineApplicable, + ); + recovered_ret_ty = Some(suggestable_ret_ty); + } else if let Some(sugg) = suggest_impl_trait( + &tcx.infer_ctxt().build(TypingMode::non_body_analysis()), + tcx.param_env(def_id), + ret_ty, + ) { + diag.span_suggestion( + infer_ret_ty.span, + "replace with an appropriate return type", + sugg, + Applicability::MachineApplicable, + ); + } else if ret_ty.is_closure() { + diag.help("consider using an `Fn`, `FnMut`, or `FnOnce` trait bound"); + } + + // Also note how `Fn` traits work just in case! + if ret_ty.is_closure() { + diag.note( + "for more information on `Fn` traits and closure types, see \ + https://doc.rust-lang.org/book/ch13-01-closures.html", + ); } + let guar = diag.emit(); + ty::Binder::dummy(tcx.mk_fn_sig( + fn_sig.inputs().iter().copied(), + recovered_ret_ty.unwrap_or_else(|| Ty::new_error(tcx, guar)), + fn_sig.c_variadic, + fn_sig.safety, + fn_sig.abi, + )) } pub fn suggest_impl_trait<'tcx>( diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index f52d4f42eca..075615de522 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -8,8 +8,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::lint; -use rustc_span::Span; -use rustc_span::symbol::{Symbol, kw}; +use rustc_span::{Span, Symbol, kw}; use tracing::{debug, instrument}; use crate::delegation::inherit_generics_for_delegation_item; diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index 0b81f469371..d3ff1f7bebe 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 1a6c0a93436..8f84492146f 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -9,8 +9,7 @@ use rustc_middle::ty::{ self, GenericPredicates, ImplTraitInTraitData, Ty, TyCtxt, TypeVisitable, TypeVisitor, Upcast, }; use rustc_middle::{bug, span_bug}; -use rustc_span::symbol::Ident; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Ident, Span}; use tracing::{debug, instrument, trace}; use super::item_bounds::explicit_item_bounds_with_filter; @@ -957,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 { @@ -1060,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_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 0f797bcdae4..7c65e9613b6 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -26,9 +26,8 @@ use rustc_middle::middle::resolve_bound_vars::*; use rustc_middle::query::Providers; use rustc_middle::ty::{self, TyCtxt, TypeSuperVisitable, TypeVisitor}; use rustc_middle::{bug, span_bug}; -use rustc_span::Span; use rustc_span::def_id::{DefId, LocalDefId, LocalDefIdMap}; -use rustc_span::symbol::{Ident, sym}; +use rustc_span::{Ident, Span, sym}; use tracing::{debug, debug_span, instrument}; use crate::errors; diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 5595504c3d9..c0526903e88 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -10,8 +10,7 @@ use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::{self, Article, IsSuggestable, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; -use rustc_span::symbol::Ident; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Ident, Span}; use super::{ItemCtxt, bad_placeholder}; use crate::errors::TypeofReservedKeywordUsed; @@ -473,7 +472,7 @@ fn infer_placeholder_type<'tcx>( fn check_feature_inherent_assoc_ty(tcx: TyCtxt<'_>, span: Span) { if !tcx.features().inherent_associated_types() { use rustc_session::parse::feature_err; - use rustc_span::symbol::sym; + use rustc_span::sym; feature_err( &tcx.sess, sym::inherent_associated_types, diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 5ab6faf3b7c..00ba1741ed7 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -6,8 +6,7 @@ use rustc_errors::{ }; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::Ty; -use rustc_span::symbol::Ident; -use rustc_span::{Span, Symbol}; +use rustc_span::{Ident, Span, Symbol}; use crate::fluent_generated as fluent; mod pattern_types; @@ -1655,6 +1654,14 @@ pub(crate) struct NonConstRange { pub span: Span, } +#[derive(Subdiagnostic)] +pub(crate) enum InvalidReceiverTyHint { + #[note(hir_analysis_invalid_receiver_ty_help_weak_note)] + Weak, + #[note(hir_analysis_invalid_receiver_ty_help_nonnull_note)] + NonNull, +} + #[derive(Diagnostic)] #[diag(hir_analysis_invalid_receiver_ty_no_arbitrary_self_types, code = E0307)] #[note] @@ -1673,6 +1680,8 @@ pub(crate) struct InvalidReceiverTy<'tcx> { #[primary_span] pub span: Span, pub receiver_ty: Ty<'tcx>, + #[subdiagnostic] + pub hint: Option<InvalidReceiverTyHint>, } #[derive(Diagnostic)] diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 0404e38a293..e949d4a1126 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -9,8 +9,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::bug; use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt}; -use rustc_span::symbol::Ident; -use rustc_span::{ErrorGuaranteed, Span, Symbol, sym}; +use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym}; use rustc_trait_selection::traits; use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; use smallvec::SmallVec; @@ -745,7 +744,7 @@ fn check_assoc_const_binding_type<'tcx>( let generics = tcx.generics_of(enclosing_item_owner_id); for index in collector.params { let param = generics.param_at(index as _, tcx); - let is_self_param = param.name == rustc_span::symbol::kw::SelfUpper; + let is_self_param = param.name == kw::SelfUpper; guar.get_or_insert(cx.dcx().emit_err(crate::errors::ParamInTyOfAssocConstBinding { span: assoc_const.span, assoc_const, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs index d3c86989440..71a5727ed6c 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs @@ -1,9 +1,8 @@ -use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; +use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::struct_span_code_err; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::DefId; use rustc_lint_defs::builtin::UNUSED_ASSOCIATED_TYPE_BOUNDS; use rustc_middle::span_bug; use rustc_middle::ty::fold::BottomUpFolder; @@ -11,7 +10,7 @@ use rustc_middle::ty::{ self, DynKind, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast, }; -use rustc_span::{ErrorGuaranteed, Span}; +use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility; use rustc_trait_selection::traits::{self, hir_ty_lowering_dyn_compatibility_violations}; use rustc_type_ir::elaborate::ClauseWithSupertraitSpan; @@ -128,8 +127,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - let mut associated_types: FxIndexMap<Span, FxIndexSet<DefId>> = FxIndexMap::default(); + let mut needed_associated_types = FxIndexSet::default(); + let principal_span = regular_traits.first().map_or(DUMMY_SP, |info| info.bottom().1); let regular_traits_refs_spans = trait_bounds .into_iter() .filter(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id())); @@ -145,13 +145,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let bound_predicate = pred.kind(); match bound_predicate.skip_binder() { ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => { - let pred = bound_predicate.rebind(pred); - associated_types.entry(original_span).or_default().extend( - tcx.associated_items(pred.def_id()) + // FIXME(negative_bounds): Handle this correctly... + let trait_ref = + tcx.anonymize_bound_vars(bound_predicate.rebind(pred.trait_ref)); + needed_associated_types.extend( + tcx.associated_items(trait_ref.def_id()) .in_definition_order() .filter(|item| item.kind == ty::AssocKind::Type) .filter(|item| !item.is_impl_trait_in_trait()) - .map(|item| item.def_id), + // If the associated type has a `where Self: Sized` bound, + // we do not need to constrain the associated type. + .filter(|item| !tcx.generics_require_sized_self(item.def_id)) + .map(|item| (item.def_id, trait_ref)), ); } ty::PredicateKind::Clause(ty::ClauseKind::Projection(pred)) => { @@ -201,26 +206,25 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // So every `Projection` clause is an `Assoc = Foo` bound. `associated_types` contains all associated // types's `DefId`, so the following loop removes all the `DefIds` of the associated types that have a // corresponding `Projection` clause - for def_ids in associated_types.values_mut() { - for (projection_bound, span) in &projection_bounds { - let def_id = projection_bound.item_def_id(); - def_ids.swap_remove(&def_id); - if tcx.generics_require_sized_self(def_id) { - tcx.emit_node_span_lint( - UNUSED_ASSOCIATED_TYPE_BOUNDS, - hir_id, - *span, - crate::errors::UnusedAssociatedTypeBounds { span: *span }, - ); - } + for (projection_bound, span) in &projection_bounds { + let def_id = projection_bound.item_def_id(); + let trait_ref = tcx.anonymize_bound_vars( + projection_bound.map_bound(|p| p.projection_term.trait_ref(tcx)), + ); + needed_associated_types.swap_remove(&(def_id, trait_ref)); + if tcx.generics_require_sized_self(def_id) { + tcx.emit_node_span_lint( + UNUSED_ASSOCIATED_TYPE_BOUNDS, + hir_id, + *span, + crate::errors::UnusedAssociatedTypeBounds { span: *span }, + ); } - // If the associated type has a `where Self: Sized` bound, we do not need to constrain the associated - // type in the `dyn Trait`. - def_ids.retain(|def_id| !tcx.generics_require_sized_self(def_id)); } if let Err(guar) = self.check_for_required_assoc_tys( - associated_types, + principal_span, + needed_associated_types, potential_assoc_types, hir_trait_bounds, ) { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 6e10450313c..0623d35853e 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -16,8 +16,7 @@ use rustc_middle::ty::{ }; use rustc_session::parse::feature_err; use rustc_span::edit_distance::find_best_match_for_name; -use rustc_span::symbol::{Ident, kw, sym}; -use rustc_span::{BytePos, DUMMY_SP, Span, Symbol}; +use rustc_span::{BytePos, DUMMY_SP, Ident, Span, Symbol, kw, sym}; use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility; use rustc_trait_selection::traits::{ FulfillmentError, TraitAliasExpansionInfo, dyn_compatibility_violations_for_assoc_item, @@ -721,51 +720,42 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// emit a generic note suggesting using a `where` clause to constraint instead. pub(crate) fn check_for_required_assoc_tys( &self, - associated_types: FxIndexMap<Span, FxIndexSet<DefId>>, + principal_span: Span, + missing_assoc_types: FxIndexSet<(DefId, ty::PolyTraitRef<'tcx>)>, potential_assoc_types: Vec<usize>, trait_bounds: &[hir::PolyTraitRef<'_>], ) -> Result<(), ErrorGuaranteed> { - if associated_types.values().all(|v| v.is_empty()) { + if missing_assoc_types.is_empty() { return Ok(()); } let tcx = self.tcx(); - // FIXME: Marked `mut` so that we can replace the spans further below with a more - // appropriate one, but this should be handled earlier in the span assignment. - let associated_types: FxIndexMap<Span, Vec<_>> = associated_types + // FIXME: This logic needs some more care w.r.t handling of conflicts + let missing_assoc_types: Vec<_> = missing_assoc_types .into_iter() - .map(|(span, def_ids)| { - (span, def_ids.into_iter().map(|did| tcx.associated_item(did)).collect()) - }) + .map(|(def_id, trait_ref)| (tcx.associated_item(def_id), trait_ref)) .collect(); - let mut names: FxIndexMap<String, Vec<Symbol>> = Default::default(); + let mut names: FxIndexMap<_, Vec<Symbol>> = Default::default(); let mut names_len = 0; // Account for things like `dyn Foo + 'a`, like in tests `issue-22434.rs` and // `issue-22560.rs`. - let mut trait_bound_spans: Vec<Span> = vec![]; let mut dyn_compatibility_violations = Ok(()); - for (span, items) in &associated_types { - if !items.is_empty() { - trait_bound_spans.push(*span); - } - for assoc_item in items { - let trait_def_id = assoc_item.container_id(tcx); - names.entry(tcx.def_path_str(trait_def_id)).or_default().push(assoc_item.name); - names_len += 1; - - let violations = - dyn_compatibility_violations_for_assoc_item(tcx, trait_def_id, *assoc_item); - if !violations.is_empty() { - dyn_compatibility_violations = Err(report_dyn_incompatibility( - tcx, - *span, - None, - trait_def_id, - &violations, - ) - .emit()); - } + for (assoc_item, trait_ref) in &missing_assoc_types { + names.entry(trait_ref).or_default().push(assoc_item.name); + names_len += 1; + + let violations = + dyn_compatibility_violations_for_assoc_item(tcx, trait_ref.def_id(), *assoc_item); + if !violations.is_empty() { + dyn_compatibility_violations = Err(report_dyn_incompatibility( + tcx, + principal_span, + None, + trait_ref.def_id(), + &violations, + ) + .emit()); } } @@ -813,6 +803,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .into_iter() .map(|(trait_, mut assocs)| { assocs.sort(); + let trait_ = trait_.print_trait_sugared(); format!("{} in `{trait_}`", match &assocs[..] { [] => String::new(), [only] => format!("`{only}`"), @@ -826,10 +817,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { names.sort(); let names = names.join(", "); - trait_bound_spans.sort(); let mut err = struct_span_code_err!( self.dcx(), - trait_bound_spans, + principal_span, E0191, "the value of the associated type{} {} must be specified", pluralize!(names_len), @@ -839,81 +829,83 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let mut types_count = 0; let mut where_constraints = vec![]; let mut already_has_generics_args_suggestion = false; - for (span, assoc_items) in &associated_types { - let mut names: UnordMap<_, usize> = Default::default(); - for item in assoc_items { - types_count += 1; - *names.entry(item.name).or_insert(0) += 1; - } - let mut dupes = false; - let mut shadows = false; - for item in assoc_items { - let prefix = if names[&item.name] > 1 { - let trait_def_id = item.container_id(tcx); - dupes = true; - format!("{}::", tcx.def_path_str(trait_def_id)) - } else if bound_names.get(&item.name).is_some_and(|x| x != &item) { - let trait_def_id = item.container_id(tcx); - shadows = true; - format!("{}::", tcx.def_path_str(trait_def_id)) - } else { - String::new() - }; - let mut is_shadowed = false; - - if let Some(assoc_item) = bound_names.get(&item.name) - && assoc_item != &item - { - is_shadowed = true; + let mut names: UnordMap<_, usize> = Default::default(); + for (item, _) in &missing_assoc_types { + types_count += 1; + *names.entry(item.name).or_insert(0) += 1; + } + let mut dupes = false; + let mut shadows = false; + for (item, trait_ref) in &missing_assoc_types { + let prefix = if names[&item.name] > 1 { + let trait_def_id = trait_ref.def_id(); + dupes = true; + format!("{}::", tcx.def_path_str(trait_def_id)) + } else if bound_names.get(&item.name).is_some_and(|x| *x != item) { + let trait_def_id = trait_ref.def_id(); + shadows = true; + format!("{}::", tcx.def_path_str(trait_def_id)) + } else { + String::new() + }; - let rename_message = - if assoc_item.def_id.is_local() { ", consider renaming it" } else { "" }; - err.span_label( - tcx.def_span(assoc_item.def_id), - format!("`{}{}` shadowed here{}", prefix, item.name, rename_message), - ); - } + let mut is_shadowed = false; - let rename_message = if is_shadowed { ", consider renaming it" } else { "" }; + if let Some(assoc_item) = bound_names.get(&item.name) + && *assoc_item != item + { + is_shadowed = true; - if let Some(sp) = tcx.hir().span_if_local(item.def_id) { - err.span_label( - sp, - format!("`{}{}` defined here{}", prefix, item.name, rename_message), - ); - } + let rename_message = + if assoc_item.def_id.is_local() { ", consider renaming it" } else { "" }; + err.span_label( + tcx.def_span(assoc_item.def_id), + format!("`{}{}` shadowed here{}", prefix, item.name, rename_message), + ); } - if potential_assoc_types.len() == assoc_items.len() { - // When the amount of missing associated types equals the number of - // extra type arguments present. A suggesting to replace the generic args with - // associated types is already emitted. - already_has_generics_args_suggestion = true; - } else if let (Ok(snippet), false, false) = - (tcx.sess.source_map().span_to_snippet(*span), dupes, shadows) - { - let types: Vec<_> = - assoc_items.iter().map(|item| format!("{} = Type", item.name)).collect(); - let code = if snippet.ends_with('>') { - // The user wrote `Trait<'a>` or similar and we don't have a type we can - // suggest, but at least we can clue them to the correct syntax - // `Trait<'a, Item = Type>` while accounting for the `<'a>` in the - // suggestion. - format!("{}, {}>", &snippet[..snippet.len() - 1], types.join(", ")) - } else if in_expr_or_pat { - // The user wrote `Iterator`, so we don't have a type we can suggest, but at - // least we can clue them to the correct syntax `Iterator::<Item = Type>`. - format!("{}::<{}>", snippet, types.join(", ")) - } else { - // The user wrote `Iterator`, so we don't have a type we can suggest, but at - // least we can clue them to the correct syntax `Iterator<Item = Type>`. - format!("{}<{}>", snippet, types.join(", ")) - }; - suggestions.push((*span, code)); - } else if dupes { - where_constraints.push(*span); + + let rename_message = if is_shadowed { ", consider renaming it" } else { "" }; + + if let Some(sp) = tcx.hir().span_if_local(item.def_id) { + err.span_label( + sp, + format!("`{}{}` defined here{}", prefix, item.name, rename_message), + ); } } + if potential_assoc_types.len() == missing_assoc_types.len() { + // When the amount of missing associated types equals the number of + // extra type arguments present. A suggesting to replace the generic args with + // associated types is already emitted. + already_has_generics_args_suggestion = true; + } else if let (Ok(snippet), false, false) = + (tcx.sess.source_map().span_to_snippet(principal_span), dupes, shadows) + { + let types: Vec<_> = missing_assoc_types + .iter() + .map(|(item, _)| format!("{} = Type", item.name)) + .collect(); + let code = if snippet.ends_with('>') { + // The user wrote `Trait<'a>` or similar and we don't have a type we can + // suggest, but at least we can clue them to the correct syntax + // `Trait<'a, Item = Type>` while accounting for the `<'a>` in the + // suggestion. + format!("{}, {}>", &snippet[..snippet.len() - 1], types.join(", ")) + } else if in_expr_or_pat { + // The user wrote `Iterator`, so we don't have a type we can suggest, but at + // least we can clue them to the correct syntax `Iterator::<Item = Type>`. + format!("{}::<{}>", snippet, types.join(", ")) + } else { + // The user wrote `Iterator`, so we don't have a type we can suggest, but at + // least we can clue them to the correct syntax `Iterator<Item = Type>`. + format!("{}<{}>", snippet, types.join(", ")) + }; + suggestions.push((principal_span, code)); + } else if dupes { + where_constraints.push(principal_span); + } + let where_msg = "consider introducing a new type parameter, adding `where` constraints \ using the fully-qualified path to the associated types"; if !where_constraints.is_empty() && suggestions.is_empty() { @@ -924,32 +916,29 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } if suggestions.len() != 1 || already_has_generics_args_suggestion { // We don't need this label if there's an inline suggestion, show otherwise. - for (span, assoc_items) in &associated_types { - let mut names: FxIndexMap<_, usize> = FxIndexMap::default(); - for item in assoc_items { - types_count += 1; - *names.entry(item.name).or_insert(0) += 1; - } - let mut label = vec![]; - for item in assoc_items { - let postfix = if names[&item.name] > 1 { - let trait_def_id = item.container_id(tcx); - format!(" (from trait `{}`)", tcx.def_path_str(trait_def_id)) - } else { - String::new() - }; - label.push(format!("`{}`{}", item.name, postfix)); - } - if !label.is_empty() { - err.span_label( - *span, - format!( - "associated type{} {} must be specified", - pluralize!(label.len()), - label.join(", "), - ), - ); - } + let mut names: FxIndexMap<_, usize> = FxIndexMap::default(); + for (item, _) in &missing_assoc_types { + types_count += 1; + *names.entry(item.name).or_insert(0) += 1; + } + let mut label = vec![]; + for (item, trait_ref) in &missing_assoc_types { + let postfix = if names[&item.name] > 1 { + format!(" (from trait `{}`)", trait_ref.print_trait_sugared()) + } else { + String::new() + }; + label.push(format!("`{}`{}", item.name, postfix)); + } + if !label.is_empty() { + err.span_label( + principal_span, + format!( + "associated type{} {} must be specified", + pluralize!(label.len()), + label.join(", "), + ), + ); } } suggestions.sort_by_key(|&(span, _)| span); diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs index c3f4fc8699a..df00948dd21 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -9,7 +9,7 @@ use rustc_middle::ty::{ self, GenericArgsRef, GenericParamDef, GenericParamDefKind, IsSuggestable, Ty, }; use rustc_session::lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS; -use rustc_span::symbol::{kw, sym}; +use rustc_span::{kw, sym}; use smallvec::SmallVec; use tracing::{debug, instrument}; diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index a357ade0294..b56222763d0 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -45,8 +45,7 @@ use rustc_middle::ty::{ use rustc_middle::{bug, span_bug}; use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS; use rustc_span::edit_distance::find_best_match_for_name; -use rustc_span::symbol::{Ident, Symbol, kw}; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::wf::object_region_bounds; use rustc_trait_selection::traits::{self, ObligationCtxt}; @@ -356,7 +355,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ty::Region::new_late_param( tcx, scope.to_def_id(), - ty::BoundRegionKind::Named(id.to_def_id(), name), + ty::LateParamRegionKind::Named(id.to_def_id(), name), ) // (*) -- not late-bound, won't change diff --git a/compiler/rustc_hir_analysis/src/variance/dump.rs b/compiler/rustc_hir_analysis/src/variance/dump.rs index a0fdf95a831..31f0adf720d 100644 --- a/compiler/rustc_hir_analysis/src/variance/dump.rs +++ b/compiler/rustc_hir_analysis/src/variance/dump.rs @@ -2,7 +2,7 @@ use std::fmt::Write; use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; use rustc_middle::ty::{GenericArgs, TyCtxt}; -use rustc_span::symbol::sym; +use rustc_span::sym; fn format_variances(tcx: TyCtxt<'_>, def_id: LocalDefId) -> String { let variances = tcx.variances_of(def_id); diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 8dd8a5c98ef..5c1c5892190 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -11,16 +11,17 @@ use std::vec; use rustc_abi::ExternAbi; use rustc_ast::util::parser::{self, AssocOp, ExprPrecedence, Fixity}; +use rustc_ast::{DUMMY_NODE_ID, DelimArgs}; use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent}; use rustc_ast_pretty::pp::{self, Breaks}; +use rustc_ast_pretty::pprust::state::MacHeader; use rustc_ast_pretty::pprust::{Comments, PrintState}; use rustc_hir::{ BindingMode, ByRef, ConstArgKind, GenericArg, GenericBound, GenericParam, GenericParamKind, HirId, LifetimeParamKind, Node, PatKind, PreciseCapturingArg, RangeEnd, Term, }; -use rustc_span::FileName; use rustc_span::source_map::SourceMap; -use rustc_span::symbol::{Ident, Symbol, kw}; +use rustc_span::{FileName, Ident, Span, Symbol, kw}; use {rustc_ast as ast, rustc_hir as hir}; pub fn id_to_string(map: &dyn rustc_hir::intravisit::Map<'_>, hir_id: HirId) -> String { @@ -68,15 +69,115 @@ impl PpAnn for &dyn rustc_hir::intravisit::Map<'_> { pub struct State<'a> { pub s: pp::Printer, comments: Option<Comments<'a>>, - attrs: &'a dyn Fn(HirId) -> &'a [ast::Attribute], + attrs: &'a dyn Fn(HirId) -> &'a [hir::Attribute], ann: &'a (dyn PpAnn + 'a), } impl<'a> State<'a> { - fn attrs(&self, id: HirId) -> &'a [ast::Attribute] { + fn attrs(&self, id: HirId) -> &'a [hir::Attribute] { (self.attrs)(id) } + fn print_inner_attributes(&mut self, attrs: &[hir::Attribute]) -> bool { + self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, true) + } + + fn print_outer_attributes(&mut self, attrs: &[hir::Attribute]) -> bool { + self.print_either_attributes(attrs, ast::AttrStyle::Outer, false, true) + } + + fn print_either_attributes( + &mut self, + attrs: &[hir::Attribute], + kind: ast::AttrStyle, + is_inline: bool, + trailing_hardbreak: bool, + ) -> bool { + let mut printed = false; + for attr in attrs { + if attr.style == kind { + self.print_attribute_inline(attr, is_inline); + if is_inline { + self.nbsp(); + } + printed = true; + } + } + if printed && trailing_hardbreak && !is_inline { + self.hardbreak_if_not_bol(); + } + printed + } + + fn print_attribute_inline(&mut self, attr: &hir::Attribute, is_inline: bool) { + if !is_inline { + self.hardbreak_if_not_bol(); + } + self.maybe_print_comment(attr.span.lo()); + match &attr.kind { + hir::AttrKind::Normal(normal) => { + match attr.style { + ast::AttrStyle::Inner => self.word("#!["), + ast::AttrStyle::Outer => self.word("#["), + } + if normal.unsafety == hir::Safety::Unsafe { + self.word("unsafe("); + } + self.print_attr_item(&normal, attr.span); + if normal.unsafety == hir::Safety::Unsafe { + self.word(")"); + } + self.word("]"); + } + hir::AttrKind::DocComment(comment_kind, data) => { + self.word(rustc_ast_pretty::pprust::state::doc_comment_to_string( + *comment_kind, + attr.style, + *data, + )); + self.hardbreak() + } + } + } + + fn print_attr_item(&mut self, item: &hir::AttrItem, span: Span) { + self.ibox(0); + let path = ast::Path { + span, + segments: item + .path + .segments + .iter() + .map(|i| ast::PathSegment { ident: *i, args: None, id: DUMMY_NODE_ID }) + .collect(), + tokens: None, + }; + + match &item.args { + hir::AttrArgs::Delimited(DelimArgs { dspan: _, delim, tokens }) => self + .print_mac_common( + Some(MacHeader::Path(&path)), + false, + None, + *delim, + tokens, + true, + span, + ), + hir::AttrArgs::Empty => { + PrintState::print_path(self, &path, false, 0); + } + hir::AttrArgs::Eq { eq_span: _, expr } => { + PrintState::print_path(self, &path, false, 0); + self.space(); + self.word_space("="); + let token_str = self.meta_item_lit_to_string(expr); + self.word(token_str); + } + } + self.end(); + } + fn print_node(&mut self, node: Node<'_>) { match node { Node::Param(a) => self.print_param(a), @@ -164,7 +265,7 @@ pub fn print_crate<'a>( krate: &hir::Mod<'_>, filename: FileName, input: String, - attrs: &'a dyn Fn(HirId) -> &'a [ast::Attribute], + attrs: &'a dyn Fn(HirId) -> &'a [hir::Attribute], ann: &'a dyn PpAnn, ) -> String { let mut s = State { @@ -191,6 +292,10 @@ where printer.s.eof() } +pub fn attribute_to_string(ann: &dyn PpAnn, attr: &hir::Attribute) -> String { + to_string(ann, |s| s.print_attribute_inline(attr, false)) +} + pub fn ty_to_string(ann: &dyn PpAnn, ty: &hir::Ty<'_>) -> String { to_string(ann, |s| s.print_type(ty)) } @@ -242,7 +347,7 @@ impl<'a> State<'a> { self.commasep_cmnt(b, exprs, |s, e| s.print_expr(e), |e| e.span); } - fn print_mod(&mut self, _mod: &hir::Mod<'_>, attrs: &[ast::Attribute]) { + fn print_mod(&mut self, _mod: &hir::Mod<'_>, attrs: &[hir::Attribute]) { self.print_inner_attributes(attrs); for &item_id in _mod.item_ids { self.ann.nested(self, Nested::Item(item_id)); @@ -297,8 +402,10 @@ impl<'a> State<'a> { } hir::TyKind::Path(ref qpath) => self.print_qpath(qpath, false), hir::TyKind::TraitObject(bounds, lifetime, syntax) => { - if syntax == ast::TraitObjectSyntax::Dyn { - self.word_space("dyn"); + match syntax { + ast::TraitObjectSyntax::Dyn => self.word_nbsp("dyn"), + ast::TraitObjectSyntax::DynStar => self.word_nbsp("dyn*"), + ast::TraitObjectSyntax::None => {} } let mut first = true; for bound in bounds { @@ -926,14 +1033,14 @@ impl<'a> State<'a> { self.print_block_maybe_unclosed(blk, &[], false) } - fn print_block_with_attrs(&mut self, blk: &hir::Block<'_>, attrs: &[ast::Attribute]) { + fn print_block_with_attrs(&mut self, blk: &hir::Block<'_>, attrs: &[hir::Attribute]) { self.print_block_maybe_unclosed(blk, attrs, true) } fn print_block_maybe_unclosed( &mut self, blk: &hir::Block<'_>, - attrs: &[ast::Attribute], + attrs: &[hir::Attribute], close_box: bool, ) { match blk.rules { diff --git a/compiler/rustc_hir_typeck/Cargo.toml b/compiler/rustc_hir_typeck/Cargo.toml index 8ddbb4b3397..1f5acaa58a9 100644 --- a/compiler/rustc_hir_typeck/Cargo.toml +++ b/compiler/rustc_hir_typeck/Cargo.toml @@ -9,7 +9,7 @@ itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_ir = { path = "../rustc_ast_ir" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index b430f48965a..9d364775445 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -13,9 +13,8 @@ use rustc_middle::ty::adjustment::{ }; use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; -use rustc_span::Span; use rustc_span::def_id::LocalDefId; -use rustc_span::symbol::{Ident, sym}; +use rustc_span::{Ident, Span, sym}; use rustc_trait_selection::error_reporting::traits::DefIdOrName; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 80b91c21598..8190095971b 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -43,8 +43,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeAndMut, TypeVisitableExt, VariantDe use rustc_middle::{bug, span_bug}; use rustc_session::lint; use rustc_span::def_id::LOCAL_CRATE; -use rustc_span::symbol::sym; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Span, sym}; use rustc_trait_selection::infer::InferCtxtExt; use tracing::{debug, instrument}; @@ -722,13 +721,11 @@ impl<'a, 'tcx> CastCheck<'tcx> { use rustc_middle::ty::cast::IntTy::*; if self.cast_ty.is_dyn_star() { - if fcx.tcx.features().dyn_star() { - span_bug!(self.span, "should be handled by `coerce`"); - } else { - // Report "casting is invalid" rather than "non-primitive cast" - // if the feature is not enabled. - return Err(CastError::IllegalCast); - } + // This coercion will fail if the feature is not enabled, OR + // if the coercion is (currently) illegal (e.g. `dyn* Foo + Send` + // to `dyn* Foo`). Report "casting is invalid" rather than + // "non-primitive cast". + return Err(CastError::IllegalCast); } let (t_from, t_cast) = match (CastTy::from_ty(self.expr_ty), CastTy::from_ty(self.cast_ty)) diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index 51d78646373..b0698183b57 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -10,7 +10,7 @@ use rustc_infer::infer::RegionVariableOrigin; use rustc_infer::traits::WellFormedLoc; use rustc_middle::ty::{self, Binder, Ty, TyCtxt}; use rustc_span::def_id::LocalDefId; -use rustc_span::symbol::sym; +use rustc_span::sym; use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode}; use tracing::{debug, instrument}; diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index bf41dcbe4a3..f9e4a592d92 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -59,8 +59,7 @@ use rustc_middle::ty::error::TypeError; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt}; use rustc_session::parse::feature_err; -use rustc_span::symbol::sym; -use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Span}; +use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Span, sym}; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{ @@ -738,8 +737,10 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { return Err(TypeError::Mismatch); } - if let ty::Dynamic(a_data, _, _) = a.kind() - && let ty::Dynamic(b_data, _, _) = b.kind() + // FIXME(dyn_star): We should probably allow things like casting from + // `dyn* Foo + Send` to `dyn* Foo`. + if let ty::Dynamic(a_data, _, ty::DynStar) = a.kind() + && let ty::Dynamic(b_data, _, ty::DynStar) = b.kind() && a_data.principal_def_id() == b_data.principal_def_id() { return self.unify_and(a, b, |_| vec![]); @@ -863,7 +864,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let outer_universe = self.infcx.universe(); let result = if let ty::FnPtr(_, hdr_b) = b.kind() - && let (hir::Safety::Safe, hir::Safety::Unsafe) = (fn_ty_a.safety(), hdr_b.safety) + && fn_ty_a.safety().is_safe() + && hdr_b.safety.is_unsafe() { let unsafe_a = self.tcx.safe_to_unsafe_fn_ty(fn_ty_a); self.unify_and(unsafe_a, b, to_unsafe) @@ -925,7 +927,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396). - if b_hdr.safety == hir::Safety::Safe + if b_hdr.safety.is_safe() && !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty() { return Err(TypeError::TargetFeatureCast(def_id)); @@ -1315,43 +1317,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - // Then try to coerce the previous expressions to the type of the new one. - // This requires ensuring there are no coercions applied to *any* of the - // previous expressions, other than noop reborrows (ignoring lifetimes). - for expr in exprs { - let expr = expr.as_coercion_site(); - let noop = match self.typeck_results.borrow().expr_adjustments(expr) { - &[ - Adjustment { kind: Adjust::Deref(_), .. }, - Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(mutbl_adj)), .. }, - ] => { - match *self.node_ty(expr.hir_id).kind() { - ty::Ref(_, _, mt_orig) => { - let mutbl_adj: hir::Mutability = mutbl_adj.into(); - // Reborrow that we can safely ignore, because - // the next adjustment can only be a Deref - // which will be merged into it. - mutbl_adj == mt_orig - } - _ => false, - } - } - &[Adjustment { kind: Adjust::NeverToAny, .. }] | &[] => true, - _ => false, - }; - - if !noop { - debug!( - "coercion::try_find_coercion_lub: older expression {:?} had adjustments, requiring LUB", - expr, - ); - - return Err(self - .commit_if_ok(|_| self.at(cause, self.param_env).lub(prev_ty, new_ty)) - .unwrap_err()); - } - } - match self.commit_if_ok(|_| coerce.coerce(prev_ty, new_ty)) { Err(_) => { // Avoid giving strange errors on failed attempts. diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index d0272651c08..e51323fc5c8 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -9,8 +9,7 @@ use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::BottomUpFolder; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, AssocItem, Ty, TypeFoldable, TypeVisitableExt}; -use rustc_span::symbol::sym; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Ident, Span, sym}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::ObligationCause; use tracing::instrument; @@ -1118,7 +1117,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Constructor, } let mut maybe_emit_help = |def_id: hir::def_id::DefId, - callable: rustc_span::symbol::Ident, + callable: Ident, args: &[hir::Expr<'_>], kind: CallableKind| { let arg_idx = args.iter().position(|a| a.hir_id == expr.hir_id).unwrap(); diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 7746a5a7132..ff09583cc65 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -10,8 +10,7 @@ use rustc_errors::{ use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::{self, Ty}; use rustc_span::edition::{Edition, LATEST_STABLE_EDITION}; -use rustc_span::symbol::Ident; -use rustc_span::{Span, Symbol}; +use rustc_span::{Ident, Span, Symbol}; use crate::fluent_generated as fluent; diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 66978399efb..caea53d9200 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -30,11 +30,10 @@ use rustc_middle::ty::{self, AdtKind, GenericArgsRef, Ty, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; use rustc_session::errors::ExprParenthesesNeeded; use rustc_session::parse::feature_err; -use rustc_span::Span; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::Spanned; -use rustc_span::symbol::{Ident, Symbol, kw, sym}; +use rustc_span::{Ident, Span, Symbol, kw, sym}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt}; use tracing::{debug, instrument, trace}; diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index b57c248ed2b..c128485d93e 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -762,7 +762,7 @@ fn compute_unsafe_infer_vars<'a, 'tcx>( if let Some(def_id) = typeck_results.type_dependent_def_id(ex.hir_id) && let method_ty = self.fcx.tcx.type_of(def_id).instantiate_identity() && let sig = method_ty.fn_sig(self.fcx.tcx) - && let hir::Safety::Unsafe = sig.safety() + && sig.safety().is_unsafe() { let mut collector = InferVarCollector { value: (ex.hir_id, ex.span, UnsafeUseReason::Method), @@ -782,7 +782,7 @@ fn compute_unsafe_infer_vars<'a, 'tcx>( if func_ty.is_fn() && let sig = func_ty.fn_sig(self.fcx.tcx) - && let hir::Safety::Unsafe = sig.safety() + && sig.safety().is_unsafe() { let mut collector = InferVarCollector { value: (ex.hir_id, ex.span, UnsafeUseReason::Call), @@ -813,7 +813,7 @@ fn compute_unsafe_infer_vars<'a, 'tcx>( // `is_fn` excludes closures, but those can't be unsafe. if ty.is_fn() && let sig = ty.fn_sig(self.fcx.tcx) - && let hir::Safety::Unsafe = sig.safety() + && sig.safety().is_unsafe() { let mut collector = InferVarCollector { value: (ex.hir_id, ex.span, UnsafeUseReason::Path), diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 0dacfc9b7bf..364499378b0 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -29,10 +29,9 @@ use rustc_middle::ty::{ }; use rustc_middle::{bug, span_bug}; use rustc_session::lint; -use rustc_span::Span; use rustc_span::def_id::LocalDefId; use rustc_span::hygiene::DesugaringKind; -use rustc_span::symbol::kw; +use rustc_span::{Span, kw}; use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded; use rustc_trait_selection::traits::{ self, NormalizeExt, ObligationCauseCode, StructurallyNormalizeExt, @@ -147,18 +146,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("write_ty({:?}, {:?}) in fcx {}", id, self.resolve_vars_if_possible(ty), self.tag()); let mut typeck = self.typeck_results.borrow_mut(); let mut node_ty = typeck.node_types_mut(); - if let Some(ty) = node_ty.get(id) - && let Err(e) = ty.error_reported() - { - // Do not overwrite nodes that were already marked as `{type error}`. This allows us to - // silence unnecessary errors from obligations that were set earlier than a type error - // was produced, but that is overwritten by later analysis. This happens in particular - // for `Sized` obligations introduced in gather_locals. (#117846) - self.set_tainted_by_errors(e); - return; - } - node_ty.insert(id, ty); + if let Some(prev) = node_ty.insert(id, ty) { + if prev.references_error() { + node_ty.insert(id, prev); + } else if !ty.references_error() { + // Could change this to a bug, but there's lots of diagnostic code re-lowering + // or re-typechecking nodes that were already typecked. + // Lots of that diagnostics code relies on subtle effects of re-lowering, so we'll + // let it keep doing that and just ensure that compilation won't succeed. + self.dcx().span_delayed_bug( + self.tcx.hir().span(id), + format!("`{prev}` overridden by `{ty}` for {id:?} in {:?}", self.body_id), + ); + } + } if let Err(e) = ty.error_reported() { self.set_tainted_by_errors(e); @@ -1105,7 +1107,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Res::Local(hid) = res { let ty = self.local_ty(span, hid); let ty = self.normalize(span, ty); - self.write_ty(hir_id, ty); return (ty, res); } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs index 6eaba641888..ee0436f73e1 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs @@ -5,8 +5,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_infer::traits::ObligationCauseCode; use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; -use rustc_span::Span; -use rustc_span::symbol::kw; +use rustc_span::{Span, kw}; use rustc_trait_selection::traits; use crate::FnCtxt; diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 6b1cceefbee..fffea8f640b 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -22,8 +22,7 @@ use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::Session; -use rustc_span::symbol::{Ident, kw}; -use rustc_span::{DUMMY_SP, Span, sym}; +use rustc_span::{DUMMY_SP, Ident, Span, kw, sym}; use rustc_trait_selection::error_reporting::infer::{FailureCode, ObligationCauseExt}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt, SelectionContext}; @@ -1751,10 +1750,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - pub(in super::super) fn check_decl(&self, decl: Declaration<'tcx>) { + pub(in super::super) fn check_decl(&self, decl: Declaration<'tcx>) -> Ty<'tcx> { // Determine and write the type which we'll check the pattern against. let decl_ty = self.local_ty(decl.span, decl.hir_id); - self.write_ty(decl.hir_id, decl_ty); // Type check the initializer. if let Some(ref init) = decl.init { @@ -1786,11 +1784,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } self.diverges.set(previous_diverges); } + decl_ty } /// Type check a `let` statement. fn check_decl_local(&self, local: &'tcx hir::LetStmt<'tcx>) { - self.check_decl(local.into()); + let ty = self.check_decl(local.into()); + self.write_ty(local.hir_id, ty); if local.pat.is_never_pattern() { self.diverges.set(Diverges::Always { span: local.pat.span, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index b9011e89f04..d432199f037 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -17,8 +17,7 @@ use rustc_infer::infer; use rustc_infer::traits::Obligation; use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt}; use rustc_session::Session; -use rustc_span::symbol::Ident; -use rustc_span::{self, DUMMY_SP, Span, sym}; +use rustc_span::{self, DUMMY_SP, Ident, Span, sym}; use rustc_trait_selection::error_reporting::TypeErrCtxt; use rustc_trait_selection::error_reporting::infer::sub_relations::SubRelations; use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt}; diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index ddcd90a2a9d..964ef5b2106 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -10,7 +10,7 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::lang_items::LangItem; use rustc_hir::{ Arm, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, GenericBound, HirId, - Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicateKind, + Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicateKind, expr_needs_parens, }; use rustc_hir_analysis::collect::suggest_impl_trait; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; @@ -24,8 +24,7 @@ use rustc_middle::ty::{ }; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::source_map::Spanned; -use rustc_span::symbol::{Ident, sym}; -use rustc_span::{Span, Symbol}; +use rustc_span::{Ident, Span, Symbol, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::error_reporting::traits::DefIdOrName; use rustc_trait_selection::infer::InferCtxtExt; @@ -35,7 +34,6 @@ use tracing::{debug, instrument}; use super::FnCtxt; use crate::fn_ctxt::rustc_span::BytePos; -use crate::hir::is_range_literal; use crate::method::probe; use crate::method::probe::{IsSuggestion, Mode, ProbeScope}; use crate::{errors, fluent_generated as fluent}; @@ -2648,7 +2646,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let make_sugg = |expr: &Expr<'_>, span: Span, sugg: &str| { - if self.needs_parentheses(expr) { + if expr_needs_parens(expr) { ( vec![ (span.shrink_to_lo(), format!("{prefix}{sugg}(")), @@ -2861,7 +2859,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; } - if self.needs_parentheses(expr) { + if expr_needs_parens(expr) { return Some(( vec![ (span, format!("{suggestion}(")), @@ -2902,16 +2900,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } - fn needs_parentheses(&self, expr: &hir::Expr<'_>) -> bool { - match expr.kind { - // parenthesize if needed (Issue #46756) - hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true, - // parenthesize borrows of range literals (Issue #54505) - _ if is_range_literal(expr) => true, - _ => false, - } - } - pub(crate) fn suggest_cast( &self, err: &mut Diag<'_>, diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index e0b6ea0ac9d..4008021c3a8 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -19,8 +19,7 @@ use rustc_middle::ty::{ self, GenericArgs, GenericArgsRef, GenericParamDefKind, Ty, TypeVisitableExt, }; use rustc_middle::{bug, span_bug}; -use rustc_span::symbol::Ident; -use rustc_span::{ErrorGuaranteed, Span}; +use rustc_span::{ErrorGuaranteed, Ident, Span}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{self, NormalizeExt}; use tracing::{debug, instrument}; diff --git a/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs b/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs index b20592c85d2..5ccfcf93f69 100644 --- a/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs +++ b/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs @@ -8,9 +8,7 @@ use rustc_lint::{ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER}; use rustc_middle::span_bug; use rustc_middle::ty::{self, Ty}; use rustc_session::lint::builtin::{RUST_2021_PRELUDE_COLLISIONS, RUST_2024_PRELUDE_COLLISIONS}; -use rustc_span::Span; -use rustc_span::symbol::kw::{Empty, Underscore}; -use rustc_span::symbol::{Ident, sym}; +use rustc_span::{Ident, Span, kw, sym}; use rustc_trait_selection::infer::InferCtxtExt; use tracing::debug; @@ -369,11 +367,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect(); // Find an identifier with which this trait was imported (note that `_` doesn't count). - let any_id = import_items - .iter() - .find_map(|item| if item.ident.name != Underscore { Some(item.ident) } else { None }); + let any_id = import_items.iter().find_map(|item| { + if item.ident.name != kw::Underscore { Some(item.ident) } else { None } + }); if let Some(any_id) = any_id { - if any_id.name == Empty { + if any_id.name == kw::Empty { // Glob import, so just use its name. return None; } else { diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 3b377076545..ace114f1ffa 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -25,8 +25,7 @@ use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::edit_distance::{ edit_distance_with_substrings, find_best_match_for_name_with_substrings, }; -use rustc_span::symbol::{Ident, sym}; -use rustc_span::{DUMMY_SP, Span, Symbol}; +use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym}; use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::query::CanonicalTyGoal; @@ -1230,23 +1229,16 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { if let Some(by_value_pick) = by_value_pick { if let Ok(by_value_pick) = by_value_pick.as_ref() { if by_value_pick.kind == PickKind::InherentImplPick { - if let Err(e) = self.check_for_shadowed_autorefd_method( - by_value_pick, - step, - self_ty, - hir::Mutability::Not, - track_unstable_candidates, - ) { - return Some(Err(e)); - } - if let Err(e) = self.check_for_shadowed_autorefd_method( - by_value_pick, - step, - self_ty, - hir::Mutability::Mut, - track_unstable_candidates, - ) { - return Some(Err(e)); + for mutbl in [hir::Mutability::Not, hir::Mutability::Mut] { + if let Err(e) = self.check_for_shadowed_autorefd_method( + by_value_pick, + step, + self_ty, + mutbl, + track_unstable_candidates, + ) { + return Some(Err(e)); + } } } } @@ -1337,6 +1329,15 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { mutbl: hir::Mutability, track_unstable_candidates: bool, ) -> Result<(), MethodError<'tcx>> { + // The errors emitted by this function are part of + // the arbitrary self types work, and should not impact + // other users. + if !self.tcx.features().arbitrary_self_types() + && !self.tcx.features().arbitrary_self_types_pointers() + { + return Ok(()); + } + // We don't want to remember any of the diagnostic hints from this // shadow search, but we do need to provide Some/None for the // unstable_candidates in order to reflect the behavior of the diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 6b1a288510a..1de88d52767 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -8,7 +8,7 @@ use std::borrow::Cow; use hir::Expr; use rustc_ast::ast::Mutability; -use rustc_attr::parse_confusables; +use rustc_attr_parsing::parse_confusables; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::unord::UnordSet; @@ -27,9 +27,9 @@ use rustc_middle::ty::print::{ }; use rustc_middle::ty::{self, GenericArgKind, IsSuggestable, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::def_id::DefIdSet; -use rustc_span::symbol::{Ident, kw, sym}; use rustc_span::{ - DUMMY_SP, ErrorGuaranteed, ExpnKind, FileName, MacroKind, Span, Symbol, edit_distance, + DUMMY_SP, ErrorGuaranteed, ExpnKind, FileName, Ident, MacroKind, Span, Symbol, edit_distance, + kw, sym, }; use rustc_trait_selection::error_reporting::traits::DefIdOrName; use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedNote; diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 72b930ee84d..805079afdb0 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -11,9 +11,8 @@ use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; use rustc_session::errors::ExprParenthesesNeeded; -use rustc_span::Span; use rustc_span::source_map::Spanned; -use rustc_span::symbol::{Ident, sym}; +use rustc_span::{Ident, Span, sym}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{FulfillmentError, Obligation, ObligationCtxt}; use rustc_type_ir::TyKind::*; diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 2a8ed26aa2b..98b28240f4c 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -10,8 +10,12 @@ use rustc_errors::{ }; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; -use rustc_hir::{self as hir, BindingMode, ByRef, HirId, LangItem, Mutability, Pat, PatKind}; +use rustc_hir::{ + self as hir, BindingMode, ByRef, ExprKind, HirId, LangItem, Mutability, Pat, PatKind, + expr_needs_parens, +}; use rustc_infer::infer; +use rustc_middle::traits::PatternOriginExpr; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; @@ -19,8 +23,7 @@ use rustc_session::parse::feature_err; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::Spanned; -use rustc_span::symbol::{Ident, kw, sym}; -use rustc_span::{BytePos, DUMMY_SP, Span}; +use rustc_span::{BytePos, DUMMY_SP, Ident, Span, kw, sym}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode}; use tracing::{debug, instrument, trace}; @@ -94,10 +97,32 @@ struct PatInfo<'a, 'tcx> { impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn pattern_cause(&self, ti: &TopInfo<'tcx>, cause_span: Span) -> ObligationCause<'tcx> { + // If origin_expr exists, then expected represents the type of origin_expr. + // If span also exists, then span == origin_expr.span (although it doesn't need to exist). + // In that case, we can peel away references from both and treat them + // as the same. + let origin_expr_info = ti.origin_expr.map(|mut cur_expr| { + let mut count = 0; + + // cur_ty may have more layers of references than cur_expr. + // We can only make suggestions about cur_expr, however, so we'll + // use that as our condition for stopping. + while let ExprKind::AddrOf(.., inner) = &cur_expr.kind { + cur_expr = inner; + count += 1; + } + + PatternOriginExpr { + peeled_span: cur_expr.span, + peeled_count: count, + peeled_prefix_suggestion_parentheses: expr_needs_parens(cur_expr), + } + }); + let code = ObligationCauseCode::Pattern { span: ti.span, root_ty: ti.expected, - origin_expr: ti.origin_expr.is_some(), + origin_expr: origin_expr_info, }; self.cause(cause_span, code) } @@ -692,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) } } @@ -705,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 } @@ -2240,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", + ) } } @@ -2604,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_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index 7c667511aa9..ba635071135 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -7,8 +7,7 @@ use rustc_middle::ty::adjustment::{ PointerCoercion, }; use rustc_middle::ty::{self, Ty}; -use rustc_span::Span; -use rustc_span::symbol::{Ident, sym}; +use rustc_span::{Ident, Span, sym}; use tracing::debug; use {rustc_ast as ast, rustc_hir as hir}; diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 4f283644cbe..b3085843141 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -14,7 +14,7 @@ //! to everything owned by `x`, so the result is the same for something //! like `x.f = 5` and so on (presuming `x` is not a borrowed pointer to a //! struct). These adjustments are performed in -//! `adjust_upvar_borrow_kind()` (you can trace backwards through the code +//! `adjust_for_non_move_closure` (you can trace backwards through the code //! from there). //! //! The fact that we are inferring borrow kinds as we go results in a @@ -1684,8 +1684,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // want to capture by ref to allow precise capture using reborrows. // // If the data will be moved out of this place, then the place will be truncated - // at the first Deref in `adjust_upvar_borrow_kind_for_consume` and then moved into - // the closure. + // at the first Deref in `adjust_for_move_closure` and then moved into the closure. hir::CaptureBy::Value { .. } if !place.deref_tys().any(Ty::is_ref) => { ty::UpvarCapture::ByValue } diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 6f1e3a0cf8c..5612aa75aae 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -15,8 +15,7 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, fold_regions}; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperFoldable}; -use rustc_span::Span; -use rustc_span::symbol::sym; +use rustc_span::{Span, sym}; use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded; use rustc_trait_selection::solve; use tracing::{debug, instrument}; @@ -555,11 +554,13 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { let hidden_type = self.resolve(decl.hidden_type, &decl.hidden_type.span); let opaque_type_key = self.resolve(opaque_type_key, &decl.hidden_type.span); - if let ty::Alias(ty::Opaque, alias_ty) = hidden_type.ty.kind() - && alias_ty.def_id == opaque_type_key.def_id.to_def_id() - && alias_ty.args == opaque_type_key.args - { - continue; + if !self.fcx.next_trait_solver() { + if let ty::Alias(ty::Opaque, alias_ty) = hidden_type.ty.kind() + && alias_ty.def_id == opaque_type_key.def_id.to_def_id() + && alias_ty.args == opaque_type_key.args + { + continue; + } } // Here we only detect impl trait definition conflicts when they diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs index 1f46155abc8..569034954c3 100644 --- a/compiler/rustc_incremental/src/assert_dep_graph.rs +++ b/compiler/rustc_incremental/src/assert_dep_graph.rs @@ -47,10 +47,9 @@ use rustc_middle::dep_graph::{ use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; use rustc_middle::{bug, span_bug}; -use rustc_span::Span; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::{Span, Symbol, sym}; use tracing::debug; -use {rustc_ast as ast, rustc_graphviz as dot, rustc_hir as hir}; +use {rustc_graphviz as dot, rustc_hir as hir}; use crate::errors; @@ -106,7 +105,7 @@ struct IfThisChanged<'tcx> { } impl<'tcx> IfThisChanged<'tcx> { - fn argument(&self, attr: &ast::Attribute) -> Option<Symbol> { + fn argument(&self, attr: &hir::Attribute) -> Option<Symbol> { let mut value = None; for list_item in attr.meta_item_list().unwrap_or_default() { match list_item.ident() { diff --git a/compiler/rustc_incremental/src/errors.rs b/compiler/rustc_incremental/src/errors.rs index cb21f975878..b4a207386dc 100644 --- a/compiler/rustc_incremental/src/errors.rs +++ b/compiler/rustc_incremental/src/errors.rs @@ -1,8 +1,7 @@ use std::path::{Path, PathBuf}; use rustc_macros::Diagnostic; -use rustc_span::symbol::Ident; -use rustc_span::{Span, Symbol}; +use rustc_span::{Ident, Span, Symbol}; #[derive(Diagnostic)] #[diag(incremental_unrecognized_depnode)] diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs index 2075d4214c1..b99872e7ae6 100644 --- a/compiler/rustc_incremental/src/persist/dirty_clean.rs +++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs @@ -19,16 +19,17 @@ //! Errors are reported if we are in the suitable configuration but //! the required condition is not met. -use rustc_ast::{self as ast, Attribute, MetaItemInner}; +use rustc_ast::{self as ast, MetaItemInner}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::unord::UnordSet; use rustc_hir::def_id::LocalDefId; -use rustc_hir::{ImplItemKind, ItemKind as HirItem, Node as HirNode, TraitItemKind, intravisit}; +use rustc_hir::{ + Attribute, ImplItemKind, ItemKind as HirItem, Node as HirNode, TraitItemKind, intravisit, +}; use rustc_middle::dep_graph::{DepNode, DepNodeExt, label_strs}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; -use rustc_span::Span; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::{Span, Symbol, sym}; use thin_vec::ThinVec; use tracing::debug; diff --git a/compiler/rustc_index/Cargo.toml b/compiler/rustc_index/Cargo.toml index 33e8e2824c7..f27db7a5400 100644 --- a/compiler/rustc_index/Cargo.toml +++ b/compiler/rustc_index/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start -rustc_index_macros = { path = "../rustc_index_macros", default-features = false } +rustc_index_macros = { path = "../rustc_index_macros" } rustc_macros = { path = "../rustc_macros", optional = true } rustc_serialize = { path = "../rustc_serialize", optional = true } smallvec = "1.8.1" diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index aba1e938296..664b77fd49e 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -1191,6 +1191,14 @@ impl<T: Idx> MixedBitSet<T> { } } + #[inline] + pub fn clear(&mut self) { + match self { + MixedBitSet::Small(set) => set.clear(), + MixedBitSet::Large(set) => set.clear(), + } + } + bit_relations_inherent_impls! {} } diff --git a/compiler/rustc_index_macros/Cargo.toml b/compiler/rustc_index_macros/Cargo.toml index a7c2a1804dd..98bc1b6a29b 100644 --- a/compiler/rustc_index_macros/Cargo.toml +++ b/compiler/rustc_index_macros/Cargo.toml @@ -12,5 +12,4 @@ proc-macro2 = "1" quote = "1" [features] -default = ["nightly"] nightly = [] diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 1d3d32ef749..d5aab4781de 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -316,16 +316,6 @@ impl<'tcx> InferCtxt<'tcx> { }), ); - // ...also include the query member constraints. - output_query_region_constraints.member_constraints.extend( - query_response - .value - .region_constraints - .member_constraints - .iter() - .map(|p_c| instantiate_value(self.tcx, &result_args, p_c.clone())), - ); - let user_result: R = query_response.instantiate_projected(self.tcx, &result_args, |q_r| q_r.value.clone()); @@ -643,7 +633,7 @@ pub fn make_query_region_constraints<'tcx>( outlives_obligations: impl Iterator<Item = (Ty<'tcx>, ty::Region<'tcx>, ConstraintCategory<'tcx>)>, region_constraints: &RegionConstraintData<'tcx>, ) -> QueryRegionConstraints<'tcx> { - let RegionConstraintData { constraints, verifys, member_constraints } = region_constraints; + let RegionConstraintData { constraints, verifys } = region_constraints; assert!(verifys.is_empty()); @@ -674,5 +664,5 @@ pub fn make_query_region_constraints<'tcx>( })) .collect(); - QueryRegionConstraints { outlives, member_constraints: member_constraints.clone() } + QueryRegionConstraints { outlives } } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 544f941dda5..c5a56005c06 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -17,7 +17,6 @@ pub use relate::StructurallyRelateAliases; pub use relate::combine::PredicateEmittingRelation; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; -use rustc_data_structures::sync::Lrc; use rustc_data_structures::undo_log::{Rollback, UndoLogs}; use rustc_data_structures::unify as ut; use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed}; @@ -41,8 +40,7 @@ use rustc_middle::ty::{ GenericParamDefKind, InferConst, IntVid, PseudoCanonicalInput, Ty, TyCtxt, TyVid, TypeVisitable, TypingEnv, TypingMode, }; -use rustc_span::Span; -use rustc_span::symbol::Symbol; +use rustc_span::{Span, Symbol}; use snapshot::undo_log::InferCtxtUndoLogs; use tracing::{debug, instrument}; use type_variable::TypeVariableOrigin; @@ -686,26 +684,6 @@ impl<'tcx> InferCtxt<'tcx> { self.inner.borrow_mut().unwrap_region_constraints().make_subregion(origin, a, b); } - /// Require that the region `r` be equal to one of the regions in - /// the set `regions`. - #[instrument(skip(self), level = "debug")] - pub fn member_constraint( - &self, - key: ty::OpaqueTypeKey<'tcx>, - definition_span: Span, - hidden_ty: Ty<'tcx>, - region: ty::Region<'tcx>, - in_regions: Lrc<Vec<ty::Region<'tcx>>>, - ) { - self.inner.borrow_mut().unwrap_region_constraints().member_constraint( - key, - definition_span, - hidden_ty, - region, - in_regions, - ); - } - /// Processes a `Coerce` predicate from the fulfillment context. /// This is NOT the preferred way to handle coercion, which is to /// invoke `FnCtxt::coerce` or a similar method (see `coercion.rs`). diff --git a/compiler/rustc_infer/src/infer/opaque_types/mod.rs b/compiler/rustc_infer/src/infer/opaque_types/mod.rs index b64686afd23..137d438a479 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs @@ -1,6 +1,5 @@ use hir::def_id::{DefId, LocalDefId}; use rustc_data_structures::fx::FxIndexMap; -use rustc_data_structures::sync::Lrc; use rustc_hir as hir; use rustc_middle::bug; use rustc_middle::traits::ObligationCause; @@ -8,8 +7,7 @@ use rustc_middle::traits::solve::Goal; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::BottomUpFolder; use rustc_middle::ty::{ - self, GenericArgKind, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, - TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, + self, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, }; use rustc_span::Span; use tracing::{debug, instrument}; @@ -181,289 +179,6 @@ impl<'tcx> InferCtxt<'tcx> { Err(TypeError::Sorts(ExpectedFound::new(a, b))) } } - - /// Given the map `opaque_types` containing the opaque - /// `impl Trait` types whose underlying, hidden types are being - /// inferred, this method adds constraints to the regions - /// appearing in those underlying hidden types to ensure that they - /// at least do not refer to random scopes within the current - /// function. These constraints are not (quite) sufficient to - /// guarantee that the regions are actually legal values; that - /// final condition is imposed after region inference is done. - /// - /// # The Problem - /// - /// Let's work through an example to explain how it works. Assume - /// the current function is as follows: - /// - /// ```text - /// fn foo<'a, 'b>(..) -> (impl Bar<'a>, impl Bar<'b>) - /// ``` - /// - /// Here, we have two `impl Trait` types whose values are being - /// inferred (the `impl Bar<'a>` and the `impl - /// Bar<'b>`). Conceptually, this is sugar for a setup where we - /// define underlying opaque types (`Foo1`, `Foo2`) and then, in - /// the return type of `foo`, we *reference* those definitions: - /// - /// ```text - /// type Foo1<'x> = impl Bar<'x>; - /// type Foo2<'x> = impl Bar<'x>; - /// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. } - /// // ^^^^ ^^ - /// // | | - /// // | args - /// // def_id - /// ``` - /// - /// As indicating in the comments above, each of those references - /// is (in the compiler) basically generic parameters (`args`) - /// applied to the type of a suitable `def_id` (which identifies - /// `Foo1` or `Foo2`). - /// - /// Now, at this point in compilation, what we have done is to - /// replace each of the references (`Foo1<'a>`, `Foo2<'b>`) with - /// fresh inference variables C1 and C2. We wish to use the values - /// of these variables to infer the underlying types of `Foo1` and - /// `Foo2`. That is, this gives rise to higher-order (pattern) unification - /// constraints like: - /// - /// ```text - /// for<'a> (Foo1<'a> = C1) - /// for<'b> (Foo1<'b> = C2) - /// ``` - /// - /// For these equation to be satisfiable, the types `C1` and `C2` - /// can only refer to a limited set of regions. For example, `C1` - /// can only refer to `'static` and `'a`, and `C2` can only refer - /// to `'static` and `'b`. The job of this function is to impose that - /// constraint. - /// - /// Up to this point, C1 and C2 are basically just random type - /// inference variables, and hence they may contain arbitrary - /// regions. In fact, it is fairly likely that they do! Consider - /// this possible definition of `foo`: - /// - /// ```text - /// fn foo<'a, 'b>(x: &'a i32, y: &'b i32) -> (impl Bar<'a>, impl Bar<'b>) { - /// (&*x, &*y) - /// } - /// ``` - /// - /// Here, the values for the concrete types of the two impl - /// traits will include inference variables: - /// - /// ```text - /// &'0 i32 - /// &'1 i32 - /// ``` - /// - /// Ordinarily, the subtyping rules would ensure that these are - /// sufficiently large. But since `impl Bar<'a>` isn't a specific - /// type per se, we don't get such constraints by default. This - /// is where this function comes into play. It adds extra - /// constraints to ensure that all the regions which appear in the - /// inferred type are regions that could validly appear. - /// - /// This is actually a bit of a tricky constraint in general. We - /// want to say that each variable (e.g., `'0`) can only take on - /// values that were supplied as arguments to the opaque type - /// (e.g., `'a` for `Foo1<'a>`) or `'static`, which is always in - /// scope. We don't have a constraint quite of this kind in the current - /// region checker. - /// - /// # The Solution - /// - /// We generally prefer to make `<=` constraints, since they - /// integrate best into the region solver. To do that, we find the - /// "minimum" of all the arguments that appear in the args: that - /// is, some region which is less than all the others. In the case - /// of `Foo1<'a>`, that would be `'a` (it's the only choice, after - /// all). Then we apply that as a least bound to the variables - /// (e.g., `'a <= '0`). - /// - /// In some cases, there is no minimum. Consider this example: - /// - /// ```text - /// fn baz<'a, 'b>() -> impl Trait<'a, 'b> { ... } - /// ``` - /// - /// Here we would report a more complex "in constraint", like `'r - /// in ['a, 'b, 'static]` (where `'r` is some region appearing in - /// the hidden type). - /// - /// # Constrain regions, not the hidden concrete type - /// - /// Note that generating constraints on each region `Rc` is *not* - /// the same as generating an outlives constraint on `Tc` itself. - /// For example, if we had a function like this: - /// - /// ``` - /// # #![feature(type_alias_impl_trait)] - /// # fn main() {} - /// # trait Foo<'a> {} - /// # impl<'a, T> Foo<'a> for (&'a u32, T) {} - /// fn foo<'a, T>(x: &'a u32, y: T) -> impl Foo<'a> { - /// (x, y) - /// } - /// - /// // Equivalent to: - /// # mod dummy { use super::*; - /// type FooReturn<'a, T> = impl Foo<'a>; - /// fn foo<'a, T>(x: &'a u32, y: T) -> FooReturn<'a, T> { - /// (x, y) - /// } - /// # } - /// ``` - /// - /// then the hidden type `Tc` would be `(&'0 u32, T)` (where `'0` - /// is an inference variable). If we generated a constraint that - /// `Tc: 'a`, then this would incorrectly require that `T: 'a` -- - /// but this is not necessary, because the opaque type we - /// create will be allowed to reference `T`. So we only generate a - /// constraint that `'0: 'a`. - #[instrument(level = "debug", skip(self))] - pub fn register_member_constraints( - &self, - opaque_type_key: OpaqueTypeKey<'tcx>, - concrete_ty: Ty<'tcx>, - span: Span, - ) { - let concrete_ty = self.resolve_vars_if_possible(concrete_ty); - debug!(?concrete_ty); - - let variances = self.tcx.variances_of(opaque_type_key.def_id); - debug!(?variances); - - // For a case like `impl Foo<'a, 'b>`, we would generate a constraint - // `'r in ['a, 'b, 'static]` for each region `'r` that appears in the - // hidden type (i.e., it must be equal to `'a`, `'b`, or `'static`). - // - // `conflict1` and `conflict2` are the two region bounds that we - // detected which were unrelated. They are used for diagnostics. - - // Create the set of choice regions: each region in the hidden - // type can be equal to any of the region parameters of the - // opaque type definition. - let choice_regions: Lrc<Vec<ty::Region<'tcx>>> = Lrc::new( - opaque_type_key - .args - .iter() - .enumerate() - .filter(|(i, _)| variances[*i] == ty::Invariant) - .filter_map(|(_, arg)| match arg.unpack() { - GenericArgKind::Lifetime(r) => Some(r), - GenericArgKind::Type(_) | GenericArgKind::Const(_) => None, - }) - .chain(std::iter::once(self.tcx.lifetimes.re_static)) - .collect(), - ); - - // FIXME(#42940): This should use the `FreeRegionsVisitor`, but that's - // not currently sound until we have existential regions. - concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { - tcx: self.tcx, - op: |r| { - self.member_constraint( - opaque_type_key, - span, - concrete_ty, - r, - Lrc::clone(&choice_regions), - ) - }, - }); - } -} - -/// Visitor that requires that (almost) all regions in the type visited outlive -/// `least_region`. We cannot use `push_outlives_components` because regions in -/// closure signatures are not included in their outlives components. We need to -/// ensure all regions outlive the given bound so that we don't end up with, -/// say, `ReVar` appearing in a return type and causing ICEs when other -/// functions end up with region constraints involving regions from other -/// functions. -/// -/// We also cannot use `for_each_free_region` because for closures it includes -/// the regions parameters from the enclosing item. -/// -/// We ignore any type parameters because impl trait values are assumed to -/// capture all the in-scope type parameters. -struct ConstrainOpaqueTypeRegionVisitor<'tcx, OP: FnMut(ty::Region<'tcx>)> { - tcx: TyCtxt<'tcx>, - op: OP, -} - -impl<'tcx, OP> TypeVisitor<TyCtxt<'tcx>> for ConstrainOpaqueTypeRegionVisitor<'tcx, OP> -where - OP: FnMut(ty::Region<'tcx>), -{ - fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) { - t.super_visit_with(self); - } - - fn visit_region(&mut self, r: ty::Region<'tcx>) { - match *r { - // ignore bound regions, keep visiting - ty::ReBound(_, _) => {} - _ => (self.op)(r), - } - } - - fn visit_ty(&mut self, ty: Ty<'tcx>) { - // We're only interested in types involving regions - if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) { - return; - } - - match ty.kind() { - ty::Closure(_, args) => { - // Skip lifetime parameters of the enclosing item(s) - - for upvar in args.as_closure().upvar_tys() { - upvar.visit_with(self); - } - args.as_closure().sig_as_fn_ptr_ty().visit_with(self); - } - - ty::CoroutineClosure(_, args) => { - // Skip lifetime parameters of the enclosing item(s) - - for upvar in args.as_coroutine_closure().upvar_tys() { - upvar.visit_with(self); - } - - args.as_coroutine_closure().signature_parts_ty().visit_with(self); - } - - ty::Coroutine(_, args) => { - // Skip lifetime parameters of the enclosing item(s) - // Also skip the witness type, because that has no free regions. - - for upvar in args.as_coroutine().upvar_tys() { - upvar.visit_with(self); - } - args.as_coroutine().return_ty().visit_with(self); - args.as_coroutine().yield_ty().visit_with(self); - args.as_coroutine().resume_ty().visit_with(self); - } - - ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { - // Skip lifetime parameters that are not captures. - let variances = self.tcx.variances_of(*def_id); - - for (v, s) in std::iter::zip(variances, args.iter()) { - if *v != ty::Bivariant { - s.visit_with(self); - } - } - } - - _ => { - ty.super_visit_with(self); - } - } - } } impl<'tcx> InferCtxt<'tcx> { diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index 270217e26b7..6dce4b2b21d 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -4,7 +4,6 @@ use std::ops::Range; use std::{cmp, fmt, mem}; use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::sync::Lrc; use rustc_data_structures::undo_log::UndoLogs; use rustc_data_structures::unify as ut; use rustc_index::IndexVec; @@ -12,7 +11,6 @@ use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::infer::unify_key::{RegionVariableValue, RegionVidKey}; use rustc_middle::ty::{self, ReBound, ReStatic, ReVar, Region, RegionVid, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; -use rustc_span::Span; use tracing::{debug, instrument}; use self::CombineMapType::*; @@ -22,8 +20,6 @@ use crate::infer::snapshot::undo_log::{InferCtxtUndoLogs, Snapshot}; mod leak_check; -pub use rustc_middle::infer::MemberConstraint; - #[derive(Clone, Default)] pub struct RegionConstraintStorage<'tcx> { /// For each `RegionVid`, the corresponding `RegionVariableOrigin`. @@ -73,11 +69,6 @@ pub struct RegionConstraintData<'tcx> { /// be a region variable (or neither, as it happens). pub constraints: Vec<(Constraint<'tcx>, SubregionOrigin<'tcx>)>, - /// Constraints of the form `R0 member of [R1, ..., Rn]`, meaning that - /// `R0` must be equal to one of the regions `R1..Rn`. These occur - /// with `impl Trait` quite frequently. - pub member_constraints: Vec<MemberConstraint<'tcx>>, - /// A "verify" is something that we need to verify after inference /// is done, but which does not directly affect inference in any /// way. @@ -466,29 +457,6 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { } } - pub(super) fn member_constraint( - &mut self, - key: ty::OpaqueTypeKey<'tcx>, - definition_span: Span, - hidden_ty: Ty<'tcx>, - member_region: ty::Region<'tcx>, - choice_regions: Lrc<Vec<ty::Region<'tcx>>>, - ) { - debug!("member_constraint({:?} in {:#?})", member_region, choice_regions); - - if choice_regions.iter().any(|&r| r == member_region) { - return; - } - - self.storage.data.member_constraints.push(MemberConstraint { - key, - definition_span, - hidden_ty, - member_region, - choice_regions, - }); - } - #[instrument(skip(self, origin), level = "debug")] pub(super) fn make_subregion( &mut self, @@ -745,8 +713,8 @@ impl<'tcx> RegionConstraintData<'tcx> { /// Returns `true` if this region constraint data contains no constraints, and `false` /// otherwise. pub fn is_empty(&self) -> bool { - let RegionConstraintData { constraints, member_constraints, verifys } = self; - constraints.is_empty() && member_constraints.is_empty() && verifys.is_empty() + let RegionConstraintData { constraints, verifys } = self; + constraints.is_empty() && verifys.is_empty() } } diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index a977bd15d3b..ab8ada1596c 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -1,7 +1,6 @@ use rustc_data_structures::fx::FxHashSet; use rustc_middle::ty::{self, ToPolyTraitRef, TyCtxt}; -use rustc_span::Span; -use rustc_span::symbol::Ident; +use rustc_span::{Ident, Span}; pub use rustc_type_ir::elaborate::*; use crate::traits::{self, Obligation, ObligationCauseCode, PredicateObligation}; diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index 7a2ba07ce87..dcb9c5d22d6 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -11,7 +11,7 @@ rustc_ast = { path = "../rustc_ast" } rustc_ast_lowering = { path = "../rustc_ast_lowering" } rustc_ast_passes = { path = "../rustc_ast_passes" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_borrowck = { path = "../rustc_borrowck" } rustc_builtin_macros = { path = "../rustc_builtin_macros" } rustc_codegen_llvm = { path = "../rustc_codegen_llvm", optional = true } diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 91f190c6a28..1456255ea14 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -22,9 +22,8 @@ use rustc_session::config::{self, Cfg, CheckCfg, ExpectedValues, Input, OutFileN use rustc_session::filesearch::{self, sysroot_candidates}; use rustc_session::parse::ParseSess; use rustc_session::{CompilerIO, EarlyDiagCtxt, Session, lint}; -use rustc_span::FileName; use rustc_span::source_map::{FileLoader, RealFileLoader, SourceMapInputs}; -use rustc_span::symbol::sym; +use rustc_span::{FileName, sym}; use tracing::trace; use crate::util; diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs index 1c4dda2a436..a2a29612e48 100644 --- a/compiler/rustc_interface/src/lib.rs +++ b/compiler/rustc_interface/src/lib.rs @@ -8,7 +8,7 @@ // tidy-alphabetical-end mod callbacks; -mod errors; +pub mod errors; pub mod interface; pub mod passes; mod proc_macro_decls; @@ -17,8 +17,8 @@ pub mod util; pub use callbacks::setup_callbacks; pub use interface::{Config, run_compiler}; -pub use passes::DEFAULT_QUERY_PROVIDERS; -pub use queries::{Linker, Queries}; +pub use passes::{DEFAULT_QUERY_PROVIDERS, create_and_enter_global_ctxt, parse}; +pub use queries::Linker; #[cfg(test)] mod tests; diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 430bc7db077..aff66e48fbb 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -19,7 +19,7 @@ use rustc_incremental::setup_dep_graph; use rustc_lint::{BufferedEarlyLint, EarlyCheckNode, LintStore, unerased_lint_store}; use rustc_metadata::creader::CStore; use rustc_middle::arena::Arena; -use rustc_middle::ty::{self, GlobalCtxt, RegisteredTools, TyCtxt}; +use rustc_middle::ty::{self, CurrentGcx, GlobalCtxt, RegisteredTools, TyCtxt}; use rustc_middle::util::Providers; use rustc_parse::{ new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal, validate_attr, @@ -32,8 +32,7 @@ use rustc_session::cstore::Untracked; use rustc_session::output::{collect_crate_types, filename_for_input, find_crate_name}; use rustc_session::search_paths::PathKind; use rustc_session::{Limit, Session}; -use rustc_span::symbol::{Symbol, sym}; -use rustc_span::{ErrorGuaranteed, FileName, SourceFileHash, SourceFileHashAlgorithm}; +use rustc_span::{ErrorGuaranteed, FileName, SourceFileHash, SourceFileHashAlgorithm, Symbol, sym}; use rustc_target::spec::PanicStrategy; use rustc_trait_selection::traits; use tracing::{info, instrument}; @@ -41,7 +40,7 @@ use tracing::{info, instrument}; use crate::interface::Compiler; use crate::{errors, proc_macro_decls, util}; -pub(crate) fn parse<'a>(sess: &'a Session) -> ast::Crate { +pub fn parse<'a>(sess: &'a Session) -> ast::Crate { let krate = sess .time("parse_crate", || { let mut parser = unwrap_or_emit_fatal(match &sess.io.input { @@ -76,6 +75,7 @@ fn pre_expansion_lint<'a>( || { rustc_lint::check_ast_node( sess, + None, features, true, lint_store, @@ -310,6 +310,7 @@ fn early_lint_checks(tcx: TyCtxt<'_>, (): ()) { let lint_store = unerased_lint_store(tcx.sess); rustc_lint::check_ast_node( sess, + Some(tcx), tcx.features(), false, lint_store, @@ -709,13 +710,11 @@ pub static DEFAULT_QUERY_PROVIDERS: LazyLock<Providers> = LazyLock::new(|| { *providers }); -pub(crate) fn create_global_ctxt<'tcx>( - compiler: &'tcx Compiler, +pub fn create_and_enter_global_ctxt<T, F: for<'tcx> FnOnce(TyCtxt<'tcx>) -> T>( + compiler: &Compiler, mut krate: rustc_ast::Crate, - gcx_cell: &'tcx OnceLock<GlobalCtxt<'tcx>>, - arena: &'tcx WorkerLocal<Arena<'tcx>>, - hir_arena: &'tcx WorkerLocal<rustc_hir::Arena<'tcx>>, -) -> &'tcx GlobalCtxt<'tcx> { + f: F, +) -> T { let sess = &compiler.sess; rustc_builtin_macros::cmdline_attrs::inject( @@ -763,44 +762,63 @@ pub(crate) fn create_global_ctxt<'tcx>( let incremental = dep_graph.is_fully_enabled(); - sess.time("setup_global_ctxt", || { - let qcx = gcx_cell.get_or_init(move || { - TyCtxt::create_global_ctxt( - sess, - crate_types, - stable_crate_id, - arena, - hir_arena, - untracked, - dep_graph, - rustc_query_impl::query_callbacks(arena), - rustc_query_impl::query_system( - providers.queries, - providers.extern_queries, - query_result_on_disk_cache, - incremental, - ), - providers.hooks, - compiler.current_gcx.clone(), - ) - }); - - qcx.enter(|tcx| { - let feed = tcx.create_crate_num(stable_crate_id).unwrap(); - assert_eq!(feed.key(), LOCAL_CRATE); - feed.crate_name(crate_name); + let gcx_cell = OnceLock::new(); + let arena = WorkerLocal::new(|_| Arena::default()); + let hir_arena = WorkerLocal::new(|_| rustc_hir::Arena::default()); + + // This closure is necessary to force rustc to perform the correct lifetime + // subtyping for GlobalCtxt::enter to be allowed. + let inner: Box< + dyn for<'tcx> FnOnce( + &'tcx Session, + CurrentGcx, + &'tcx OnceLock<GlobalCtxt<'tcx>>, + &'tcx WorkerLocal<Arena<'tcx>>, + &'tcx WorkerLocal<rustc_hir::Arena<'tcx>>, + F, + ) -> T, + > = Box::new(move |sess, current_gcx, gcx_cell, arena, hir_arena, f| { + TyCtxt::create_global_ctxt( + gcx_cell, + sess, + crate_types, + stable_crate_id, + arena, + hir_arena, + untracked, + dep_graph, + rustc_query_impl::query_callbacks(arena), + rustc_query_impl::query_system( + providers.queries, + providers.extern_queries, + query_result_on_disk_cache, + incremental, + ), + providers.hooks, + current_gcx, + |tcx| { + let feed = tcx.create_crate_num(stable_crate_id).unwrap(); + assert_eq!(feed.key(), LOCAL_CRATE); + feed.crate_name(crate_name); + + let feed = tcx.feed_unit_query(); + feed.features_query(tcx.arena.alloc(rustc_expand::config::features( + tcx.sess, + &pre_configured_attrs, + crate_name, + ))); + feed.crate_for_resolver(tcx.arena.alloc(Steal::new((krate, pre_configured_attrs)))); + feed.output_filenames(Arc::new(outputs)); + + let res = f(tcx); + // FIXME maybe run finish even when a fatal error occured? or at least tcx.alloc_self_profile_query_strings()? + tcx.finish(); + res + }, + ) + }); - let feed = tcx.feed_unit_query(); - feed.features_query(tcx.arena.alloc(rustc_expand::config::features( - sess, - &pre_configured_attrs, - crate_name, - ))); - feed.crate_for_resolver(tcx.arena.alloc(Steal::new((krate, pre_configured_attrs)))); - feed.output_filenames(Arc::new(outputs)); - }); - qcx - }) + inner(&compiler.sess, compiler.current_gcx.clone(), &gcx_cell, &arena, &hir_arena, f) } /// Runs all analyses that we guarantee to run, even if errors were reported in earlier analyses. diff --git a/compiler/rustc_interface/src/proc_macro_decls.rs b/compiler/rustc_interface/src/proc_macro_decls.rs index 2c8014d8b3a..82593dbc2b7 100644 --- a/compiler/rustc_interface/src/proc_macro_decls.rs +++ b/compiler/rustc_interface/src/proc_macro_decls.rs @@ -2,7 +2,7 @@ use rustc_ast::attr; use rustc_hir::def_id::LocalDefId; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; -use rustc_span::symbol::sym; +use rustc_span::sym; fn proc_macro_decls_static(tcx: TyCtxt<'_>, (): ()) -> Option<LocalDefId> { let mut decls = None; diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index bb2ad3b3dd0..c8914c9be9c 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -1,117 +1,18 @@ use std::any::Any; -use std::cell::{RefCell, RefMut}; use std::sync::Arc; -use rustc_ast as ast; use rustc_codegen_ssa::CodegenResults; use rustc_codegen_ssa::traits::CodegenBackend; -use rustc_data_structures::steal::Steal; use rustc_data_structures::svh::Svh; -use rustc_data_structures::sync::{OnceLock, WorkerLocal}; use rustc_hir::def_id::LOCAL_CRATE; -use rustc_middle::arena::Arena; use rustc_middle::dep_graph::DepGraph; -use rustc_middle::ty::{GlobalCtxt, TyCtxt}; +use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_session::config::{self, OutputFilenames, OutputType}; use crate::errors::FailedWritingFile; -use crate::interface::Compiler; use crate::passes; -/// Represent the result of a query. -/// -/// This result can be stolen once with the [`steal`] method and generated with the [`compute`] method. -/// -/// [`steal`]: Steal::steal -/// [`compute`]: Self::compute -pub struct Query<T> { - /// `None` means no value has been computed yet. - result: RefCell<Option<Steal<T>>>, -} - -impl<T> Query<T> { - fn compute<F: FnOnce() -> T>(&self, f: F) -> QueryResult<'_, T> { - QueryResult(RefMut::map( - self.result.borrow_mut(), - |r: &mut Option<Steal<T>>| -> &mut Steal<T> { - r.get_or_insert_with(|| Steal::new(f())) - }, - )) - } -} - -pub struct QueryResult<'a, T>(RefMut<'a, Steal<T>>); - -impl<'a, T> std::ops::Deref for QueryResult<'a, T> { - type Target = RefMut<'a, Steal<T>>; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl<'a, T> std::ops::DerefMut for QueryResult<'a, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl<'a, 'tcx> QueryResult<'a, &'tcx GlobalCtxt<'tcx>> { - pub fn enter<T>(&mut self, f: impl FnOnce(TyCtxt<'tcx>) -> T) -> T { - (*self.0).borrow().enter(f) - } -} - -pub struct Queries<'tcx> { - compiler: &'tcx Compiler, - gcx_cell: OnceLock<GlobalCtxt<'tcx>>, - - arena: WorkerLocal<Arena<'tcx>>, - hir_arena: WorkerLocal<rustc_hir::Arena<'tcx>>, - - parse: Query<ast::Crate>, - // This just points to what's in `gcx_cell`. - gcx: Query<&'tcx GlobalCtxt<'tcx>>, -} - -impl<'tcx> Queries<'tcx> { - pub fn new(compiler: &'tcx Compiler) -> Queries<'tcx> { - Queries { - compiler, - gcx_cell: OnceLock::new(), - arena: WorkerLocal::new(|_| Arena::default()), - hir_arena: WorkerLocal::new(|_| rustc_hir::Arena::default()), - parse: Query { result: RefCell::new(None) }, - gcx: Query { result: RefCell::new(None) }, - } - } - - pub fn finish(&'tcx self) { - if let Some(gcx) = self.gcx_cell.get() { - gcx.finish(); - } - } - - pub fn parse(&self) -> QueryResult<'_, ast::Crate> { - self.parse.compute(|| passes::parse(&self.compiler.sess)) - } - - pub fn global_ctxt(&'tcx self) -> QueryResult<'tcx, &'tcx GlobalCtxt<'tcx>> { - self.gcx.compute(|| { - let krate = self.parse().steal(); - - passes::create_global_ctxt( - self.compiler, - krate, - &self.gcx_cell, - &self.arena, - &self.hir_arena, - ) - }) - } -} - pub struct Linker { dep_graph: DepGraph, output_filenames: Arc<OutputFilenames>, @@ -186,22 +87,3 @@ impl Linker { codegen_backend.link(sess, codegen_results, &self.output_filenames) } } - -impl Compiler { - pub fn enter<F, T>(&self, f: F) -> T - where - F: for<'tcx> FnOnce(&'tcx Queries<'tcx>) -> T, - { - // Must declare `_timer` first so that it is dropped after `queries`. - let _timer; - let queries = Queries::new(self); - let ret = f(&queries); - - // The timer's lifetime spans the dropping of `queries`, which contains - // the global context. - _timer = self.sess.timer("free_global_ctxt"); - queries.finish(); - - ret - } -} diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index e76e9ca9f85..9ad69039914 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -23,8 +23,7 @@ use rustc_session::utils::{CanonicalizedPath, NativeLib, NativeLibKind}; use rustc_session::{CompilerIO, EarlyDiagCtxt, Session, build_session, filesearch, getopts}; use rustc_span::edition::{DEFAULT_EDITION, Edition}; use rustc_span::source_map::{RealFileLoader, SourceMapInputs}; -use rustc_span::symbol::sym; -use rustc_span::{FileName, SourceFileHashAlgorithm}; +use rustc_span::{FileName, SourceFileHashAlgorithm, sym}; use rustc_target::spec::{ CodeModel, FramePointer, LinkerFlavorCli, MergeFunctions, OnBrokenPipe, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TlsModel, WasmCAbi, @@ -767,7 +766,11 @@ fn test_unstable_options_tracking_hash() { }) ); tracked!(codegen_backend, Some("abc".to_string())); - tracked!(coverage_options, CoverageOptions { level: CoverageLevel::Mcdc, no_mir_spans: true }); + tracked!(coverage_options, CoverageOptions { + level: CoverageLevel::Mcdc, + no_mir_spans: true, + discard_all_spans_in_codegen: true + }); tracked!(crate_attr, vec!["abc".to_string()]); tracked!(cross_crate_inline_threshold, InliningThreshold::Always); tracked!(debug_info_for_profiling, true); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 2af25bfd3aa..984b8104f53 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -18,7 +18,7 @@ use rustc_session::{EarlyDiagCtxt, Session, filesearch}; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; use rustc_span::source_map::SourceMapInputs; -use rustc_span::symbol::sym; +use rustc_span::sym; use rustc_target::spec::Target; use tracing::info; @@ -453,7 +453,7 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu .opts .crate_name .clone() - .or_else(|| rustc_attr::find_crate_name(attrs).map(|n| n.to_string())); + .or_else(|| rustc_attr_parsing::find_crate_name(attrs).map(|n| n.to_string())); match sess.io.output_file { None => { diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml index ec5f0f06c59..cc5a9029633 100644 --- a/compiler/rustc_lint/Cargo.toml +++ b/compiler/rustc_lint/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 01d9ac20fae..7522e21d0ef 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -536,9 +536,6 @@ lint_non_camel_case_type = {$sort} `{$name}` should have an upper camel case nam .suggestion = convert the identifier to upper camel case .label = should have an UpperCamelCase name -lint_non_existent_doc_keyword = found non-existing keyword `{$keyword}` used in `#[doc(keyword = "...")]` - .help = only existing keywords are allowed in core/std - lint_non_fmt_panic = panic message is not a string literal .note = this usage of `{$name}!()` is deprecated; it will be a hard error in Rust 2021 .more_info_note = for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html> @@ -806,14 +803,14 @@ lint_unexpected_cfg_add_build_rs_println = or consider adding `{$build_rs_printl lint_unexpected_cfg_add_cargo_feature = consider using a Cargo feature instead lint_unexpected_cfg_add_cargo_toml_lint_cfg = or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint:{$cargo_toml_lint_cfg} lint_unexpected_cfg_add_cmdline_arg = to expect this configuration use `{$cmdline_arg}` -lint_unexpected_cfg_cargo_update = the {$macro_kind} `{$macro_name}` may come from an old version of it's defining crate, try updating your dependencies with `cargo update` +lint_unexpected_cfg_cargo_update = the {$macro_kind} `{$macro_name}` may come from an old version of the `{$crate_name}` crate, try updating your dependency with `cargo update -p {$crate_name}` lint_unexpected_cfg_define_features = consider defining some features in `Cargo.toml` lint_unexpected_cfg_doc_cargo = see <https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html> for more information about checking conditional configuration lint_unexpected_cfg_doc_rustc = see <https://doc.rust-lang.org/nightly/rustc/check-cfg.html> for more information about checking conditional configuration lint_unexpected_cfg_from_external_macro_origin = using a cfg inside a {$macro_kind} will use the cfgs from the destination crate and not the ones from the defining crate -lint_unexpected_cfg_from_external_macro_refer = try refering to `{$macro_name}` crate for guidance on how handle this unexpected cfg +lint_unexpected_cfg_from_external_macro_refer = try referring to `{$macro_name}` crate for guidance on how handle this unexpected cfg lint_unexpected_cfg_name = unexpected `cfg` condition name: `{$name}` lint_unexpected_cfg_name_expected_names = expected names are: {$possibilities}{$and_more -> [0] {""} diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 3543784bc72..6e823957cc6 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -39,8 +39,7 @@ pub use rustc_session::lint::builtin::*; use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass}; use rustc_span::edition::Edition; use rustc_span::source_map::Spanned; -use rustc_span::symbol::{Ident, Symbol, kw, sym}; -use rustc_span::{BytePos, InnerSpan, Span}; +use rustc_span::{BytePos, Ident, InnerSpan, Span, Symbol, kw, sym}; use rustc_target::asm::InlineAsmArch; use rustc_trait_selection::infer::{InferCtxtExt, TyCtxtInferExt}; use rustc_trait_selection::traits::misc::type_allowed_to_implement_copy; @@ -387,7 +386,7 @@ pub struct MissingDoc; impl_lint_pass!(MissingDoc => [MISSING_DOCS]); -fn has_doc(attr: &ast::Attribute) -> bool { +fn has_doc(attr: &hir::Attribute) -> bool { if attr.is_doc_comment() { return true; } @@ -1012,7 +1011,7 @@ declare_lint_pass!(InvalidNoMangleItems => [NO_MANGLE_CONST_ITEMS, NO_MANGLE_GEN impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems { fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { let attrs = cx.tcx.hir().attrs(it.hir_id()); - let check_no_mangle_on_generic_fn = |no_mangle_attr: &ast::Attribute, + let check_no_mangle_on_generic_fn = |no_mangle_attr: &hir::Attribute, impl_generics: Option<&hir::Generics<'_>>, generics: &hir::Generics<'_>, span| { @@ -1176,7 +1175,7 @@ declare_lint_pass!( ); impl<'tcx> LateLintPass<'tcx> for UnstableFeatures { - fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) { + fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &hir::Attribute) { if attr.has_name(sym::feature) && let Some(items) = attr.meta_item_list() { @@ -1814,7 +1813,7 @@ declare_lint! { "detects edition keywords being used as an identifier", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024), - reference: "issue #49716 <https://github.com/rust-lang/rust/issues/49716>", + reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/gen-keyword.html>", }; } @@ -1829,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/context.rs b/compiler/rustc_lint/src/context.rs index 44c7888a530..50bb1fb942e 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -20,12 +20,11 @@ use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::print::{PrintError, PrintTraitRefExt as _, Printer, with_no_trimmed_paths}; use rustc_middle::ty::{self, GenericArg, RegisteredTools, Ty, TyCtxt, TypingEnv, TypingMode}; use rustc_session::lint::{ - BuiltinLintDiag, FutureIncompatibleInfo, Level, Lint, LintBuffer, LintExpectationId, LintId, + FutureIncompatibleInfo, Level, Lint, LintBuffer, LintExpectationId, LintId, }; use rustc_session::{LintStoreMarker, Session}; -use rustc_span::Span; use rustc_span::edit_distance::find_best_match_for_names; -use rustc_span::symbol::{Ident, Symbol, sym}; +use rustc_span::{Ident, Span, Symbol, sym}; use tracing::debug; use {rustc_abi as abi, rustc_hir as hir}; @@ -33,8 +32,6 @@ use self::TargetLint::*; use crate::levels::LintLevelsBuilder; use crate::passes::{EarlyLintPassObject, LateLintPassObject}; -mod diagnostics; - type EarlyLintPassFactory = dyn Fn() -> EarlyLintPassObject + sync::DynSend + sync::DynSync; type LateLintPassFactory = dyn for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx> + sync::DynSend + sync::DynSync; @@ -511,38 +508,6 @@ pub struct EarlyContext<'a> { pub buffered: LintBuffer, } -impl EarlyContext<'_> { - /// Emit a lint at the appropriate level, with an associated span and an existing - /// diagnostic. - /// - /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature - #[rustc_lint_diagnostics] - pub fn span_lint_with_diagnostics( - &self, - lint: &'static Lint, - span: MultiSpan, - diagnostic: BuiltinLintDiag, - ) { - self.opt_span_lint_with_diagnostics(lint, Some(span), diagnostic); - } - - /// Emit a lint at the appropriate level, with an optional associated span and an existing - /// diagnostic. - /// - /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature - #[rustc_lint_diagnostics] - pub fn opt_span_lint_with_diagnostics( - &self, - lint: &'static Lint, - span: Option<MultiSpan>, - diagnostic: BuiltinLintDiag, - ) { - self.opt_span_lint(lint, span, |diag| { - diagnostics::decorate_lint(self.sess(), diagnostic, diag); - }); - } -} - pub trait LintContext { fn sess(&self) -> &Session; diff --git a/compiler/rustc_lint/src/dangling.rs b/compiler/rustc_lint/src/dangling.rs index 10769b57a76..98b717a3070 100644 --- a/compiler/rustc_lint/src/dangling.rs +++ b/compiler/rustc_lint/src/dangling.rs @@ -4,8 +4,7 @@ use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, LangItem}; use rustc_middle::ty::{Ty, TyCtxt}; use rustc_session::{declare_lint, impl_lint_pass}; -use rustc_span::Span; -use rustc_span::symbol::sym; +use rustc_span::{Span, sym}; use crate::lints::DanglingPointersFromTemporaries; use crate::{LateContext, LateLintPass}; diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index a68a2a7f983..bc7cd3d118c 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -9,35 +9,39 @@ use rustc_ast::visit::{self as ast_visit, Visitor, walk_list}; use rustc_ast::{self as ast, HasAttrs}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_feature::Features; -use rustc_middle::ty::RegisteredTools; +use rustc_middle::ty::{RegisteredTools, TyCtxt}; use rustc_session::Session; use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass}; -use rustc_span::Span; -use rustc_span::symbol::Ident; +use rustc_span::{Ident, Span}; use tracing::debug; -use crate::context::{EarlyContext, LintStore}; +use crate::context::{EarlyContext, LintContext, LintStore}; use crate::passes::{EarlyLintPass, EarlyLintPassObject}; +mod diagnostics; + macro_rules! lint_callback { ($cx:expr, $f:ident, $($args:expr),*) => ({ $cx.pass.$f(&$cx.context, $($args),*); }) } /// Implements the AST traversal for early lint passes. `T` provides the /// `check_*` methods. -pub struct EarlyContextAndPass<'a, T: EarlyLintPass> { - context: EarlyContext<'a>, +pub struct EarlyContextAndPass<'ecx, 'tcx, T: EarlyLintPass> { + context: EarlyContext<'ecx>, + tcx: Option<TyCtxt<'tcx>>, pass: T, } -impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> { +impl<'ecx, 'tcx, T: EarlyLintPass> EarlyContextAndPass<'ecx, 'tcx, T> { // This always-inlined function is for the hot call site. #[inline(always)] #[allow(rustc::diagnostic_outside_of_impl)] fn inlined_check_id(&mut self, id: ast::NodeId) { for early_lint in self.context.buffered.take(id) { let BufferedEarlyLint { span, node_id: _, lint_id, diagnostic } = early_lint; - self.context.opt_span_lint_with_diagnostics(lint_id.lint, span, diagnostic); + self.context.opt_span_lint(lint_id.lint, span, |diag| { + diagnostics::decorate_lint(self.context.sess(), self.tcx, diagnostic, diag); + }); } } @@ -49,7 +53,7 @@ impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> { /// Merge the lints specified by any lint attributes into the /// current lint context, call the provided function, then reset the /// lints in effect to their previous state. - fn with_lint_attrs<F>(&mut self, id: ast::NodeId, attrs: &'a [ast::Attribute], f: F) + fn with_lint_attrs<F>(&mut self, id: ast::NodeId, attrs: &'_ [ast::Attribute], f: F) where F: FnOnce(&mut Self), { @@ -67,19 +71,21 @@ impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> { } } -impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> { - fn visit_coroutine_kind(&mut self, coroutine_kind: &'a ast::CoroutineKind) -> Self::Result { +impl<'ast, 'ecx, 'tcx, T: EarlyLintPass> ast_visit::Visitor<'ast> + for EarlyContextAndPass<'ecx, 'tcx, T> +{ + fn visit_coroutine_kind(&mut self, coroutine_kind: &'ast ast::CoroutineKind) -> Self::Result { self.check_id(coroutine_kind.closure_id()); } - fn visit_param(&mut self, param: &'a ast::Param) { + fn visit_param(&mut self, param: &'ast ast::Param) { self.with_lint_attrs(param.id, ¶m.attrs, |cx| { lint_callback!(cx, check_param, param); ast_visit::walk_param(cx, param); }); } - fn visit_item(&mut self, it: &'a ast::Item) { + fn visit_item(&mut self, it: &'ast ast::Item) { self.with_lint_attrs(it.id, &it.attrs, |cx| { lint_callback!(cx, check_item, it); ast_visit::walk_item(cx, it); @@ -87,31 +93,31 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> }) } - fn visit_foreign_item(&mut self, it: &'a ast::ForeignItem) { + fn visit_foreign_item(&mut self, it: &'ast ast::ForeignItem) { self.with_lint_attrs(it.id, &it.attrs, |cx| { ast_visit::walk_item(cx, it); }) } - fn visit_pat(&mut self, p: &'a ast::Pat) { + fn visit_pat(&mut self, p: &'ast ast::Pat) { lint_callback!(self, check_pat, p); self.check_id(p.id); ast_visit::walk_pat(self, p); lint_callback!(self, check_pat_post, p); } - fn visit_pat_field(&mut self, field: &'a ast::PatField) { + fn visit_pat_field(&mut self, field: &'ast ast::PatField) { self.with_lint_attrs(field.id, &field.attrs, |cx| { ast_visit::walk_pat_field(cx, field); }); } - fn visit_anon_const(&mut self, c: &'a ast::AnonConst) { + fn visit_anon_const(&mut self, c: &'ast ast::AnonConst) { self.check_id(c.id); ast_visit::walk_anon_const(self, c); } - fn visit_expr(&mut self, e: &'a ast::Expr) { + fn visit_expr(&mut self, e: &'ast ast::Expr) { self.with_lint_attrs(e.id, &e.attrs, |cx| { lint_callback!(cx, check_expr, e); ast_visit::walk_expr(cx, e); @@ -119,13 +125,13 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> }) } - fn visit_expr_field(&mut self, f: &'a ast::ExprField) { + fn visit_expr_field(&mut self, f: &'ast ast::ExprField) { self.with_lint_attrs(f.id, &f.attrs, |cx| { ast_visit::walk_expr_field(cx, f); }) } - fn visit_stmt(&mut self, s: &'a ast::Stmt) { + fn visit_stmt(&mut self, s: &'ast ast::Stmt) { // Add the statement's lint attributes to our // current state when checking the statement itself. // This allows us to handle attributes like @@ -145,33 +151,33 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> ast_visit::walk_stmt(self, s); } - fn visit_fn(&mut self, fk: ast_visit::FnKind<'a>, span: Span, id: ast::NodeId) { + fn visit_fn(&mut self, fk: ast_visit::FnKind<'ast>, span: Span, id: ast::NodeId) { lint_callback!(self, check_fn, fk, span, id); self.check_id(id); ast_visit::walk_fn(self, fk); } - fn visit_variant_data(&mut self, s: &'a ast::VariantData) { + fn visit_variant_data(&mut self, s: &'ast ast::VariantData) { if let Some(ctor_node_id) = s.ctor_node_id() { self.check_id(ctor_node_id); } ast_visit::walk_struct_def(self, s); } - fn visit_field_def(&mut self, s: &'a ast::FieldDef) { + fn visit_field_def(&mut self, s: &'ast ast::FieldDef) { self.with_lint_attrs(s.id, &s.attrs, |cx| { ast_visit::walk_field_def(cx, s); }) } - fn visit_variant(&mut self, v: &'a ast::Variant) { + fn visit_variant(&mut self, v: &'ast ast::Variant) { self.with_lint_attrs(v.id, &v.attrs, |cx| { lint_callback!(cx, check_variant, v); ast_visit::walk_variant(cx, v); }) } - fn visit_ty(&mut self, t: &'a ast::Ty) { + fn visit_ty(&mut self, t: &'ast ast::Ty) { lint_callback!(self, check_ty, t); self.check_id(t.id); ast_visit::walk_ty(self, t); @@ -181,55 +187,55 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> lint_callback!(self, check_ident, ident); } - fn visit_local(&mut self, l: &'a ast::Local) { + fn visit_local(&mut self, l: &'ast ast::Local) { self.with_lint_attrs(l.id, &l.attrs, |cx| { lint_callback!(cx, check_local, l); ast_visit::walk_local(cx, l); }) } - fn visit_block(&mut self, b: &'a ast::Block) { + fn visit_block(&mut self, b: &'ast ast::Block) { lint_callback!(self, check_block, b); self.check_id(b.id); ast_visit::walk_block(self, b); } - fn visit_arm(&mut self, a: &'a ast::Arm) { + fn visit_arm(&mut self, a: &'ast ast::Arm) { self.with_lint_attrs(a.id, &a.attrs, |cx| { lint_callback!(cx, check_arm, a); ast_visit::walk_arm(cx, a); }) } - fn visit_generic_arg(&mut self, arg: &'a ast::GenericArg) { + fn visit_generic_arg(&mut self, arg: &'ast ast::GenericArg) { lint_callback!(self, check_generic_arg, arg); ast_visit::walk_generic_arg(self, arg); } - fn visit_generic_param(&mut self, param: &'a ast::GenericParam) { + fn visit_generic_param(&mut self, param: &'ast ast::GenericParam) { self.with_lint_attrs(param.id, ¶m.attrs, |cx| { lint_callback!(cx, check_generic_param, param); ast_visit::walk_generic_param(cx, param); }); } - fn visit_generics(&mut self, g: &'a ast::Generics) { + fn visit_generics(&mut self, g: &'ast ast::Generics) { lint_callback!(self, check_generics, g); ast_visit::walk_generics(self, g); } - fn visit_where_predicate(&mut self, p: &'a ast::WherePredicate) { + fn visit_where_predicate(&mut self, p: &'ast ast::WherePredicate) { lint_callback!(self, enter_where_predicate, p); ast_visit::walk_where_predicate(self, p); lint_callback!(self, exit_where_predicate, p); } - fn visit_poly_trait_ref(&mut self, t: &'a ast::PolyTraitRef) { + fn visit_poly_trait_ref(&mut self, t: &'ast ast::PolyTraitRef) { lint_callback!(self, check_poly_trait_ref, t); ast_visit::walk_poly_trait_ref(self, t); } - fn visit_assoc_item(&mut self, item: &'a ast::AssocItem, ctxt: ast_visit::AssocCtxt) { + fn visit_assoc_item(&mut self, item: &'ast ast::AssocItem, ctxt: ast_visit::AssocCtxt) { self.with_lint_attrs(item.id, &item.attrs, |cx| { match ctxt { ast_visit::AssocCtxt::Trait => { @@ -243,32 +249,32 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> }); } - fn visit_lifetime(&mut self, lt: &'a ast::Lifetime, _: ast_visit::LifetimeCtxt) { + fn visit_lifetime(&mut self, lt: &'ast ast::Lifetime, _: ast_visit::LifetimeCtxt) { self.check_id(lt.id); ast_visit::walk_lifetime(self, lt); } - fn visit_path(&mut self, p: &'a ast::Path, id: ast::NodeId) { + fn visit_path(&mut self, p: &'ast ast::Path, id: ast::NodeId) { self.check_id(id); ast_visit::walk_path(self, p); } - fn visit_path_segment(&mut self, s: &'a ast::PathSegment) { + fn visit_path_segment(&mut self, s: &'ast ast::PathSegment) { self.check_id(s.id); ast_visit::walk_path_segment(self, s); } - fn visit_attribute(&mut self, attr: &'a ast::Attribute) { + fn visit_attribute(&mut self, attr: &'ast ast::Attribute) { lint_callback!(self, check_attribute, attr); ast_visit::walk_attribute(self, attr); } - fn visit_mac_def(&mut self, mac: &'a ast::MacroDef, id: ast::NodeId) { + fn visit_mac_def(&mut self, mac: &'ast ast::MacroDef, id: ast::NodeId) { lint_callback!(self, check_mac_def, mac); self.check_id(id); } - fn visit_mac_call(&mut self, mac: &'a ast::MacCall) { + fn visit_mac_call(&mut self, mac: &'ast ast::MacCall) { lint_callback!(self, check_mac, mac); ast_visit::walk_mac(self, mac); } @@ -310,28 +316,18 @@ crate::early_lint_methods!(impl_early_lint_pass, []); /// This trait generalizes over those nodes. pub trait EarlyCheckNode<'a>: Copy { fn id(self) -> ast::NodeId; - fn attrs<'b>(self) -> &'b [ast::Attribute] - where - 'a: 'b; - fn check<'b, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, T>) - where - 'a: 'b; + fn attrs(self) -> &'a [ast::Attribute]; + fn check<'ecx, 'tcx, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'ecx, 'tcx, T>); } impl<'a> EarlyCheckNode<'a> for (&'a ast::Crate, &'a [ast::Attribute]) { fn id(self) -> ast::NodeId { ast::CRATE_NODE_ID } - fn attrs<'b>(self) -> &'b [ast::Attribute] - where - 'a: 'b, - { + fn attrs(self) -> &'a [ast::Attribute] { self.1 } - fn check<'b, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, T>) - where - 'a: 'b, - { + fn check<'ecx, 'tcx, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'ecx, 'tcx, T>) { lint_callback!(cx, check_crate, self.0); ast_visit::walk_crate(cx, self.0); lint_callback!(cx, check_crate_post, self.0); @@ -342,16 +338,10 @@ impl<'a> EarlyCheckNode<'a> for (ast::NodeId, &'a [ast::Attribute], &'a [P<ast:: fn id(self) -> ast::NodeId { self.0 } - fn attrs<'b>(self) -> &'b [ast::Attribute] - where - 'a: 'b, - { + fn attrs(self) -> &'a [ast::Attribute] { self.1 } - fn check<'b, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, T>) - where - 'a: 'b, - { + fn check<'ecx, 'tcx, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'ecx, 'tcx, T>) { walk_list!(cx, visit_attribute, self.1); walk_list!(cx, visit_item, self.2); } @@ -359,6 +349,7 @@ impl<'a> EarlyCheckNode<'a> for (ast::NodeId, &'a [ast::Attribute], &'a [P<ast:: pub fn check_ast_node<'a>( sess: &Session, + tcx: Option<TyCtxt<'_>>, features: &Features, pre_expansion: bool, lint_store: &LintStore, @@ -382,22 +373,23 @@ pub fn check_ast_node<'a>( let passes = if pre_expansion { &lint_store.pre_expansion_passes } else { &lint_store.early_passes }; if passes.is_empty() { - check_ast_node_inner(sess, check_node, context, builtin_lints); + check_ast_node_inner(sess, tcx, check_node, context, builtin_lints); } else { let mut passes: Vec<_> = passes.iter().map(|mk_pass| (mk_pass)()).collect(); passes.push(Box::new(builtin_lints)); let pass = RuntimeCombinedEarlyLintPass { passes: &mut passes[..] }; - check_ast_node_inner(sess, check_node, context, pass); + check_ast_node_inner(sess, tcx, check_node, context, pass); } } fn check_ast_node_inner<'a, T: EarlyLintPass>( sess: &Session, + tcx: Option<TyCtxt<'_>>, check_node: impl EarlyCheckNode<'a>, context: EarlyContext<'_>, pass: T, ) { - let mut cx = EarlyContextAndPass { context, pass }; + let mut cx = EarlyContextAndPass { context, tcx, pass }; cx.with_lint_attrs(check_node.id(), check_node.attrs(), |cx| check_node.check(cx)); diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index a3731e31c2b..6d73715562b 100644 --- a/compiler/rustc_lint/src/context/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -8,17 +8,22 @@ use rustc_errors::{ Applicability, Diag, DiagArgValue, LintDiagnostic, elided_lifetime_in_path_suggestion, }; use rustc_middle::middle::stability; +use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_session::lint::{BuiltinLintDiag, ElidedLifetimeResolution}; -use rustc_span::BytePos; -use rustc_span::symbol::kw; +use rustc_span::{BytePos, kw}; use tracing::debug; use crate::lints::{self, ElidedNamedLifetime}; mod check_cfg; -pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Diag<'_, ()>) { +pub(super) fn decorate_lint( + sess: &Session, + tcx: Option<TyCtxt<'_>>, + diagnostic: BuiltinLintDiag, + diag: &mut Diag<'_, ()>, +) { match diagnostic { BuiltinLintDiag::UnicodeTextFlow(comment_span, content) => { let spans: Vec<_> = content @@ -199,10 +204,10 @@ pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: & .decorate_lint(diag); } BuiltinLintDiag::UnexpectedCfgName(name, value) => { - check_cfg::unexpected_cfg_name(sess, name, value).decorate_lint(diag); + check_cfg::unexpected_cfg_name(sess, tcx, name, value).decorate_lint(diag); } BuiltinLintDiag::UnexpectedCfgValue(name, value) => { - check_cfg::unexpected_cfg_value(sess, name, value).decorate_lint(diag); + check_cfg::unexpected_cfg_value(sess, tcx, name, value).decorate_lint(diag); } BuiltinLintDiag::DeprecatedWhereclauseLocation(left_sp, sugg) => { let suggestion = match sugg { diff --git a/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs b/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs index 63a722f6067..eebb131599a 100644 --- a/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs +++ b/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs @@ -1,10 +1,10 @@ use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::bug; +use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_session::config::ExpectedValues; use rustc_span::edit_distance::find_best_match_for_name; -use rustc_span::symbol::Ident; -use rustc_span::{ExpnKind, Span, Symbol, sym}; +use rustc_span::{ExpnKind, Ident, Span, Symbol, sym}; use crate::lints; @@ -73,17 +73,20 @@ fn rustc_macro_help(span: Span) -> Option<lints::UnexpectedCfgRustcMacroHelp> { } } -fn cargo_macro_help(span: Span) -> Option<lints::UnexpectedCfgCargoMacroHelp> { +fn cargo_macro_help( + tcx: Option<TyCtxt<'_>>, + span: Span, +) -> Option<lints::UnexpectedCfgCargoMacroHelp> { let oexpn = span.ctxt().outer_expn_data(); if let Some(def_id) = oexpn.macro_def_id && let ExpnKind::Macro(macro_kind, macro_name) = oexpn.kind && def_id.krate != LOCAL_CRATE + && let Some(tcx) = tcx { Some(lints::UnexpectedCfgCargoMacroHelp { macro_kind: macro_kind.descr(), macro_name, - // FIXME: Get access to a `TyCtxt` from an `EarlyContext` - // crate_name: cx.tcx.crate_name(def_id.krate), + crate_name: tcx.crate_name(def_id.krate), }) } else { None @@ -92,6 +95,7 @@ fn cargo_macro_help(span: Span) -> Option<lints::UnexpectedCfgCargoMacroHelp> { pub(super) fn unexpected_cfg_name( sess: &Session, + tcx: Option<TyCtxt<'_>>, (name, name_span): (Symbol, Span), value: Option<(Symbol, Span)>, ) -> lints::UnexpectedCfgName { @@ -223,7 +227,7 @@ pub(super) fn unexpected_cfg_name( }; lints::unexpected_cfg_name::InvocationHelp::Cargo { help, - macro_help: cargo_macro_help(name_span), + macro_help: cargo_macro_help(tcx, name_span), } } else { let help = lints::UnexpectedCfgRustcHelp::new(&inst(EscapeQuotes::No)); @@ -238,6 +242,7 @@ pub(super) fn unexpected_cfg_name( pub(super) fn unexpected_cfg_value( sess: &Session, + tcx: Option<TyCtxt<'_>>, (name, name_span): (Symbol, Span), value: Option<(Symbol, Span)>, ) -> lints::UnexpectedCfgValue { @@ -339,7 +344,7 @@ pub(super) fn unexpected_cfg_value( }; lints::unexpected_cfg_value::InvocationHelp::Cargo { help, - macro_help: cargo_macro_help(name_span), + macro_help: cargo_macro_help(tcx, name_span), } } else { let help = if can_suggest_adding_value { diff --git a/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs b/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs index 8e22a9bdc45..6556a8d8f2d 100644 --- a/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs +++ b/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs @@ -2,8 +2,7 @@ use rustc_hir as hir; use rustc_middle::ty::Ty; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_session::{declare_lint, declare_lint_pass}; -use rustc_span::Span; -use rustc_span::symbol::sym; +use rustc_span::{Span, sym}; use crate::context::LintContext; use crate::lints::{EnumIntrinsicsMemDiscriminate, EnumIntrinsicsMemVariant}; diff --git a/compiler/rustc_lint/src/if_let_rescope.rs b/compiler/rustc_lint/src/if_let_rescope.rs index 1402129195f..259ea908fc6 100644 --- a/compiler/rustc_lint/src/if_let_rescope.rs +++ b/compiler/rustc_lint/src/if_let_rescope.rs @@ -84,7 +84,7 @@ declare_lint! { rewriting in `match` is an option to preserve the semantics up to Edition 2021", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024), - reference: "issue #124085 <https://github.com/rust-lang/rust/issues/124085>", + reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-if-let-scope.html>", }; } diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index 1aacdbd448c..b5dd6cf16ee 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -177,7 +177,7 @@ fn check_fn(tcx: TyCtxt<'_>, parent_def_id: LocalDefId) { // Lazily compute these two, since they're likely a bit expensive. variances: LazyCell::new(|| { let mut functional_variances = FunctionalVariances { - tcx: tcx, + tcx, variances: FxHashMap::default(), ambient_variance: ty::Covariant, generics: tcx.generics_of(parent_def_id), @@ -325,7 +325,7 @@ where ParamKind::Free(def_id, name) => ty::Region::new_late_param( self.tcx, self.parent_def_id.to_def_id(), - ty::BoundRegionKind::Named(def_id, name), + ty::LateParamRegionKind::Named(def_id, name), ), // Totally ignore late bound args from binders. ParamKind::Late => return true, @@ -475,7 +475,7 @@ fn extract_def_id_from_arg<'tcx>( ) | ty::ReLateParam(ty::LateParamRegion { scope: _, - bound_region: ty::BoundRegionKind::Named(def_id, ..), + kind: ty::LateParamRegionKind::Named(def_id, ..), }) => def_id, _ => unreachable!(), }, @@ -544,7 +544,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for FunctionalVariances<'tcx> { ) | ty::ReLateParam(ty::LateParamRegion { scope: _, - bound_region: ty::BoundRegionKind::Named(def_id, ..), + kind: ty::LateParamRegionKind::Named(def_id, ..), }) => def_id, _ => { return Ok(a); diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 482650e04e8..b31a4c74787 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -10,13 +10,12 @@ use rustc_hir::{ }; use rustc_middle::ty::{self, GenericArgsRef, Ty as MiddleTy}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; use rustc_span::hygiene::{ExpnKind, MacroKind}; -use rustc_span::symbol::{Symbol, kw, sym}; +use rustc_span::{Span, sym}; use tracing::debug; use crate::lints::{ - BadOptAccessDiag, DefaultHashTypesDiag, DiagOutOfImpl, LintPassByHand, NonExistentDocKeyword, + BadOptAccessDiag, DefaultHashTypesDiag, DiagOutOfImpl, LintPassByHand, NonGlobImportTypeIrInherent, QueryInstability, QueryUntracked, SpanUseEqCtxtDiag, SymbolInternStringLiteralDiag, TyQualified, TykindDiag, TykindKind, TypeIrInherentUsage, UntranslatableDiag, @@ -171,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 { @@ -376,46 +359,6 @@ impl EarlyLintPass for LintPassImpl { } declare_tool_lint! { - /// The `existing_doc_keyword` lint detects use `#[doc()]` keywords - /// that don't exist, e.g. `#[doc(keyword = "..")]`. - pub rustc::EXISTING_DOC_KEYWORD, - Allow, - "Check that documented keywords in std and core actually exist", - report_in_external_macro: true -} - -declare_lint_pass!(ExistingDocKeyword => [EXISTING_DOC_KEYWORD]); - -fn is_doc_keyword(s: Symbol) -> bool { - s <= kw::Union -} - -impl<'tcx> LateLintPass<'tcx> for ExistingDocKeyword { - fn check_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::Item<'_>) { - for attr in cx.tcx.hir().attrs(item.hir_id()) { - if !attr.has_name(sym::doc) { - continue; - } - if let Some(list) = attr.meta_item_list() { - for nested in list { - if nested.has_name(sym::keyword) { - let keyword = nested - .value_str() - .expect("#[doc(keyword = \"...\")] expected a value!"); - if is_doc_keyword(keyword) { - return; - } - cx.emit_span_lint(EXISTING_DOC_KEYWORD, attr.span, NonExistentDocKeyword { - keyword, - }); - } - } - } - } - } -} - -declare_tool_lint! { /// The `untranslatable_diagnostic` lint detects messages passed to functions with `impl /// Into<{D,Subd}iagMessage` parameters without using translatable Fluent strings. /// diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 4b1dafbdbee..5a4ed68440a 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -1,3 +1,4 @@ +use rustc_ast::attr::AttributeExt; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::{Diag, LintDiagnostic, MultiSpan}; @@ -19,8 +20,7 @@ use rustc_session::lint::builtin::{ UNFULFILLED_LINT_EXPECTATIONS, UNKNOWN_LINTS, UNUSED_ATTRIBUTES, }; use rustc_session::lint::{Level, Lint, LintExpectationId, LintId}; -use rustc_span::symbol::{Symbol, sym}; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Span, Symbol, sym}; use tracing::{debug, instrument}; use {rustc_ast as ast, rustc_hir as hir}; @@ -371,7 +371,7 @@ impl<'tcx> Visitor<'tcx> for LintLevelMaximum<'tcx> { /// FIXME(blyxyas): In a future revision, we should also graph #![allow]s, /// but that is handled with more care - fn visit_attribute(&mut self, attribute: &'tcx ast::Attribute) { + fn visit_attribute(&mut self, attribute: &'tcx hir::Attribute) { if matches!( Level::from_attr(attribute), Some( @@ -383,10 +383,9 @@ impl<'tcx> Visitor<'tcx> for LintLevelMaximum<'tcx> { ) ) { let store = unerased_lint_store(self.tcx.sess); - let Some(meta) = attribute.meta() else { return }; // Lint attributes are always a metalist inside a // metalist (even with just one lint). - let Some(meta_item_list) = meta.meta_item_list() else { return }; + let Some(meta_item_list) = attribute.meta_item_list() else { return }; for meta_list in meta_item_list { // Convert Path to String @@ -686,7 +685,12 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { }; } - fn add(&mut self, attrs: &[ast::Attribute], is_crate_node: bool, source_hir_id: Option<HirId>) { + fn add( + &mut self, + attrs: &[impl AttributeExt], + is_crate_node: bool, + source_hir_id: Option<HirId>, + ) { let sess = self.sess; for (attr_index, attr) in attrs.iter().enumerate() { if attr.has_name(sym::automatically_derived) { @@ -910,7 +914,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { let src = LintLevelSource::Node { name, span: sp, reason }; for &id in ids { - if self.check_gated_lint(id, attr.span, false) { + if self.check_gated_lint(id, sp, false) { self.insert_spec(id, (level, src)); } } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index a99c94592b3..d7f0d2a6941 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -600,8 +600,6 @@ fn register_internals(store: &mut LintStore) { store.register_late_mod_pass(|_| Box::new(DefaultHashTypes)); store.register_lints(&QueryStability::lint_vec()); store.register_late_mod_pass(|_| Box::new(QueryStability)); - store.register_lints(&ExistingDocKeyword::lint_vec()); - store.register_late_mod_pass(|_| Box::new(ExistingDocKeyword)); store.register_lints(&TyTyKind::lint_vec()); store.register_late_mod_pass(|_| Box::new(TyTyKind)); store.register_lints(&TypeIr::lint_vec()); @@ -629,7 +627,6 @@ fn register_internals(store: &mut LintStore) { LintId::of(LINT_PASS_IMPL_WITHOUT_MACRO), LintId::of(USAGE_OF_QUALIFIED_TY), LintId::of(NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT), - LintId::of(EXISTING_DOC_KEYWORD), LintId::of(BAD_OPT_ACCESS), LintId::of(SPAN_USE_EQ_CTXT), ]); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 5da9f6d2053..ac995b59caf 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -16,8 +16,7 @@ use rustc_middle::ty::{Clause, PolyExistentialTraitRef, Ty, TyCtxt}; use rustc_session::Session; use rustc_session::lint::AmbiguityErrorDiag; use rustc_span::edition::Edition; -use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent}; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol, kw, sym}; use crate::builtin::{InitError, ShorthandAssocTyCollector, TypeAliasBounds}; use crate::errors::{OverruledAttributeSub, RequestedLevel}; @@ -951,13 +950,6 @@ pub(crate) struct NonGlobImportTypeIrInherent { pub(crate) struct LintPassByHand; #[derive(LintDiagnostic)] -#[diag(lint_non_existent_doc_keyword)] -#[help] -pub(crate) struct NonExistentDocKeyword { - pub keyword: Symbol, -} - -#[derive(LintDiagnostic)] #[diag(lint_diag_out_of_impl)] pub(crate) struct DiagOutOfImpl; @@ -1816,14 +1808,14 @@ pub(crate) enum AmbiguousWidePointerComparisonsAddrSuggestion<'a> { } #[derive(LintDiagnostic)] -pub(crate) enum UnpredictableFunctionPointerComparisons<'a> { +pub(crate) enum UnpredictableFunctionPointerComparisons<'a, 'tcx> { #[diag(lint_unpredictable_fn_pointer_comparisons)] #[note(lint_note_duplicated_fn)] #[note(lint_note_deduplicated_fn)] #[note(lint_note_visit_fn_addr_eq)] Suggestion { #[subdiagnostic] - sugg: UnpredictableFunctionPointerComparisonsSuggestion<'a>, + sugg: UnpredictableFunctionPointerComparisonsSuggestion<'a, 'tcx>, }, #[diag(lint_unpredictable_fn_pointer_comparisons)] #[note(lint_note_duplicated_fn)] @@ -1833,22 +1825,40 @@ pub(crate) enum UnpredictableFunctionPointerComparisons<'a> { } #[derive(Subdiagnostic)] -#[multipart_suggestion( - lint_fn_addr_eq_suggestion, - style = "verbose", - applicability = "maybe-incorrect" -)] -pub(crate) struct UnpredictableFunctionPointerComparisonsSuggestion<'a> { - pub ne: &'a str, - pub cast_right: String, - pub deref_left: &'a str, - pub deref_right: &'a str, - #[suggestion_part(code = "{ne}std::ptr::fn_addr_eq({deref_left}")] - pub left: Span, - #[suggestion_part(code = ", {deref_right}")] - pub middle: Span, - #[suggestion_part(code = "{cast_right})")] - pub right: Span, +pub(crate) enum UnpredictableFunctionPointerComparisonsSuggestion<'a, 'tcx> { + #[multipart_suggestion( + lint_fn_addr_eq_suggestion, + style = "verbose", + applicability = "maybe-incorrect" + )] + FnAddrEq { + ne: &'a str, + deref_left: &'a str, + deref_right: &'a str, + #[suggestion_part(code = "{ne}std::ptr::fn_addr_eq({deref_left}")] + left: Span, + #[suggestion_part(code = ", {deref_right}")] + middle: Span, + #[suggestion_part(code = ")")] + right: Span, + }, + #[multipart_suggestion( + lint_fn_addr_eq_suggestion, + style = "verbose", + applicability = "maybe-incorrect" + )] + FnAddrEqWithCast { + ne: &'a str, + deref_left: &'a str, + deref_right: &'a str, + fn_sig: rustc_middle::ty::PolyFnSig<'tcx>, + #[suggestion_part(code = "{ne}std::ptr::fn_addr_eq({deref_left}")] + left: Span, + #[suggestion_part(code = ", {deref_right}")] + middle: Span, + #[suggestion_part(code = " as {fn_sig})")] + right: Span, + }, } pub(crate) struct ImproperCTypes<'a> { @@ -2187,8 +2197,7 @@ pub(crate) struct UnexpectedCfgRustcMacroHelp { pub(crate) struct UnexpectedCfgCargoMacroHelp { pub macro_kind: &'static str, pub macro_name: Symbol, - // FIXME: Figure out a way to get the crate name - // crate_name: String, + pub crate_name: Symbol, } #[derive(LintDiagnostic)] @@ -2205,8 +2214,7 @@ pub(crate) struct UnexpectedCfgName { pub(crate) mod unexpected_cfg_name { use rustc_errors::DiagSymbolList; use rustc_macros::Subdiagnostic; - use rustc_span::symbol::Ident; - use rustc_span::{Span, Symbol}; + use rustc_span::{Ident, Span, Symbol}; #[derive(Subdiagnostic)] pub(crate) enum CodeSuggestion { @@ -2679,7 +2687,7 @@ impl<G: EmissionGuarantee> LintDiagnostic<'_, G> for ElidedNamedLifetime { // but currently this lint's suggestions can conflict with those of `clippy::needless_lifetimes`: // https://github.com/rust-lang/rust/pull/129840#issuecomment-2323349119 // HACK: `'static` suggestions will never sonflict, emit only those for now. - if name != rustc_span::symbol::kw::StaticLifetime { + if name != kw::StaticLifetime { return; } match kind { 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 23f4f728906..ce280fef8b6 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_lint/src/non_ascii_idents.rs b/compiler/rustc_lint/src/non_ascii_idents.rs index 9b495c19990..593c8616c1d 100644 --- a/compiler/rustc_lint/src/non_ascii_idents.rs +++ b/compiler/rustc_lint/src/non_ascii_idents.rs @@ -2,7 +2,7 @@ use rustc_ast as ast; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::unord::UnordMap; use rustc_session::{declare_lint, declare_lint_pass}; -use rustc_span::symbol::Symbol; +use rustc_span::Symbol; use unicode_security::general_security_profile::IdentifierType; use crate::lints::{ @@ -205,7 +205,7 @@ impl EarlyLintPass for NonAsciiIdents { (IdentifierType::Not_NFKC, "Not_NFKC"), ] { let codepoints: Vec<_> = - chars.extract_if(|(_, ty)| *ty == Some(id_ty)).collect(); + chars.extract_if(.., |(_, ty)| *ty == Some(id_ty)).collect(); if codepoints.is_empty() { continue; } @@ -217,7 +217,7 @@ impl EarlyLintPass for NonAsciiIdents { } let remaining = chars - .extract_if(|(c, _)| !GeneralSecurityProfile::identifier_allowed(*c)) + .extract_if(.., |(c, _)| !GeneralSecurityProfile::identifier_allowed(*c)) .collect::<Vec<_>>(); if !remaining.is_empty() { cx.emit_span_lint(UNCOMMON_CODEPOINTS, sp, IdentifierUncommonCodepoints { diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs index 36b1ff59c67..6f047b4da49 100644 --- a/compiler/rustc_lint/src/non_fmt_panic.rs +++ b/compiler/rustc_lint/src/non_fmt_panic.rs @@ -8,8 +8,7 @@ use rustc_parse_format::{ParseMode, Parser, Piece}; use rustc_session::lint::FutureIncompatibilityReason; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::edition::Edition; -use rustc_span::symbol::kw; -use rustc_span::{InnerSpan, Span, Symbol, hygiene, sym}; +use rustc_span::{InnerSpan, Span, Symbol, hygiene, kw, sym}; use rustc_trait_selection::infer::InferCtxtExt; use crate::lints::{NonFmtPanicBraces, NonFmtPanicUnused}; diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index 3c33b2dd478..a1cc3a85109 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -5,8 +5,7 @@ use rustc_hir::{Body, HirId, Item, ItemKind, Node, Path, TyKind}; use rustc_middle::ty::TyCtxt; use rustc_session::{declare_lint, impl_lint_pass}; use rustc_span::def_id::{DefId, LOCAL_CRATE}; -use rustc_span::symbol::kw; -use rustc_span::{ExpnKind, MacroKind, Span, sym}; +use rustc_span::{ExpnKind, MacroKind, Span, kw, sym}; use crate::lints::{NonLocalDefinitionsCargoUpdateNote, NonLocalDefinitionsDiag}; use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent}; diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index 4d8ebf2909e..70dce78b572 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -1,14 +1,13 @@ use rustc_abi::ExternAbi; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::FnKind; -use rustc_hir::{GenericParamKind, PatKind}; +use rustc_hir::{AttrArgs, AttrItem, AttrKind, GenericParamKind, PatKind}; use rustc_middle::ty; use rustc_session::config::CrateType; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::def_id::LocalDefId; -use rustc_span::symbol::{Ident, sym}; -use rustc_span::{BytePos, Span}; -use {rustc_ast as ast, rustc_attr as attr, rustc_hir as hir}; +use rustc_span::{BytePos, Ident, Span, sym}; +use {rustc_ast as ast, rustc_attr_parsing as attr, rustc_hir as hir}; use crate::lints::{ NonCamelCaseType, NonCamelCaseTypeSub, NonSnakeCaseDiag, NonSnakeCaseDiagSub, @@ -342,35 +341,35 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase { let crate_ident = if let Some(name) = &cx.tcx.sess.opts.crate_name { Some(Ident::from_str(name)) } else { - attr::find_by_name(cx.tcx.hir().attrs(hir::CRATE_HIR_ID), sym::crate_name) - .and_then(|attr| attr.meta()) - .and_then(|meta| { - meta.name_value_literal().and_then(|lit| { - if let ast::LitKind::Str(name, ..) = lit.kind { - // Discard the double quotes surrounding the literal. - let sp = cx - .sess() - .source_map() - .span_to_snippet(lit.span) - .ok() - .and_then(|snippet| { - let left = snippet.find('"')?; - let right = - snippet.rfind('"').map(|pos| snippet.len() - pos)?; - - Some( - lit.span - .with_lo(lit.span.lo() + BytePos(left as u32 + 1)) - .with_hi(lit.span.hi() - BytePos(right as u32)), - ) - }) - .unwrap_or(lit.span); - - Some(Ident::new(name, sp)) - } else { - None - } - }) + ast::attr::find_by_name(cx.tcx.hir().attrs(hir::CRATE_HIR_ID), sym::crate_name) + .and_then(|attr| { + if let AttrKind::Normal(n) = &attr.kind + && let AttrItem { args: AttrArgs::Eq { eq_span: _, expr: ref lit }, .. } = + n.as_ref() + && let ast::LitKind::Str(name, ..) = lit.kind + { + // Discard the double quotes surrounding the literal. + let sp = cx + .sess() + .source_map() + .span_to_snippet(lit.span) + .ok() + .and_then(|snippet| { + let left = snippet.find('"')?; + let right = snippet.rfind('"').map(|pos| snippet.len() - pos)?; + + Some( + lit.span + .with_lo(lit.span.lo() + BytePos(left as u32 + 1)) + .with_hi(lit.span.hi() - BytePos(right as u32)), + ) + }) + .unwrap_or(lit.span); + + Some(Ident::new(name, sp)) + } else { + None + } }) }; @@ -502,7 +501,7 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals { fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { let attrs = cx.tcx.hir().attrs(it.hir_id()); match it.kind { - hir::ItemKind::Static(..) if !attr::contains_name(attrs, sym::no_mangle) => { + hir::ItemKind::Static(..) if !ast::attr::contains_name(attrs, sym::no_mangle) => { NonUpperCaseGlobals::check_upper_case(cx, "static variable", &it.ident); } hir::ItemKind::Const(..) => { diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs index 76dc96ae00f..fa519281be5 100644 --- a/compiler/rustc_lint/src/noop_method_call.rs +++ b/compiler/rustc_lint/src/noop_method_call.rs @@ -3,7 +3,7 @@ use rustc_hir::{Expr, ExprKind}; use rustc_middle::ty; use rustc_middle::ty::adjustment::Adjust; use rustc_session::{declare_lint, declare_lint_pass}; -use rustc_span::symbol::sym; +use rustc_span::sym; use crate::context::LintContext; use crate::lints::{ diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs index 5de0d4bc870..53a411e2b87 100644 --- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs +++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs @@ -5,8 +5,7 @@ use rustc_middle::ty::fold::BottomUpFolder; use rustc_middle::ty::print::{PrintTraitPredicateExt as _, TraitPredPrintModifiersAndPath}; use rustc_middle::ty::{self, Ty, TypeFoldable}; use rustc_session::{declare_lint, declare_lint_pass}; -use rustc_span::Span; -use rustc_span::symbol::kw; +use rustc_span::{Span, kw}; use rustc_trait_selection::traits::{self, ObligationCtxt}; use crate::{LateContext, LateLintPass, LintContext}; diff --git a/compiler/rustc_lint/src/pass_by_value.rs b/compiler/rustc_lint/src/pass_by_value.rs index ec306f5f834..3f264859d48 100644 --- a/compiler/rustc_lint/src/pass_by_value.rs +++ b/compiler/rustc_lint/src/pass_by_value.rs @@ -3,7 +3,7 @@ use rustc_hir::def::Res; use rustc_hir::{GenericArg, PathSegment, QPath, TyKind}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::symbol::sym; +use rustc_span::sym; use crate::lints::PassByValueDiag; use crate::{LateContext, LateLintPass, LintContext}; diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs index 9d84d36e779..3a323298bee 100644 --- a/compiler/rustc_lint/src/passes.rs +++ b/compiler/rustc_lint/src/passes.rs @@ -42,9 +42,9 @@ macro_rules! late_lint_methods { fn check_field_def(a: &'tcx rustc_hir::FieldDef<'tcx>); fn check_variant(a: &'tcx rustc_hir::Variant<'tcx>); fn check_path(a: &rustc_hir::Path<'tcx>, b: rustc_hir::HirId); - fn check_attribute(a: &'tcx rustc_ast::Attribute); - fn check_attributes(a: &'tcx [rustc_ast::Attribute]); - fn check_attributes_post(a: &'tcx [rustc_ast::Attribute]); + fn check_attribute(a: &'tcx rustc_hir::Attribute); + fn check_attributes(a: &'tcx [rustc_hir::Attribute]); + fn check_attributes_post(a: &'tcx [rustc_hir::Attribute]); ]); ) } @@ -136,7 +136,7 @@ macro_rules! early_lint_methods { ($macro:path, $args:tt) => ( $macro!($args, [ fn check_param(a: &rustc_ast::Param); - fn check_ident(a: &rustc_span::symbol::Ident); + fn check_ident(a: &rustc_span::Ident); fn check_crate(a: &rustc_ast::Crate); fn check_crate_post(a: &rustc_ast::Crate); fn check_item(a: &rustc_ast::Item); diff --git a/compiler/rustc_lint/src/shadowed_into_iter.rs b/compiler/rustc_lint/src/shadowed_into_iter.rs index a73904cd776..f5ab44d7469 100644 --- a/compiler/rustc_lint/src/shadowed_into_iter.rs +++ b/compiler/rustc_lint/src/shadowed_into_iter.rs @@ -61,6 +61,7 @@ declare_lint! { "detects calling `into_iter` on boxed slices in Rust 2015, 2018, and 2021", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024), + reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/intoiterator-box-slice.html>" }; } diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs index b793ec6a493..a9797c3b32a 100644 --- a/compiler/rustc_lint/src/traits.rs +++ b/compiler/rustc_lint/src/traits.rs @@ -1,6 +1,6 @@ use rustc_hir::{self as hir, LangItem}; use rustc_session::{declare_lint, declare_lint_pass}; -use rustc_span::symbol::sym; +use rustc_span::sym; use crate::lints::{DropGlue, DropTraitConstraintsDiag}; use crate::{LateContext, LateLintPass, LintContext}; diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 33650be056d..c0371b1f606 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -4,7 +4,7 @@ use std::ops::ControlFlow; use rustc_abi::{BackendRepr, ExternAbi, TagEncoding, Variants, WrappingRange}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::DiagMessage; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutOf, SizeSkeleton}; use rustc_middle::ty::{ @@ -12,8 +12,7 @@ use rustc_middle::ty::{ }; use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass}; use rustc_span::def_id::LocalDefId; -use rustc_span::symbol::sym; -use rustc_span::{Span, Symbol, source_map}; +use rustc_span::{Span, Symbol, source_map, sym}; use tracing::debug; use {rustc_ast as ast, rustc_hir as hir}; @@ -445,7 +444,25 @@ fn lint_fn_pointer<'tcx>( let (l_ty, l_ty_refs) = peel_refs(l_ty); let (r_ty, r_ty_refs) = peel_refs(r_ty); - if !l_ty.is_fn() || !r_ty.is_fn() { + if l_ty.is_fn() && r_ty.is_fn() { + // both operands are function pointers, fallthrough + } else if let ty::Adt(l_def, l_args) = l_ty.kind() + && let ty::Adt(r_def, r_args) = r_ty.kind() + && cx.tcx.is_lang_item(l_def.did(), LangItem::Option) + && cx.tcx.is_lang_item(r_def.did(), LangItem::Option) + && let Some(l_some_arg) = l_args.get(0) + && let Some(r_some_arg) = r_args.get(0) + && l_some_arg.expect_ty().is_fn() + && r_some_arg.expect_ty().is_fn() + { + // both operands are `Option<{function ptr}>` + return cx.emit_span_lint( + UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS, + e.span, + UnpredictableFunctionPointerComparisons::Warn, + ); + } else { + // types are not function pointers, nothing to do return; } @@ -483,29 +500,36 @@ fn lint_fn_pointer<'tcx>( let middle = l_span.shrink_to_hi().until(r_span.shrink_to_lo()); let right = r_span.shrink_to_hi().until(e.span.shrink_to_hi()); - // We only check for a right cast as `FnDef` == `FnPtr` is not possible, - // only `FnPtr == FnDef` is possible. - let cast_right = if !r_ty.is_fn_ptr() { - let fn_sig = r_ty.fn_sig(cx.tcx); - format!(" as {fn_sig}") - } else { - String::new() - }; + let sugg = + // We only check for a right cast as `FnDef` == `FnPtr` is not possible, + // only `FnPtr == FnDef` is possible. + if !r_ty.is_fn_ptr() { + let fn_sig = r_ty.fn_sig(cx.tcx); - cx.emit_span_lint( - UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS, - e.span, - UnpredictableFunctionPointerComparisons::Suggestion { - sugg: UnpredictableFunctionPointerComparisonsSuggestion { + UnpredictableFunctionPointerComparisonsSuggestion::FnAddrEqWithCast { ne, + fn_sig, deref_left, deref_right, left, middle, right, - cast_right, - }, - }, + } + } else { + UnpredictableFunctionPointerComparisonsSuggestion::FnAddrEq { + ne, + deref_left, + deref_right, + left, + middle, + right, + } + }; + + cx.emit_span_lint( + UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS, + e.span, + UnpredictableFunctionPointerComparisons::Suggestion { sugg }, ); } diff --git a/compiler/rustc_lint/src/types/literal.rs b/compiler/rustc_lint/src/types/literal.rs index dca42fea57d..83942918e3b 100644 --- a/compiler/rustc_lint/src/types/literal.rs +++ b/compiler/rustc_lint/src/types/literal.rs @@ -3,7 +3,7 @@ use rustc_abi::{Integer, Size}; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::{bug, ty}; -use {rustc_ast as ast, rustc_attr as attr, rustc_hir as hir}; +use {rustc_ast as ast, rustc_attr_parsing as attr, rustc_hir as hir}; use crate::LateContext; use crate::context::LintContext; diff --git a/compiler/rustc_lint/src/unqualified_local_imports.rs b/compiler/rustc_lint/src/unqualified_local_imports.rs index bea01a33bd6..c9dd6b32d88 100644 --- a/compiler/rustc_lint/src/unqualified_local_imports.rs +++ b/compiler/rustc_lint/src/unqualified_local_imports.rs @@ -1,7 +1,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::{self as hir}; use rustc_session::{declare_lint, declare_lint_pass}; -use rustc_span::symbol::kw; +use rustc_span::kw; use crate::{LateContext, LateLintPass, LintContext, lints}; @@ -31,7 +31,7 @@ declare_lint! { /// /// This lint is meant to be used with the (unstable) rustfmt setting `group_imports = "StdExternalCrate"`. /// That setting makes rustfmt group `self::`, `super::`, and `crate::` imports separately from those - /// refering to other crates. However, rustfmt cannot know whether `use c::S;` refers to a local module `c` + /// referring to other crates. However, rustfmt cannot know whether `use c::S;` refers to a local module `c` /// or an external crate `c`, so it always gets categorized as an import from another crate. /// To ensure consistent grouping of imports from the local crate, all local imports must /// start with `self::`, `super::`, or `crate::`. This lint can be used to enforce that style. diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 9cad5d98562..2b3cb14f9e9 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -11,8 +11,7 @@ use rustc_hir::{self as hir, LangItem}; use rustc_infer::traits::util::elaborate; use rustc_middle::ty::{self, Ty, adjustment}; use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass}; -use rustc_span::symbol::{Symbol, kw, sym}; -use rustc_span::{BytePos, Span}; +use rustc_span::{BytePos, Span, Symbol, kw, sym}; use tracing::instrument; use crate::lints::{ diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 54e927df3c4..2f23ab27492 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -1677,7 +1677,7 @@ declare_lint! { "detects patterns whose meaning will change in Rust 2024", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024), - reference: "123076", + reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>", }; } @@ -2606,7 +2606,7 @@ declare_lint! { "unsafe operations in unsafe functions without an explicit unsafe block are deprecated", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024), - reference: "issue #71668 <https://github.com/rust-lang/rust/issues/71668>", + reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/unsafe-op-in-unsafe-fn.html>", explain_reason: false }; @edition Edition2024 => Warn; @@ -4189,7 +4189,7 @@ declare_lint! { "never type fallback affecting unsafe function calls", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionAndFutureReleaseSemanticsChange(Edition::Edition2024), - reference: "issue #123748 <https://github.com/rust-lang/rust/issues/123748>", + reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/never-type-fallback.html>", }; @edition Edition2024 => Deny; report_in_external_macro @@ -4243,7 +4243,7 @@ declare_lint! { "never type fallback affecting unsafe function calls", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionAndFutureReleaseError(Edition::Edition2024), - reference: "issue #123748 <https://github.com/rust-lang/rust/issues/123748>", + reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/never-type-fallback.html>", }; report_in_external_macro } @@ -4790,7 +4790,7 @@ declare_lint! { "detects unsafe functions being used as safe functions", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024), - reference: "issue #27970 <https://github.com/rust-lang/rust/issues/27970>", + reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/newly-unsafe-functions.html>", }; } @@ -4826,7 +4826,7 @@ declare_lint! { "detects missing unsafe keyword on extern declarations", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024), - reference: "issue #123743 <https://github.com/rust-lang/rust/issues/123743>", + reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/unsafe-extern.html>", }; } @@ -4867,7 +4867,7 @@ declare_lint! { "detects unsafe attributes outside of unsafe", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024), - reference: "issue #123757 <https://github.com/rust-lang/rust/issues/123757>", + reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/unsafe-attributes.html>", }; } @@ -5069,7 +5069,7 @@ declare_lint! { "Detect and warn on significant change in drop order in tail expression location", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024), - reference: "issue #123739 <https://github.com/rust-lang/rust/issues/123739>", + reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-tail-expr-scope.html>", }; } @@ -5108,7 +5108,7 @@ declare_lint! { "will be parsed as a guarded string in Rust 2024", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024), - reference: "issue #123735 <https://github.com/rust-lang/rust/issues/123735>", + reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/reserved-syntax.html>", }; crate_level_only } diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index eb761bd6475..7786d3eb59a 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -3,8 +3,9 @@ // tidy-alphabetical-end use rustc_abi::ExternAbi; +use rustc_ast::AttrId; +use rustc_ast::attr::AttributeExt; use rustc_ast::node_id::NodeId; -use rustc_ast::{AttrId, Attribute}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::stable_hasher::{ HashStable, StableCompare, StableHasher, ToStableHashKey, @@ -14,8 +15,7 @@ use rustc_hir::def::Namespace; use rustc_hir::{HashStableContext, HirId, MissingLifetimeKind}; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; pub use rustc_span::edition::Edition; -use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent}; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol, sym}; use serde::{Deserialize, Serialize}; pub use self::Level::*; @@ -221,8 +221,8 @@ impl Level { } /// Converts an `Attribute` to a level. - pub fn from_attr(attr: &Attribute) -> Option<Self> { - Self::from_symbol(attr.name_or_empty(), Some(attr.id)) + pub fn from_attr(attr: &impl AttributeExt) -> Option<Self> { + Self::from_symbol(attr.name_or_empty(), Some(attr.id())) } /// Converts a `Symbol` to a level. @@ -931,7 +931,7 @@ macro_rules! declare_lint { desc: $desc, is_externally_loaded: false, $($v: true,)* - $(feature_gate: Some(rustc_span::symbol::sym::$gate),)? + $(feature_gate: Some(rustc_span::sym::$gate),)? $(future_incompatible: Some($crate::FutureIncompatibleInfo { reason: $reason, $($field: $val,)* @@ -976,7 +976,7 @@ macro_rules! declare_tool_lint { report_in_external_macro: $external, future_incompatible: None, is_externally_loaded: true, - $(feature_gate: Some(rustc_span::symbol::sym::$gate),)? + $(feature_gate: Some(rustc_span::sym::$gate),)? crate_level_only: false, $(eval_always: $eval_always,)? ..$crate::Lint::default_fields_for_macro() diff --git a/compiler/rustc_llvm/Cargo.toml b/compiler/rustc_llvm/Cargo.toml index b29d6b79250..79a6454dbb9 100644 --- a/compiler/rustc_llvm/Cargo.toml +++ b/compiler/rustc_llvm/Cargo.toml @@ -10,5 +10,7 @@ libc = "0.2.73" [build-dependencies] # tidy-alphabetical-start -cc = "1.1.23" +# Pinned so `cargo update` bumps don't cause breakage. Please also update the +# pinned `cc` in `rustc_codegen_ssa` if you update `cc` here. +cc = "=1.2.5" # tidy-alphabetical-end diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index b79205ff946..36441a95adb 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1796,56 +1796,6 @@ extern "C" LLVMValueRef LLVMRustBuildMaxNum(LLVMBuilderRef B, LLVMValueRef LHS, return wrap(unwrap(B)->CreateMaxNum(unwrap(LHS), unwrap(RHS))); } -// This struct contains all necessary info about a symbol exported from a DLL. -struct LLVMRustCOFFShortExport { - const char *name; - bool ordinal_present; - // The value of `ordinal` is only meaningful if `ordinal_present` is true. - uint16_t ordinal; -}; - -// Machine must be a COFF machine type, as defined in PE specs. -extern "C" LLVMRustResult -LLVMRustWriteImportLibrary(const char *ImportName, const char *Path, - const LLVMRustCOFFShortExport *Exports, - size_t NumExports, uint16_t Machine, bool MinGW) { - std::vector<llvm::object::COFFShortExport> ConvertedExports; - ConvertedExports.reserve(NumExports); - - for (size_t i = 0; i < NumExports; ++i) { - bool ordinal_present = Exports[i].ordinal_present; - uint16_t ordinal = ordinal_present ? Exports[i].ordinal : 0; - ConvertedExports.push_back(llvm::object::COFFShortExport{ - Exports[i].name, // Name - std::string{}, // ExtName - std::string{}, // SymbolName - std::string{}, // AliasTarget -#if LLVM_VERSION_GE(19, 0) - std::string{}, // ExportAs -#endif - ordinal, // Ordinal - ordinal_present, // Noname - false, // Data - false, // Private - false // Constant - }); - } - - auto Error = llvm::object::writeImportLibrary( - ImportName, Path, ConvertedExports, - static_cast<llvm::COFF::MachineTypes>(Machine), MinGW); - if (Error) { - std::string errorString; - auto stream = llvm::raw_string_ostream(errorString); - stream << Error; - stream.flush(); - LLVMRustSetLastError(errorString.c_str()); - return LLVMRustResult::Failure; - } else { - return LLVMRustResult::Success; - } -} - // Transfers ownership of DiagnosticHandler unique_ptr to the caller. extern "C" DiagnosticHandler * LLVMRustContextGetDiagnosticHandler(LLVMContextRef C) { diff --git a/compiler/rustc_macros/src/diagnostics/mod.rs b/compiler/rustc_macros/src/diagnostics/mod.rs index 0f7b8921509..91398f1a9da 100644 --- a/compiler/rustc_macros/src/diagnostics/mod.rs +++ b/compiler/rustc_macros/src/diagnostics/mod.rs @@ -16,7 +16,7 @@ use synstructure::Structure; /// # extern crate rustc_errors; /// # use rustc_errors::Applicability; /// # extern crate rustc_span; -/// # use rustc_span::{symbol::Ident, Span}; +/// # use rustc_span::{Ident, Span}; /// # extern crate rust_middle; /// # use rustc_middle::ty::Ty; /// #[derive(Diagnostic)] diff --git a/compiler/rustc_metadata/Cargo.toml b/compiler/rustc_metadata/Cargo.toml index 12519be9870..0b9fdbbd3da 100644 --- a/compiler/rustc_metadata/Cargo.toml +++ b/compiler/rustc_metadata/Cargo.toml @@ -11,7 +11,7 @@ libloading = "0.8.0" odht = { version = "0.3.1", features = ["nightly"] } rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_expand = { path = "../rustc_expand" } diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 29dba2bca61..c8715f94d5d 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -17,6 +17,7 @@ use rustc_data_structures::sync::{self, FreezeReadGuard, FreezeWriteGuard}; use rustc_errors::DiagCtxtHandle; use rustc_expand::base::SyntaxExtension; use rustc_fs_util::try_canonicalize; +use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE, LocalDefId, StableCrateId}; use rustc_hir::definitions::Definitions; use rustc_index::IndexVec; @@ -28,8 +29,7 @@ use rustc_session::lint::{self, BuiltinLintDiag}; use rustc_session::output::validate_crate_name; use rustc_session::search_paths::PathKind; use rustc_span::edition::Edition; -use rustc_span::symbol::{Ident, Symbol, sym}; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym}; use rustc_target::spec::{PanicStrategy, Target, TargetTuple}; use tracing::{debug, info, trace}; @@ -97,7 +97,13 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { } pub enum LoadedMacro { - MacroDef { def: MacroDef, ident: Ident, attrs: AttrVec, span: Span, edition: Edition }, + MacroDef { + def: MacroDef, + ident: Ident, + attrs: Vec<hir::Attribute>, + span: Span, + edition: Edition, + }, ProcMacro(SyntaxExtension), } diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs index e8de0acb7c9..582c2215d92 100644 --- a/compiler/rustc_metadata/src/dependency_format.rs +++ b/compiler/rustc_metadata/src/dependency_format.rs @@ -52,7 +52,8 @@ //! than finding a number of solutions (there are normally quite a few). use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_hir::def_id::CrateNum; +use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; +use rustc_index::IndexVec; use rustc_middle::bug; use rustc_middle::middle::dependency_format::{Dependencies, DependencyList, Linkage}; use rustc_middle::ty::TyCtxt; @@ -84,7 +85,7 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { let sess = &tcx.sess; if !sess.opts.output_types.should_codegen() { - return Vec::new(); + return IndexVec::new(); } let preferred_linkage = match ty { @@ -131,7 +132,7 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { match preferred_linkage { // If the crate is not linked, there are no link-time dependencies. - Linkage::NotLinked => return Vec::new(), + Linkage::NotLinked => return IndexVec::new(), Linkage::Static => { // Attempt static linkage first. For dylibs and executables, we may be // able to retry below with dynamic linkage. @@ -156,7 +157,7 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { } sess.dcx().emit_err(RlibRequired { crate_name: tcx.crate_name(cnum) }); } - return Vec::new(); + return IndexVec::new(); } } Linkage::Dynamic | Linkage::IncludedFromDylib => {} @@ -210,19 +211,32 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { // Collect what we've got so far in the return vector. let last_crate = tcx.crates(()).len(); - let mut ret = (1..last_crate + 1) - .map(|cnum| match formats.get(&CrateNum::new(cnum)) { - Some(&RequireDynamic) => Linkage::Dynamic, - Some(&RequireStatic) => Linkage::IncludedFromDylib, - None => Linkage::NotLinked, - }) - .collect::<Vec<_>>(); + let mut ret = IndexVec::new(); + + // We need to fill in something for LOCAL_CRATE as IndexVec is a dense map. + // Linkage::Static semantically the most correct thing to use as the local + // crate is always statically linked into the linker output, even when + // linking a dylib. Using Linkage::Static also allow avoiding special cases + // for LOCAL_CRATE in some places. + assert_eq!(ret.push(Linkage::Static), LOCAL_CRATE); + + for cnum in 1..last_crate + 1 { + let cnum = CrateNum::new(cnum); + assert_eq!( + ret.push(match formats.get(&cnum) { + Some(&RequireDynamic) => Linkage::Dynamic, + Some(&RequireStatic) => Linkage::IncludedFromDylib, + None => Linkage::NotLinked, + }), + cnum + ); + } // Run through the dependency list again, and add any missing libraries as // static libraries. // // If the crate hasn't been included yet and it's not actually required - // (e.g., it's an allocator) then we skip it here as well. + // (e.g., it's a panic runtime) then we skip it here as well. for &cnum in tcx.crates(()).iter() { let src = tcx.used_crate_source(cnum); if src.dylib.is_none() @@ -232,7 +246,7 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { assert!(src.rlib.is_some() || src.rmeta.is_some()); info!("adding staticlib: {}", tcx.crate_name(cnum)); add_library(tcx, cnum, RequireStatic, &mut formats, &mut unavailable_as_static); - ret[cnum.as_usize() - 1] = Linkage::Static; + ret[cnum] = Linkage::Static; } } @@ -240,8 +254,7 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { // artifact which means that we may need to inject dependencies of some // form. // - // Things like allocators and panic runtimes may not have been activated - // quite yet, so do so here. + // Things like panic runtimes may not have been activated quite yet, so do so here. activate_injected_dep(CStore::from_tcx(tcx).injected_panic_runtime(), &mut ret, &|cnum| { tcx.is_panic_runtime(cnum) }); @@ -252,8 +265,10 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { // // For situations like this, we perform one last pass over the dependencies, // making sure that everything is available in the requested format. - for (cnum, kind) in ret.iter().enumerate() { - let cnum = CrateNum::new(cnum + 1); + for (cnum, kind) in ret.iter_enumerated() { + if cnum == LOCAL_CRATE { + continue; + } let src = tcx.used_crate_source(cnum); match *kind { Linkage::NotLinked | Linkage::IncludedFromDylib => {} @@ -334,18 +349,21 @@ fn attempt_static(tcx: TyCtxt<'_>, unavailable: &mut Vec<CrateNum>) -> Option<De // All crates are available in an rlib format, so we're just going to link // everything in explicitly so long as it's actually required. - let mut ret = tcx - .crates(()) - .iter() - .map(|&cnum| match tcx.dep_kind(cnum) { - CrateDepKind::Explicit => Linkage::Static, - CrateDepKind::MacrosOnly | CrateDepKind::Implicit => Linkage::NotLinked, - }) - .collect::<Vec<_>>(); + let mut ret = IndexVec::new(); + assert_eq!(ret.push(Linkage::Static), LOCAL_CRATE); + for &cnum in tcx.crates(()) { + assert_eq!( + ret.push(match tcx.dep_kind(cnum) { + CrateDepKind::Explicit => Linkage::Static, + CrateDepKind::MacrosOnly | CrateDepKind::Implicit => Linkage::NotLinked, + }), + cnum + ); + } - // Our allocator/panic runtime may not have been linked above if it wasn't - // explicitly linked, which is the case for any injected dependency. Handle - // that here and activate them. + // Our panic runtime may not have been linked above if it wasn't explicitly + // linked, which is the case for any injected dependency. Handle that here + // and activate it. activate_injected_dep(CStore::from_tcx(tcx).injected_panic_runtime(), &mut ret, &|cnum| { tcx.is_panic_runtime(cnum) }); @@ -367,8 +385,7 @@ fn activate_injected_dep( list: &mut DependencyList, replaces_injected: &dyn Fn(CrateNum) -> bool, ) { - for (i, slot) in list.iter().enumerate() { - let cnum = CrateNum::new(i + 1); + for (cnum, slot) in list.iter_enumerated() { if !replaces_injected(cnum) { continue; } @@ -377,25 +394,23 @@ fn activate_injected_dep( } } if let Some(injected) = injected { - let idx = injected.as_usize() - 1; - assert_eq!(list[idx], Linkage::NotLinked); - list[idx] = Linkage::Static; + assert_eq!(list[injected], Linkage::NotLinked); + list[injected] = Linkage::Static; } } -// After the linkage for a crate has been determined we need to verify that -// there's only going to be one allocator in the output. -fn verify_ok(tcx: TyCtxt<'_>, list: &[Linkage]) { +/// After the linkage for a crate has been determined we need to verify that +/// there's only going to be one panic runtime in the output. +fn verify_ok(tcx: TyCtxt<'_>, list: &DependencyList) { let sess = &tcx.sess; if list.is_empty() { return; } let mut panic_runtime = None; - for (i, linkage) in list.iter().enumerate() { + for (cnum, linkage) in list.iter_enumerated() { if let Linkage::NotLinked = *linkage { continue; } - let cnum = CrateNum::new(i + 1); if tcx.is_panic_runtime(cnum) { if let Some((prev, _)) = panic_runtime { @@ -431,11 +446,10 @@ fn verify_ok(tcx: TyCtxt<'_>, list: &[Linkage]) { // strategy. If the dep isn't linked, we ignore it, and if our strategy // is abort then it's compatible with everything. Otherwise all crates' // panic strategy must match our own. - for (i, linkage) in list.iter().enumerate() { + for (cnum, linkage) in list.iter_enumerated() { if let Linkage::NotLinked = *linkage { continue; } - let cnum = CrateNum::new(i + 1); if cnum == runtime_cnum || tcx.is_compiler_builtins(cnum) { continue; } @@ -450,13 +464,16 @@ fn verify_ok(tcx: TyCtxt<'_>, list: &[Linkage]) { }); } - let found_drop_strategy = tcx.panic_in_drop_strategy(cnum); - if tcx.sess.opts.unstable_opts.panic_in_drop != found_drop_strategy { - sess.dcx().emit_err(IncompatiblePanicInDropStrategy { - crate_name: tcx.crate_name(cnum), - found_strategy: found_drop_strategy, - desired_strategy: tcx.sess.opts.unstable_opts.panic_in_drop, - }); + // panic_in_drop_strategy isn't allowed for LOCAL_CRATE + if cnum != LOCAL_CRATE { + let found_drop_strategy = tcx.panic_in_drop_strategy(cnum); + if tcx.sess.opts.unstable_opts.panic_in_drop != found_drop_strategy { + sess.dcx().emit_err(IncompatiblePanicInDropStrategy { + crate_name: tcx.crate_name(cnum), + found_strategy: found_drop_strategy, + desired_strategy: tcx.sess.opts.unstable_opts.panic_in_drop, + }); + } } } } diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index d59ec7af6ec..b9ebf17af24 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -229,8 +229,7 @@ use rustc_session::cstore::CrateSource; use rustc_session::filesearch::FileSearch; use rustc_session::search_paths::PathKind; use rustc_session::utils::CanonicalizedPath; -use rustc_span::Span; -use rustc_span::symbol::Symbol; +use rustc_span::{Span, Symbol}; use rustc_target::spec::{Target, TargetTuple}; use tracing::{debug, info}; diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 8bd2281981b..2a1e4b261e7 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -3,7 +3,7 @@ use std::path::{Path, PathBuf}; use rustc_abi::ExternAbi; use rustc_ast::CRATE_NODE_ID; -use rustc_attr as attr; +use rustc_attr_parsing as attr; use rustc_data_structures::fx::FxHashSet; use rustc_middle::query::LocalCrate; use rustc_middle::ty::{self, List, Ty, TyCtxt}; @@ -16,7 +16,7 @@ use rustc_session::parse::feature_err; use rustc_session::search_paths::PathKind; use rustc_session::utils::NativeLibKind; use rustc_span::def_id::{DefId, LOCAL_CRATE}; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::{Symbol, sym}; use rustc_target::spec::LinkSelfContainedComponents; use crate::{errors, fluent_generated}; @@ -544,7 +544,7 @@ impl<'tcx> Collector<'tcx> { // can move them to the end of the list below. let mut existing = self .libs - .extract_if(|lib| { + .extract_if(.., |lib| { if lib.name.as_str() == passed_lib.name { // FIXME: This whole logic is questionable, whether modifiers are // involved or not, library reordering and kind overriding without diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index b9586338655..c2b5e318bda 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -31,8 +31,7 @@ use rustc_serialize::{Decodable, Decoder}; use rustc_session::Session; use rustc_session::cstore::{CrateSource, ExternCrate}; use rustc_span::hygiene::HygieneDecodeContext; -use rustc_span::symbol::kw; -use rustc_span::{BytePos, DUMMY_SP, Pos, SpanData, SpanDecoder, SyntaxContext}; +use rustc_span::{BytePos, DUMMY_SP, Pos, SpanData, SpanDecoder, SyntaxContext, kw}; use tracing::debug; use crate::creader::CStore; @@ -874,7 +873,7 @@ impl MetadataBlob { let def_key = root.tables.def_keys.get(blob, item).unwrap().decode(blob); #[cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] let def_name = if item == CRATE_DEF_INDEX { - rustc_span::symbol::kw::Crate + kw::Crate } else { def_key .disambiguated_data @@ -1369,7 +1368,7 @@ impl<'a> CrateMetadataRef<'a> { self, id: DefIndex, sess: &'a Session, - ) -> impl Iterator<Item = ast::Attribute> + 'a { + ) -> impl Iterator<Item = hir::Attribute> + 'a { self.root .tables .attributes @@ -1475,7 +1474,7 @@ impl<'a> CrateMetadataRef<'a> { ) -> &'tcx [(CrateNum, LinkagePreference)] { tcx.arena.alloc_from_iter( self.root.dylib_dependency_formats.decode(self).enumerate().flat_map(|(i, link)| { - let cnum = CrateNum::new(i + 1); + let cnum = CrateNum::new(i + 1); // We skipped LOCAL_CRATE when encoding link.map(|link| (self.cnum_map[cnum], link)) }), ) diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index f01ad31d0bb..527f2f10205 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -1,7 +1,7 @@ use std::any::Any; use std::mem; -use rustc_attr::Deprecation; +use rustc_attr_parsing::Deprecation; use rustc_data_structures::sync::Lrc; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE}; @@ -17,9 +17,8 @@ use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::util::Providers; use rustc_session::cstore::{CrateStore, ExternCrate}; use rustc_session::{Session, StableCrateId}; -use rustc_span::Span; use rustc_span::hygiene::ExpnId; -use rustc_span::symbol::{Symbol, kw}; +use rustc_span::{Span, Symbol, kw}; use super::{Decodable, DecodeContext, DecodeIterator}; use crate::creader::{CStore, LoadedMacro}; @@ -344,7 +343,7 @@ provide! { tcx, def_id, other, cdata, } associated_item => { cdata.get_associated_item(def_id.index, tcx.sess) } inherent_impls => { cdata.get_inherent_implementations_for_type(tcx, def_id.index) } - item_attrs => { tcx.arena.alloc_from_iter(cdata.get_item_attrs(def_id.index, tcx.sess)) } + attrs_for_def => { tcx.arena.alloc_from_iter(cdata.get_item_attrs(def_id.index, tcx.sess)) } is_mir_available => { cdata.is_item_mir_available(def_id.index) } is_ctfe_mir_available => { cdata.is_ctfe_mir_available(def_id.index) } cross_crate_inlinable => { table_direct } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index df5b06c6d16..c538ab99fb5 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -4,7 +4,7 @@ use std::fs::File; use std::io::{Read, Seek, Write}; use std::path::{Path, PathBuf}; -use rustc_ast::Attribute; +use rustc_ast::attr::AttributeExt; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::memmap::{Mmap, MmapMut}; use rustc_data_structures::sync::{Lrc, join, par_for_each_in}; @@ -27,9 +27,9 @@ use rustc_middle::{bug, span_bug}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder, opaque}; use rustc_session::config::{CrateType, OptLevel}; use rustc_span::hygiene::HygieneEncodeContext; -use rustc_span::symbol::sym; use rustc_span::{ ExternalSource, FileName, SourceFile, SpanData, SpanEncoder, StableSourceFileId, SyntaxContext, + sym, }; use tracing::{debug, instrument, trace}; @@ -710,15 +710,18 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { has_global_allocator: tcx.has_global_allocator(LOCAL_CRATE), has_alloc_error_handler: tcx.has_alloc_error_handler(LOCAL_CRATE), has_panic_handler: tcx.has_panic_handler(LOCAL_CRATE), - has_default_lib_allocator: attr::contains_name(attrs, sym::default_lib_allocator), + has_default_lib_allocator: ast::attr::contains_name( + attrs, + sym::default_lib_allocator, + ), proc_macro_data, debugger_visualizers, - compiler_builtins: attr::contains_name(attrs, sym::compiler_builtins), - needs_allocator: attr::contains_name(attrs, sym::needs_allocator), - needs_panic_runtime: attr::contains_name(attrs, sym::needs_panic_runtime), - no_builtins: attr::contains_name(attrs, sym::no_builtins), - panic_runtime: attr::contains_name(attrs, sym::panic_runtime), - profiler_runtime: attr::contains_name(attrs, sym::profiler_runtime), + compiler_builtins: ast::attr::contains_name(attrs, sym::compiler_builtins), + needs_allocator: ast::attr::contains_name(attrs, sym::needs_allocator), + needs_panic_runtime: ast::attr::contains_name(attrs, sym::needs_panic_runtime), + no_builtins: ast::attr::contains_name(attrs, sym::no_builtins), + panic_runtime: ast::attr::contains_name(attrs, sym::panic_runtime), + profiler_runtime: ast::attr::contains_name(attrs, sym::profiler_runtime), symbol_mangling_version: tcx.sess.opts.get_symbol_mangling_version(), crate_deps, @@ -814,7 +817,7 @@ struct AnalyzeAttrState<'a> { /// visibility: this is a piece of data that can be computed once per defid, and not once per /// attribute. Some attributes would only be usable downstream if they are public. #[inline] -fn analyze_attr(attr: &Attribute, state: &mut AnalyzeAttrState<'_>) -> bool { +fn analyze_attr(attr: &impl AttributeExt, state: &mut AnalyzeAttrState<'_>) -> bool { let mut should_encode = false; if !rustc_feature::encode_cross_crate(attr.name_or_empty()) { // Attributes not marked encode-cross-crate don't need to be encoded for downstream crates. @@ -1354,7 +1357,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { .hir() .attrs(tcx.local_def_id_to_hir_id(def_id)) .iter() - .filter(|attr| analyze_attr(attr, &mut state)); + .filter(|attr| analyze_attr(*attr, &mut state)); record_array!(self.tables.attributes[def_id.to_def_id()] <- attr_iter); @@ -1917,11 +1920,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // Proc-macros may have attributes like `#[allow_internal_unstable]`, // so downstream crates need access to them. let attrs = hir.attrs(proc_macro); - let macro_kind = if attr::contains_name(attrs, sym::proc_macro) { + let macro_kind = if ast::attr::contains_name(attrs, sym::proc_macro) { MacroKind::Bang - } else if attr::contains_name(attrs, sym::proc_macro_attribute) { + } else if ast::attr::contains_name(attrs, sym::proc_macro_attribute) { MacroKind::Attr - } else if let Some(attr) = attr::find_by_name(attrs, sym::proc_macro_derive) { + } else if let Some(attr) = ast::attr::find_by_name(attrs, sym::proc_macro_derive) { // This unwrap chain should have been checked by the proc-macro harness. name = attr.meta_item_list().unwrap()[0] .meta_item() @@ -2162,12 +2165,14 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { empty_proc_macro!(self); let formats = self.tcx.dependency_formats(()); if let Some(arr) = formats.get(&CrateType::Dylib) { - return self.lazy_array(arr.iter().map(|slot| match *slot { - Linkage::NotLinked | Linkage::IncludedFromDylib => None, + return self.lazy_array(arr.iter().skip(1 /* skip LOCAL_CRATE */).map( + |slot| match *slot { + Linkage::NotLinked | Linkage::IncludedFromDylib => None, - Linkage::Dynamic => Some(LinkagePreference::RequireDynamic), - Linkage::Static => Some(LinkagePreference::RequireStatic), - })); + Linkage::Dynamic => Some(LinkagePreference::RequireDynamic), + Linkage::Static => Some(LinkagePreference::RequireStatic), + }, + )); } LazyArray::default() } diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 4961464833a..64a1c70dd2f 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -36,11 +36,10 @@ use rustc_session::config::SymbolManglingVersion; use rustc_session::cstore::{CrateDepKind, ForeignModule, LinkagePreference, NativeLib}; use rustc_span::edition::Edition; use rustc_span::hygiene::{ExpnIndex, MacroKind, SyntaxContextData}; -use rustc_span::symbol::{Ident, Symbol}; -use rustc_span::{self, ExpnData, ExpnHash, ExpnId, Span}; +use rustc_span::{self, ExpnData, ExpnHash, ExpnId, Ident, Span, Symbol}; use rustc_target::spec::{PanicStrategy, TargetTuple}; use table::TableBuilder; -use {rustc_ast as ast, rustc_attr as attr, rustc_hir as hir}; +use {rustc_ast as ast, rustc_attr_parsing as attr, rustc_hir as hir}; use crate::creader::CrateMetadataRef; @@ -403,7 +402,7 @@ define_tables! { cross_crate_inlinable: Table<DefIndex, bool>, - optional: - attributes: Table<DefIndex, LazyArray<ast::Attribute>>, + attributes: Table<DefIndex, LazyArray<hir::Attribute>>, // For non-reexported names in a module every name is associated with a separate `DefId`, // so we can take their names, visibilities etc from other encoded tables. module_children_non_reexports: Table<DefIndex, LazyArray<DefIndex>>, diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index 3bda3a4aa63..e64500f812a 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -17,7 +17,7 @@ rustc_apfloat = "0.2.0" rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } rustc_ast_ir = { path = "../rustc_ast_ir" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_error_messages = { path = "../rustc_error_messages" } # Used for intra-doc links rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index b664230d10b..750531b638e 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -85,9 +85,9 @@ macro_rules! arena_types { [] upvars_mentioned: rustc_data_structures::fx::FxIndexMap<rustc_hir::HirId, rustc_hir::Upvar>, [] dyn_compatibility_violations: rustc_middle::traits::DynCompatibilityViolation, [] codegen_unit: rustc_middle::mir::mono::CodegenUnit<'tcx>, - [decode] attribute: rustc_ast::Attribute, - [] name_set: rustc_data_structures::unord::UnordSet<rustc_span::symbol::Symbol>, - [] ordered_name_set: rustc_data_structures::fx::FxIndexSet<rustc_span::symbol::Symbol>, + [decode] attribute: rustc_hir::Attribute, + [] name_set: rustc_data_structures::unord::UnordSet<rustc_span::Symbol>, + [] ordered_name_set: rustc_data_structures::fx::FxIndexSet<rustc_span::Symbol>, [] pats: rustc_middle::ty::PatternKind<'tcx>, // Note that this deliberately duplicates items in the `rustc_hir::arena`, diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index 87bbeb178ee..fcfc31575f8 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -64,7 +64,7 @@ pub use rustc_query_system::dep_graph::DepNode; use rustc_query_system::dep_graph::FingerprintStyle; pub use rustc_query_system::dep_graph::dep_node::DepKind; pub(crate) use rustc_query_system::dep_graph::{DepContext, DepNodeParams}; -use rustc_span::symbol::Symbol; +use rustc_span::Symbol; use crate::mir::mono::MonoItem; use crate::ty::TyCtxt; diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 0c701c834f2..308078ddf87 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -9,11 +9,10 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId, LocalModDefId}; use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; use rustc_hir::intravisit::Visitor; use rustc_hir::*; +use rustc_hir_pretty as pprust_hir; use rustc_middle::hir::nested_filter; use rustc_span::def_id::StableCrateId; -use rustc_span::symbol::{Ident, Symbol, kw, sym}; -use rustc_span::{ErrorGuaranteed, Span}; -use {rustc_ast as ast, rustc_hir_pretty as pprust_hir}; +use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym}; use crate::hir::ModuleItems; use crate::middle::debugger_visualizer::DebuggerVisualizerFile; @@ -381,7 +380,7 @@ impl<'hir> Map<'hir> { /// Gets the attributes on the crate. This is preferable to /// invoking `krate.attrs` because it registers a tighter /// dep-graph access. - pub fn krate_attrs(self) -> &'hir [ast::Attribute] { + pub fn krate_attrs(self) -> &'hir [Attribute] { self.attrs(CRATE_HIR_ID) } @@ -792,7 +791,7 @@ impl<'hir> Map<'hir> { /// Given a node ID, gets a list of attributes associated with the AST /// corresponding to the node-ID. - pub fn attrs(self, id: HirId) -> &'hir [ast::Attribute] { + pub fn attrs(self, id: HirId) -> &'hir [Attribute] { self.tcx.hir_attrs(id.owner).get(id.local_id) } diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index ad0d70152e1..ffefd81cd08 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -151,7 +151,7 @@ impl<'tcx> TyCtxt<'tcx> { self, node: OwnerNode<'_>, bodies: &SortedMap<ItemLocalId, &Body<'_>>, - attrs: &SortedMap<ItemLocalId, &[rustc_ast::Attribute]>, + attrs: &SortedMap<ItemLocalId, &[Attribute]>, ) -> (Option<Fingerprint>, Option<Fingerprint>) { if self.needs_crate_hash() { self.with_stable_hashing_context(|mut hcx| { diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index ac55497f8b3..0f408375e05 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -30,7 +30,6 @@ pub use rustc_type_ir as ir; pub use rustc_type_ir::{CanonicalTyVarKind, CanonicalVarKind}; use smallvec::SmallVec; -use crate::infer::MemberConstraint; use crate::mir::ConstraintCategory; use crate::ty::{self, GenericArg, List, Ty, TyCtxt, TypeFlags, TypeVisitableExt}; @@ -91,14 +90,13 @@ pub struct QueryResponse<'tcx, R> { #[derive(HashStable, TypeFoldable, TypeVisitable)] pub struct QueryRegionConstraints<'tcx> { pub outlives: Vec<QueryOutlivesConstraint<'tcx>>, - pub member_constraints: Vec<MemberConstraint<'tcx>>, } impl QueryRegionConstraints<'_> { /// Represents an empty (trivially true) set of region /// constraints. pub fn is_empty(&self) -> bool { - self.outlives.is_empty() && self.member_constraints.is_empty() + self.outlives.is_empty() } } diff --git a/compiler/rustc_middle/src/infer/mod.rs b/compiler/rustc_middle/src/infer/mod.rs index 19fe9e5a54f..3dfcf90cb93 100644 --- a/compiler/rustc_middle/src/infer/mod.rs +++ b/compiler/rustc_middle/src/infer/mod.rs @@ -1,34 +1,2 @@ pub mod canonical; pub mod unify_key; - -use rustc_data_structures::sync::Lrc; -use rustc_macros::{HashStable, TypeFoldable, TypeVisitable}; -use rustc_span::Span; - -use crate::ty::{OpaqueTypeKey, Region, Ty}; - -/// Requires that `region` must be equal to one of the regions in `choice_regions`. -/// We often denote this using the syntax: -/// -/// ```text -/// R0 member of [O1..On] -/// ``` -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -#[derive(HashStable, TypeFoldable, TypeVisitable)] -pub struct MemberConstraint<'tcx> { - /// The `DefId` and args of the opaque type causing this constraint. - /// Used for error reporting. - pub key: OpaqueTypeKey<'tcx>, - - /// The span where the hidden type was instantiated. - pub definition_span: Span, - - /// The hidden type in which `member_region` appears: used for error reporting. - pub hidden_ty: Ty<'tcx>, - - /// The region `R0`. - pub member_region: Region<'tcx>, - - /// The options `O1..On`. - pub choice_regions: Lrc<Vec<Region<'tcx>>>, -} diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 971d036fa69..620d9f1c357 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -9,7 +9,7 @@ use rustc_session::Session; use rustc_session::lint::builtin::{self, FORBIDDEN_LINT_GROUPS}; use rustc_session::lint::{FutureIncompatibilityReason, Level, Lint, LintExpectationId, LintId}; use rustc_span::hygiene::{ExpnKind, MacroKind}; -use rustc_span::{DUMMY_SP, DesugaringKind, Span, Symbol, symbol}; +use rustc_span::{DUMMY_SP, DesugaringKind, Span, Symbol, kw}; use tracing::instrument; use crate::ty::TyCtxt; @@ -37,7 +37,7 @@ pub enum LintLevelSource { impl LintLevelSource { pub fn name(&self) -> Symbol { match *self { - LintLevelSource::Default => symbol::kw::Default, + LintLevelSource::Default => kw::Default, LintLevelSource::Node { name, .. } => name, LintLevelSource::CommandLine(name, _) => name, } diff --git a/compiler/rustc_middle/src/metadata.rs b/compiler/rustc_middle/src/metadata.rs index c3175c6bdf5..57c8960943b 100644 --- a/compiler/rustc_middle/src/metadata.rs +++ b/compiler/rustc_middle/src/metadata.rs @@ -1,7 +1,7 @@ use rustc_hir::def::Res; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; +use rustc_span::Ident; use rustc_span::def_id::DefId; -use rustc_span::symbol::Ident; use smallvec::SmallVec; use crate::ty; diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 44428471a5f..241767fe249 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -1,7 +1,7 @@ use rustc_abi::Align; -use rustc_attr::{InlineAttr, InstructionSetAttr, OptimizeAttr}; +use rustc_attr_parsing::{InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; -use rustc_span::symbol::Symbol; +use rustc_span::Symbol; use rustc_target::spec::SanitizerSet; use crate::mir::mono::Linkage; diff --git a/compiler/rustc_middle/src/middle/dependency_format.rs b/compiler/rustc_middle/src/middle/dependency_format.rs index e3b40b64157..4f613e97631 100644 --- a/compiler/rustc_middle/src/middle/dependency_format.rs +++ b/compiler/rustc_middle/src/middle/dependency_format.rs @@ -8,13 +8,13 @@ // this will introduce circular dependency between rustc_metadata and rustc_middle use rustc_data_structures::fx::FxIndexMap; +use rustc_hir::def_id::CrateNum; +use rustc_index::IndexVec; use rustc_macros::{Decodable, Encodable, HashStable}; use rustc_session::config::CrateType; /// A list of dependencies for a certain crate type. -/// -/// The length of this vector is the same as the number of external crates used. -pub type DependencyList = Vec<Linkage>; +pub type DependencyList = IndexVec<CrateNum, Linkage>; /// A mapping of all required dependencies for a particular flavor of output. /// diff --git a/compiler/rustc_middle/src/middle/limits.rs b/compiler/rustc_middle/src/middle/limits.rs index 270bcabcc86..8a367a947d1 100644 --- a/compiler/rustc_middle/src/middle/limits.rs +++ b/compiler/rustc_middle/src/middle/limits.rs @@ -10,9 +10,9 @@ use std::num::IntErrorKind; -use rustc_ast::Attribute; +use rustc_ast::attr::AttributeExt; use rustc_session::{Limit, Limits, Session}; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::{Symbol, sym}; use crate::error::LimitInvalid; use crate::query::Providers; @@ -35,32 +35,36 @@ pub fn provide(providers: &mut Providers) { } } -pub fn get_recursion_limit(krate_attrs: &[Attribute], sess: &Session) -> Limit { +pub fn get_recursion_limit(krate_attrs: &[impl AttributeExt], sess: &Session) -> Limit { get_limit(krate_attrs, sess, sym::recursion_limit, 128) } -fn get_limit(krate_attrs: &[Attribute], sess: &Session, name: Symbol, default: usize) -> Limit { +fn get_limit( + krate_attrs: &[impl AttributeExt], + sess: &Session, + name: Symbol, + default: usize, +) -> Limit { match get_limit_size(krate_attrs, sess, name) { Some(size) => Limit::new(size), None => Limit::new(default), } } -pub fn get_limit_size(krate_attrs: &[Attribute], sess: &Session, name: Symbol) -> Option<usize> { +pub fn get_limit_size( + krate_attrs: &[impl AttributeExt], + sess: &Session, + name: Symbol, +) -> Option<usize> { for attr in krate_attrs { if !attr.has_name(name) { continue; } - if let Some(s) = attr.value_str() { - match s.as_str().parse() { + if let Some(sym) = attr.value_str() { + match sym.as_str().parse() { Ok(n) => return Some(n), Err(e) => { - let value_span = attr - .meta() - .and_then(|meta| meta.name_value_literal_span()) - .unwrap_or(attr.span); - let error_str = match e.kind() { IntErrorKind::PosOverflow => "`limit` is too large", IntErrorKind::Empty => "`limit` must be a non-negative integer", @@ -71,7 +75,11 @@ pub fn get_limit_size(krate_attrs: &[Attribute], sess: &Session, name: Symbol) - IntErrorKind::Zero => bug!("zero is a valid `limit`"), kind => bug!("unimplemented IntErrorKind variant: {:?}", kind), }; - sess.dcx().emit_err(LimitInvalid { span: attr.span, value_span, error_str }); + sess.dcx().emit_err(LimitInvalid { + span: attr.span(), + value_span: attr.value_span().unwrap(), + error_str, + }); } } } diff --git a/compiler/rustc_middle/src/middle/mod.rs b/compiler/rustc_middle/src/middle/mod.rs index 83873439bd9..692fe027c49 100644 --- a/compiler/rustc_middle/src/middle/mod.rs +++ b/compiler/rustc_middle/src/middle/mod.rs @@ -6,8 +6,7 @@ pub mod lang_items; pub mod lib_features { use rustc_data_structures::unord::UnordMap; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; - use rustc_span::Span; - use rustc_span::symbol::Symbol; + use rustc_span::{Span, Symbol}; #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(HashStable, TyEncodable, TyDecodable)] diff --git a/compiler/rustc_middle/src/middle/region.rs b/compiler/rustc_middle/src/middle/region.rs index 114211b27c1..66861519e17 100644 --- a/compiler/rustc_middle/src/middle/region.rs +++ b/compiler/rustc_middle/src/middle/region.rs @@ -84,23 +84,23 @@ use crate::ty::TyCtxt; #[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Copy, TyEncodable, TyDecodable)] #[derive(HashStable)] pub struct Scope { - pub id: hir::ItemLocalId, + pub local_id: hir::ItemLocalId, pub data: ScopeData, } impl fmt::Debug for Scope { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { match self.data { - ScopeData::Node => write!(fmt, "Node({:?})", self.id), - ScopeData::CallSite => write!(fmt, "CallSite({:?})", self.id), - ScopeData::Arguments => write!(fmt, "Arguments({:?})", self.id), - ScopeData::Destruction => write!(fmt, "Destruction({:?})", self.id), - ScopeData::IfThen => write!(fmt, "IfThen({:?})", self.id), - ScopeData::IfThenRescope => write!(fmt, "IfThen[edition2024]({:?})", self.id), + ScopeData::Node => write!(fmt, "Node({:?})", self.local_id), + ScopeData::CallSite => write!(fmt, "CallSite({:?})", self.local_id), + ScopeData::Arguments => write!(fmt, "Arguments({:?})", self.local_id), + ScopeData::Destruction => write!(fmt, "Destruction({:?})", self.local_id), + ScopeData::IfThen => write!(fmt, "IfThen({:?})", self.local_id), + ScopeData::IfThenRescope => write!(fmt, "IfThen[edition2024]({:?})", self.local_id), ScopeData::Remainder(fsi) => write!( fmt, "Remainder {{ block: {:?}, first_statement_index: {}}}", - self.id, + self.local_id, fsi.as_u32(), ), } @@ -164,18 +164,8 @@ rustc_index::newtype_index! { rustc_data_structures::static_assert_size!(ScopeData, 4); impl Scope { - /// Returns an item-local ID associated with this scope. - /// - /// N.B., likely to be replaced as API is refined; e.g., pnkfelix - /// anticipates `fn entry_node_id` and `fn each_exit_node_id`. - pub fn item_local_id(&self) -> hir::ItemLocalId { - self.id - } - pub fn hir_id(&self, scope_tree: &ScopeTree) -> Option<HirId> { - scope_tree - .root_body - .map(|hir_id| HirId { owner: hir_id.owner, local_id: self.item_local_id() }) + scope_tree.root_body.map(|hir_id| HirId { owner: hir_id.owner, local_id: self.local_id }) } /// Returns the span of this `Scope`. Note that in general the @@ -350,7 +340,7 @@ impl ScopeTree { pub fn record_var_scope(&mut self, var: hir::ItemLocalId, lifetime: Scope) { debug!("record_var_scope(sub={:?}, sup={:?})", var, lifetime); - assert!(var != lifetime.item_local_id()); + assert!(var != lifetime.local_id); self.var_map.insert(var, lifetime); } @@ -359,7 +349,7 @@ impl ScopeTree { match &candidate_type { RvalueCandidateType::Borrow { lifetime: Some(lifetime), .. } | RvalueCandidateType::Pattern { lifetime: Some(lifetime), .. } => { - assert!(var.local_id != lifetime.item_local_id()) + assert!(var.local_id != lifetime.local_id) } _ => {} } diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 94d13021612..84c3c2eb49e 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -4,7 +4,7 @@ use std::num::NonZero; use rustc_ast::NodeId; -use rustc_attr::{ +use rustc_attr_parsing::{ self as attr, ConstStability, DefaultBodyStability, DeprecatedSince, Deprecation, Stability, }; use rustc_data_structures::unord::UnordMap; @@ -18,8 +18,7 @@ use rustc_session::Session; use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE}; use rustc_session::lint::{BuiltinLintDiag, DeprecatedSinceKind, Level, Lint, LintBuffer}; use rustc_session::parse::feature_err_issue; -use rustc_span::Span; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::{Span, Symbol, sym}; use tracing::debug; pub use self::StabilityLevel::*; @@ -392,7 +391,7 @@ impl<'tcx> TyCtxt<'tcx> { match stability { Some(Stability { - level: attr::Unstable { reason, issue, is_soft, implied_by }, + level: attr::StabilityLevel::Unstable { reason, issue, is_soft, implied_by }, feature, .. }) => { @@ -475,7 +474,7 @@ impl<'tcx> TyCtxt<'tcx> { match stability { Some(DefaultBodyStability { - level: attr::Unstable { reason, issue, is_soft, .. }, + level: attr::StabilityLevel::Unstable { reason, issue, is_soft, .. }, feature, }) => { if span.allows_unstable(feature) { diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index d8a39191920..1231ea88569 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -5,7 +5,7 @@ use rustc_hir::def_id::DefId; use rustc_macros::{HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_session::RemapFileNameExt; use rustc_session::config::RemapPathScopeComponents; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Span, Symbol}; use rustc_type_ir::visit::TypeVisitableExt; use super::interpret::ReportedErrorInfo; @@ -569,7 +569,7 @@ impl<'tcx> TyCtxt<'tcx> { let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span); let caller = self.sess.source_map().lookup_char_pos(topmost.lo()); self.const_caller_location( - rustc_span::symbol::Symbol::intern( + Symbol::intern( &caller .file .name diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index 962176290df..29f26180c97 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -154,22 +154,6 @@ impl Debug for CoverageKind { } } -#[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq, Eq, PartialOrd, Ord)] -#[derive(TypeFoldable, TypeVisitable)] -pub struct SourceRegion { - pub start_line: u32, - pub start_col: u32, - pub end_line: u32, - pub end_col: u32, -} - -impl Debug for SourceRegion { - fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { - let &Self { start_line, start_col, end_line, end_col } = self; - write!(fmt, "{start_line}:{start_col} - {end_line}:{end_col}") - } -} - #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)] #[derive(TyEncodable, TyDecodable, TypeFoldable, TypeVisitable)] pub enum Op { @@ -231,7 +215,7 @@ impl MappingKind { #[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] pub struct Mapping { pub kind: MappingKind, - pub source_region: SourceRegion, + pub span: Span, } /// Stores per-function coverage information attached to a `mir::Body`, diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index fbada6ec405..37328470aa7 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -392,7 +392,7 @@ pub enum UndefinedBehaviorInfo<'tcx> { /// A discriminant of an uninhabited enum variant is written. UninhabitedEnumVariantWritten(VariantIdx), /// An uninhabited enum variant is projected. - UninhabitedEnumVariantRead(VariantIdx), + UninhabitedEnumVariantRead(Option<VariantIdx>), /// Trying to set discriminant to the niched variant, but the value does not match. InvalidNichedEnumVariantWritten { enum_ty: Ty<'tcx> }, /// ABI-incompatible argument types. diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 56340ff0095..98ef7d58a50 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -26,8 +26,7 @@ use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_serialize::{Decodable, Encodable}; use rustc_span::source_map::Spanned; -use rustc_span::symbol::Symbol; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Span, Symbol}; use tracing::trace; pub use self::query::*; @@ -1349,8 +1348,8 @@ pub struct BasicBlockData<'tcx> { } impl<'tcx> BasicBlockData<'tcx> { - pub fn new(terminator: Option<Terminator<'tcx>>) -> BasicBlockData<'tcx> { - BasicBlockData { statements: vec![], terminator, is_cleanup: false } + pub fn new(terminator: Option<Terminator<'tcx>>, is_cleanup: bool) -> BasicBlockData<'tcx> { + BasicBlockData { statements: vec![], terminator, is_cleanup } } /// Accessor for terminator. diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 266dc7ad2b3..27168b2a9f2 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -1,7 +1,7 @@ use std::fmt; use std::hash::Hash; -use rustc_attr::InlineAttr; +use rustc_attr_parsing::InlineAttr; use rustc_data_structures::base_n::{BaseNString, CASE_INSENSITIVE, ToBaseN}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxIndexMap; @@ -13,8 +13,7 @@ use rustc_index::Idx; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_query_system::ich::StableHashingContext; use rustc_session::config::OptLevel; -use rustc_span::Span; -use rustc_span::symbol::Symbol; +use rustc_span::{Span, Symbol}; use rustc_target::spec::SymbolVisibility; use tracing::debug; diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 2bfcd0a6227..2d1a0e36f90 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -23,6 +23,7 @@ pub(crate) const ALIGN: usize = 40; /// An indication of where we are in the control flow graph. Used for printing /// extra information in `dump_mir` +#[derive(Clone)] pub enum PassWhere { /// We have not started dumping the control flow graph, but we are about to. BeforeCFG, @@ -603,8 +604,8 @@ fn write_function_coverage_info( for (id, expression) in expressions.iter_enumerated() { writeln!(w, "{INDENT}coverage {id:?} => {expression:?};")?; } - for coverage::Mapping { kind, source_region } in mappings { - writeln!(w, "{INDENT}coverage {kind:?} => {source_region:?};")?; + for coverage::Mapping { kind, span } in mappings { + writeln!(w, "{INDENT}coverage {kind:?} => {span:?};")?; } writeln!(w)?; diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index f690359e012..429be9bc725 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -11,8 +11,7 @@ use rustc_hir::def_id::LocalDefId; use rustc_index::bit_set::BitMatrix; use rustc_index::{Idx, IndexVec}; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; -use rustc_span::Span; -use rustc_span::symbol::Symbol; +use rustc_span::{Span, Symbol}; use smallvec::SmallVec; use super::{ConstValue, SourceInfo}; diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index fea940ea47c..0c17a2e0fe5 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -10,10 +10,9 @@ use rustc_hir::CoroutineKind; use rustc_hir::def_id::DefId; use rustc_index::IndexVec; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; -use rustc_span::Span; use rustc_span::def_id::LocalDefId; use rustc_span::source_map::Spanned; -use rustc_span::symbol::Symbol; +use rustc_span::{Span, Symbol}; use rustc_target::asm::InlineAsmRegOrRegClass; use smallvec::SmallVec; @@ -822,6 +821,11 @@ pub enum TerminatorKind<'tcx> { /// continues at the `resume` basic block, with the second argument written to the `resume_arg` /// place. If the coroutine is dropped before then, the `drop` basic block is invoked. /// + /// Note that coroutines can be (unstably) cloned under certain conditions, which means that + /// this terminator can **return multiple times**! MIR optimizations that reorder code into + /// different basic blocks needs to be aware of that. + /// See <https://github.com/rust-lang/rust/issues/95360>. + /// /// Not permitted in bodies that are not coroutine bodies, or after coroutine lowering. /// /// **Needs clarification**: What about the evaluation order of the `resume_arg` and `value`? diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 013847f0b2d..b72c0e776fe 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -239,9 +239,9 @@ trivial! { bool, Option<(rustc_span::def_id::DefId, rustc_session::config::EntryFnType)>, Option<rustc_ast::expand::allocator::AllocatorKind>, - Option<rustc_attr::ConstStability>, - Option<rustc_attr::DefaultBodyStability>, - Option<rustc_attr::Stability>, + Option<rustc_attr_parsing::ConstStability>, + Option<rustc_attr_parsing::DefaultBodyStability>, + Option<rustc_attr_parsing::Stability>, Option<rustc_data_structures::svh::Svh>, Option<rustc_hir::def::DefKind>, Option<rustc_hir::CoroutineKind>, @@ -264,10 +264,10 @@ trivial! { Result<rustc_middle::traits::EvaluationResult, rustc_middle::traits::OverflowError>, rustc_abi::ReprOptions, rustc_ast::expand::allocator::AllocatorKind, - rustc_attr::ConstStability, - rustc_attr::DefaultBodyStability, - rustc_attr::Deprecation, - rustc_attr::Stability, + rustc_attr_parsing::ConstStability, + rustc_attr_parsing::DefaultBodyStability, + rustc_attr_parsing::Deprecation, + rustc_attr_parsing::Stability, rustc_data_structures::svh::Svh, rustc_errors::ErrorGuaranteed, rustc_hir::Constness, @@ -332,7 +332,7 @@ trivial! { rustc_span::ExpnId, rustc_span::Span, rustc_span::Symbol, - rustc_span::symbol::Ident, + rustc_span::Ident, rustc_target::spec::PanicStrategy, rustc_type_ir::Variance, u32, diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index 66fec2dd0f7..e243425c0b7 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -4,8 +4,7 @@ use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId, use rustc_hir::hir_id::{HirId, OwnerId}; use rustc_query_system::dep_graph::DepNodeIndex; use rustc_query_system::query::{DefIdCache, DefaultCache, SingleCache, VecCache}; -use rustc_span::symbol::{Ident, Symbol}; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Ident, Span, Symbol}; use crate::infer::canonical::CanonicalQueryInput; use crate::mir::mono::CollectionMode; diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index cc4e31294bd..2c2dffe8b88 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -41,10 +41,9 @@ use rustc_session::cstore::{ use rustc_session::lint::LintExpectationId; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::source_map::Spanned; -use rustc_span::symbol::Symbol; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Span, Symbol}; use rustc_target::spec::PanicStrategy; -use {rustc_abi as abi, rustc_ast as ast, rustc_attr as attr, rustc_hir as hir}; +use {rustc_abi as abi, rustc_ast as ast, rustc_attr_parsing as attr, rustc_hir as hir}; use crate::infer::canonical::{self, Canonical}; use crate::lint::LintExpectation; @@ -680,7 +679,7 @@ rustc_queries! { /// of supertraits that define the given associated type. This is used to avoid /// cycles in resolving type-dependent associated item paths like `T::Item`. query explicit_supertraits_containing_assoc_item( - key: (DefId, rustc_span::symbol::Ident) + key: (DefId, rustc_span::Ident) ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { desc { |tcx| "computing the super traits of `{}` with associated type name `{}`", tcx.def_path_str(key.0), @@ -709,7 +708,7 @@ rustc_queries! { /// To avoid cycles within the predicates of a single item we compute /// per-type-parameter predicates for resolving `T::AssocTy`. query type_param_predicates( - key: (LocalDefId, LocalDefId, rustc_span::symbol::Ident) + key: (LocalDefId, LocalDefId, rustc_span::Ident) ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { desc { |tcx| "computing the bounds for type parameter `{}`", tcx.hir().ty_param_name(key.1) } } @@ -1267,7 +1266,7 @@ rustc_queries! { /// Returns the attributes on the item at `def_id`. /// /// Do not use this directly, use `tcx.get_attrs` instead. - query item_attrs(def_id: DefId) -> &'tcx [ast::Attribute] { + query attrs_for_def(def_id: DefId) -> &'tcx [hir::Attribute] { desc { |tcx| "collecting attributes of `{}`", tcx.def_path_str(def_id) } separate_provide_extern } @@ -1284,7 +1283,7 @@ rustc_queries! { desc { |tcx| "computing target features for inline asm of `{}`", tcx.def_path_str(def_id) } } - query fn_arg_names(def_id: DefId) -> &'tcx [rustc_span::symbol::Ident] { + query fn_arg_names(def_id: DefId) -> &'tcx [rustc_span::Ident] { desc { |tcx| "looking up function parameter names for `{}`", tcx.def_path_str(def_id) } separate_provide_extern } diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index 119a99e1bf7..4a144ebb899 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -798,8 +798,8 @@ macro_rules! impl_ref_decoder { impl_ref_decoder! {<'tcx> Span, - rustc_ast::Attribute, - rustc_span::symbol::Ident, + rustc_hir::Attribute, + rustc_span::Ident, ty::Variance, rustc_span::def_id::DefId, rustc_span::def_id::LocalDefId, diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 0a77c3bc42f..b34a1782581 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -20,8 +20,7 @@ use rustc_macros::{ Decodable, Encodable, HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable, }; use rustc_span::def_id::{CRATE_DEF_ID, LocalDefId}; -use rustc_span::symbol::Symbol; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Span, Symbol}; // FIXME: Remove this import and import via `solve::` pub use rustc_type_ir::solve::BuiltinImplSource; use smallvec::{SmallVec, smallvec}; @@ -316,8 +315,8 @@ pub enum ObligationCauseCode<'tcx> { span: Option<Span>, /// The root expected type induced by a scrutinee or type expression. root_ty: Ty<'tcx>, - /// Whether the `Span` came from an expression or a type expression. - origin_expr: bool, + /// Information about the `Span`, if it came from an expression, otherwise `None`. + origin_expr: Option<PatternOriginExpr>, }, /// Computing common supertype in an if expression @@ -530,6 +529,25 @@ pub struct MatchExpressionArmCause<'tcx> { pub tail_defines_return_position_impl_trait: Option<LocalDefId>, } +/// Information about the origin expression of a pattern, relevant to diagnostics. +/// Fields here refer to the scrutinee of a pattern. +/// If the scrutinee isn't given in the diagnostic, then this won't exist. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(TypeFoldable, TypeVisitable, HashStable, TyEncodable, TyDecodable)] +pub struct PatternOriginExpr { + /// A span representing the scrutinee expression, with all leading references + /// peeled from the expression. + /// Only references in the expression are peeled - if the expression refers to a variable + /// whose type is a reference, then that reference is kept because it wasn't created + /// in the expression. + pub peeled_span: Span, + /// The number of references that were peeled to produce `peeled_span`. + pub peeled_count: usize, + /// Does the peeled expression need to be wrapped in parentheses for + /// a prefix suggestion (i.e., dereference) to be valid. + pub peeled_prefix_suggestion_parentheses: bool, +} + #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(TypeFoldable, TypeVisitable, HashStable, TyEncodable, TyDecodable)] pub struct IfExpressionCause<'tcx> { diff --git a/compiler/rustc_middle/src/traits/specialization_graph.rs b/compiler/rustc_middle/src/traits/specialization_graph.rs index 515aabbe2fc..ed7c98ee0e0 100644 --- a/compiler/rustc_middle/src/traits/specialization_graph.rs +++ b/compiler/rustc_middle/src/traits/specialization_graph.rs @@ -2,7 +2,7 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::{DefId, DefIdMap}; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; -use rustc_span::symbol::sym; +use rustc_span::sym; use crate::error::StrictCoherenceNeedsNegativeCoherence; use crate::ty::fast_reject::SimplifiedType; diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 447cbc8932e..8f4137cc011 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -17,7 +17,7 @@ use rustc_index::{IndexSlice, IndexVec}; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_query_system::ich::StableHashingContext; use rustc_session::DataTypeKind; -use rustc_span::symbol::sym; +use rustc_span::sym; use rustc_type_ir::solve::AdtDestructorKind; use tracing::{debug, info, trace}; diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index 62157d9bfe2..6309dd2e490 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -3,7 +3,7 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Namespace}; use rustc_hir::def_id::DefId; use rustc_macros::{Decodable, Encodable, HashStable}; -use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::{Ident, Symbol}; use super::{TyCtxt, Visibility}; use crate::ty; diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs index 6ab4d76e545..33d39b137b6 100644 --- a/compiler/rustc_middle/src/ty/closure.rs +++ b/compiler/rustc_middle/src/ty/closure.rs @@ -7,8 +7,7 @@ use rustc_hir::HirId; use rustc_hir::def_id::LocalDefId; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_span::def_id::LocalDefIdMap; -use rustc_span::symbol::Ident; -use rustc_span::{Span, Symbol}; +use rustc_span::{Ident, Span, Symbol}; use super::TyCtxt; use crate::hir::place::{ diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index aba5719138c..94bf1aa4f03 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -548,7 +548,7 @@ macro_rules! impl_arena_copy_decoder { impl_arena_copy_decoder! {<'tcx> Span, - rustc_span::symbol::Ident, + rustc_span::Ident, ty::Variance, rustc_span::def_id::DefId, rustc_span::def_id::LocalDefId, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index db1a479f580..977e62becf1 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -10,10 +10,11 @@ use std::cmp::Ordering; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; use std::ops::{Bound, Deref}; +use std::sync::OnceLock; use std::{fmt, iter, mem}; use rustc_abi::{ExternAbi, FieldIdx, Layout, LayoutData, TargetDataLayout, VariantIdx}; -use rustc_ast::{self as ast, attr}; +use rustc_ast as ast; use rustc_data_structures::defer; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; @@ -29,13 +30,12 @@ use rustc_data_structures::unord::UnordSet; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, LintDiagnostic, MultiSpan, }; -use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::definitions::Definitions; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; -use rustc_hir::{HirId, Node, TraitCandidate}; +use rustc_hir::{self as hir, Attribute, HirId, Node, TraitCandidate}; use rustc_index::IndexVec; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_query_system::cache::WithDepNode; @@ -47,8 +47,7 @@ use rustc_session::cstore::{CrateStoreDyn, Untracked}; use rustc_session::lint::Lint; use rustc_session::{Limit, MetadataKind, Session}; use rustc_span::def_id::{CRATE_DEF_ID, DefPathHash, StableCrateId}; -use rustc_span::symbol::{Ident, Symbol, kw, sym}; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use rustc_type_ir::TyKind::*; use rustc_type_ir::fold::TypeFoldable; use rustc_type_ir::lang_items::TraitSolverLangItem; @@ -586,7 +585,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { } fn trait_is_unsafe(self, trait_def_id: Self::DefId) -> bool { - self.trait_def(trait_def_id).safety == hir::Safety::Unsafe + self.trait_def(trait_def_id).safety.is_unsafe() } fn is_impl_trait_in_trait(self, def_id: DefId) -> bool { @@ -722,7 +721,7 @@ impl<'tcx> rustc_type_ir::inherent::Safety<TyCtxt<'tcx>> for hir::Safety { } fn is_safe(self) -> bool { - matches!(self, hir::Safety::Safe) + self.is_safe() } fn prefix_str(self) -> &'static str { @@ -1119,10 +1118,10 @@ impl<'tcx> CommonConsts<'tcx> { /// either a `ReEarlyParam` or `ReLateParam`. #[derive(Debug)] pub struct FreeRegionInfo { - /// `LocalDefId` of the free region. - pub def_id: LocalDefId, - /// the bound region corresponding to free region. - pub bound_region: ty::BoundRegionKind, + /// `LocalDefId` of the scope. + pub scope: LocalDefId, + /// the `DefId` of the free region. + pub region_def_id: DefId, /// checks if bound region is in Impl Item pub is_impl_item: bool, } @@ -1347,46 +1346,6 @@ pub struct GlobalCtxt<'tcx> { /// Stores memory for globals (statics/consts). pub(crate) alloc_map: Lock<interpret::AllocMap<'tcx>>, - - current_gcx: CurrentGcx, -} - -impl<'tcx> GlobalCtxt<'tcx> { - /// Installs `self` in a `TyCtxt` and `ImplicitCtxt` for the duration of - /// `f`. - pub fn enter<F, R>(&'tcx self, f: F) -> R - where - F: FnOnce(TyCtxt<'tcx>) -> R, - { - let icx = tls::ImplicitCtxt::new(self); - - // Reset `current_gcx` to `None` when we exit. - let _on_drop = defer(move || { - *self.current_gcx.value.write() = None; - }); - - // Set this `GlobalCtxt` as the current one. - { - let mut guard = self.current_gcx.value.write(); - assert!(guard.is_none(), "no `GlobalCtxt` is currently set"); - *guard = Some(self as *const _ as *const ()); - } - - tls::enter_context(&icx, || f(icx.tcx)) - } - - pub fn finish(&'tcx self) { - // We assume that no queries are run past here. If there are new queries - // after this point, they'll show up as "<unknown>" in self-profiling data. - self.enter(|tcx| tcx.alloc_self_profile_query_strings()); - - self.enter(|tcx| tcx.save_dep_graph()); - self.enter(|tcx| tcx.query_key_hash_verify_all()); - - if let Err((path, error)) = self.dep_graph.finish_encoding() { - self.sess.dcx().emit_fatal(crate::error::FailedWritingFile { path: &path, error }); - } - } } /// This is used to get a reference to a `GlobalCtxt` if one is available. @@ -1530,7 +1489,8 @@ impl<'tcx> TyCtxt<'tcx> { /// By only providing the `TyCtxt` inside of the closure we enforce that the type /// context and any interned value (types, args, etc.) can only be used while `ty::tls` /// has a valid reference to the context, to allow formatting values that need it. - pub fn create_global_ctxt( + pub fn create_global_ctxt<T>( + gcx_cell: &'tcx OnceLock<GlobalCtxt<'tcx>>, s: &'tcx Session, crate_types: Vec<CrateType>, stable_crate_id: StableCrateId, @@ -1542,7 +1502,8 @@ impl<'tcx> TyCtxt<'tcx> { query_system: QuerySystem<'tcx>, hooks: crate::hooks::Providers, current_gcx: CurrentGcx, - ) -> GlobalCtxt<'tcx> { + f: impl FnOnce(TyCtxt<'tcx>) -> T, + ) -> T { let data_layout = s.target.parse_data_layout().unwrap_or_else(|err| { s.dcx().emit_fatal(err); }); @@ -1551,7 +1512,7 @@ impl<'tcx> TyCtxt<'tcx> { let common_lifetimes = CommonLifetimes::new(&interners); let common_consts = CommonConsts::new(&interners, &common_types, s, &untracked); - GlobalCtxt { + let gcx = gcx_cell.get_or_init(|| GlobalCtxt { sess: s, crate_types, stable_crate_id, @@ -1575,8 +1536,23 @@ impl<'tcx> TyCtxt<'tcx> { canonical_param_env_cache: Default::default(), data_layout, alloc_map: Lock::new(interpret::AllocMap::new()), - current_gcx, + }); + + let icx = tls::ImplicitCtxt::new(&gcx); + + // Reset `current_gcx` to `None` when we exit. + let _on_drop = defer(|| { + *current_gcx.value.write() = None; + }); + + // Set this `GlobalCtxt` as the current one. + { + let mut guard = current_gcx.value.write(); + assert!(guard.is_none(), "no `GlobalCtxt` is currently set"); + *guard = Some(&gcx as *const _ as *const ()); } + + tls::enter_context(&icx, || f(icx.tcx)) } /// Obtain all lang items of this crate and all dependencies (recursively) @@ -1983,7 +1959,7 @@ impl<'tcx> TyCtxt<'tcx> { generic_param_scope: LocalDefId, mut region: Region<'tcx>, ) -> Option<FreeRegionInfo> { - let (suitable_region_binding_scope, bound_region) = loop { + let (suitable_region_binding_scope, region_def_id) = loop { let def_id = region.opt_param_def_id(self, generic_param_scope.to_def_id())?.as_local()?; let scope = self.local_parent(def_id); @@ -1993,10 +1969,7 @@ impl<'tcx> TyCtxt<'tcx> { region = self.map_opaque_lifetime_to_parent_lifetime(def_id); continue; } - break ( - scope, - ty::BoundRegionKind::Named(def_id.into(), self.item_name(def_id.into())), - ); + break (scope, def_id.into()); }; let is_impl_item = match self.hir_node_by_def_id(suitable_region_binding_scope) { @@ -2005,7 +1978,7 @@ impl<'tcx> TyCtxt<'tcx> { _ => false, }; - Some(FreeRegionInfo { def_id: suitable_region_binding_scope, bound_region, is_impl_item }) + Some(FreeRegionInfo { scope: suitable_region_binding_scope, region_def_id, is_impl_item }) } /// Given a `DefId` for an `fn`, return all the `dyn` and `impl` traits in its return type. @@ -2120,6 +2093,19 @@ impl<'tcx> TyCtxt<'tcx> { pub fn local_opaque_ty_origin(self, def_id: LocalDefId) -> hir::OpaqueTyOrigin<LocalDefId> { self.hir().expect_opaque_ty(def_id).origin } + + pub fn finish(self) { + // We assume that no queries are run past here. If there are new queries + // after this point, they'll show up as "<unknown>" in self-profiling data. + self.alloc_self_profile_query_strings(); + + self.save_dep_graph(); + self.query_key_hash_verify_all(); + + if let Err((path, error)) = self.dep_graph.finish_encoding() { + self.sess.dcx().emit_fatal(crate::error::FailedWritingFile { path: &path, error }); + } + } } macro_rules! nop_lift { @@ -2521,7 +2507,7 @@ impl<'tcx> TyCtxt<'tcx> { /// that is, a `fn` type that is equivalent in every way for being /// unsafe. pub fn safe_to_unsafe_fn_ty(self, sig: PolyFnSig<'tcx>) -> Ty<'tcx> { - assert_eq!(sig.safety(), hir::Safety::Safe); + assert!(sig.safety().is_safe()); Ty::new_fn_ptr(self, sig.map_bound(|sig| ty::FnSig { safety: hir::Safety::Unsafe, ..sig })) } @@ -3105,7 +3091,7 @@ impl<'tcx> TyCtxt<'tcx> { return ty::Region::new_late_param( self, new_parent.to_def_id(), - ty::BoundRegionKind::Named( + ty::LateParamRegionKind::Named( lbv.to_def_id(), self.item_name(lbv.to_def_id()), ), @@ -3239,12 +3225,16 @@ pub fn provide(providers: &mut Providers) { providers.extern_mod_stmt_cnum = |tcx, id| tcx.resolutions(()).extern_crate_map.get(&id).cloned(); providers.is_panic_runtime = - |tcx, LocalCrate| attr::contains_name(tcx.hir().krate_attrs(), sym::panic_runtime); + |tcx, LocalCrate| contains_name(tcx.hir().krate_attrs(), sym::panic_runtime); providers.is_compiler_builtins = - |tcx, LocalCrate| attr::contains_name(tcx.hir().krate_attrs(), sym::compiler_builtins); + |tcx, LocalCrate| contains_name(tcx.hir().krate_attrs(), sym::compiler_builtins); providers.has_panic_handler = |tcx, LocalCrate| { // We want to check if the panic handler was defined in this crate tcx.lang_items().panic_impl().is_some_and(|did| did.is_local()) }; providers.source_span = |tcx, def_id| tcx.untracked.source_span.get(def_id).unwrap_or(DUMMY_SP); } + +pub fn contains_name(attrs: &[Attribute], name: Symbol) -> bool { + attrs.iter().any(|x| x.has_name(name)) +} diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 604f1da26c6..406e732744b 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -309,7 +309,7 @@ pub fn suggest_constraining_type_params<'a>( let Some(param) = param else { return false }; { - let mut sized_constraints = constraints.extract_if(|(_, def_id, _)| { + let mut sized_constraints = constraints.extract_if(.., |(_, def_id, _)| { def_id.is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::Sized)) }); if let Some((_, def_id, _)) = sized_constraints.next() { diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index 1b073d3c466..067516917ef 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -270,7 +270,8 @@ impl<'tcx> TyCtxt<'tcx> { T: TypeFoldable<TyCtxt<'tcx>>, { self.instantiate_bound_regions_uncached(value, |br| { - ty::Region::new_late_param(self, all_outlive_scope, br.kind) + let kind = ty::LateParamRegionKind::from_bound(br.var, br.kind); + ty::Region::new_late_param(self, all_outlive_scope, kind) }) } diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index ce40ab18261..85d9db7ee74 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -2,8 +2,7 @@ use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; -use rustc_span::Span; -use rustc_span::symbol::{Symbol, kw}; +use rustc_span::{Span, Symbol, kw}; use tracing::instrument; use super::{Clause, InstantiatedPredicates, ParamConst, ParamTy, Ty, TyCtxt}; diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 1dd564d9798..49b5588e261 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -202,7 +202,7 @@ impl<'tcx> Instance<'tcx> { if !tcx.sess.opts.share_generics() // However, if the def_id is marked inline(never), then it's fine to just reuse the // upstream monomorphization. - && tcx.codegen_fn_attrs(self.def_id()).inline != rustc_attr::InlineAttr::Never + && tcx.codegen_fn_attrs(self.def_id()).inline != rustc_attr_parsing::InlineAttr::Never { return None; } @@ -277,7 +277,7 @@ impl<'tcx> InstanceKind<'tcx> { &self, tcx: TyCtxt<'tcx>, attr: Symbol, - ) -> impl Iterator<Item = &'tcx rustc_ast::Attribute> { + ) -> impl Iterator<Item = &'tcx hir::Attribute> { tcx.get_attrs(self.def_id(), attr) } diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 07573a79260..367b0c07f9b 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -16,8 +16,7 @@ use rustc_hir::def_id::DefId; use rustc_index::IndexVec; use rustc_macros::{HashStable, TyDecodable, TyEncodable, extension}; use rustc_session::config::OptLevel; -use rustc_span::symbol::{Symbol, sym}; -use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; +use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym}; use rustc_target::callconv::FnAbi; use rustc_target::spec::{ HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, PanicStrategy, Target, WasmCAbi, X86Abi, @@ -735,21 +734,22 @@ where let layout = match this.variants { Variants::Single { index } // If all variants but one are uninhabited, the variant layout is the enum layout. - if index == variant_index && - // Don't confuse variants of uninhabited enums with the enum itself. - // For more details see https://github.com/rust-lang/rust/issues/69763. - this.fields != FieldsShape::Primitive => + if index == variant_index => { this.layout } - Variants::Single { index } => { + Variants::Single { .. } | Variants::Empty => { + // Single-variant and no-variant enums *can* have other variants, but those are + // uninhabited. Produce a layout that has the right fields for that variant, so that + // the rest of the compiler can project fields etc as usual. + let tcx = cx.tcx(); let typing_env = cx.typing_env(); // Deny calling for_variant more than once for non-Single enums. if let Ok(original_layout) = tcx.layout_of(typing_env.as_query_input(this.ty)) { - assert_eq!(original_layout.variants, Variants::Single { index }); + assert_eq!(original_layout.variants, this.variants); } let fields = match this.ty.kind() { @@ -903,6 +903,7 @@ where ), ty::Coroutine(def_id, args) => match this.variants { + Variants::Empty => unreachable!(), Variants::Single { index } => TyMaybeWithLayout::Ty( args.as_coroutine() .state_tys(def_id, tcx) @@ -928,6 +929,7 @@ where let field = &def.variant(index).fields[FieldIdx::from_usize(i)]; TyMaybeWithLayout::Ty(field.ty(tcx, args)) } + Variants::Empty => panic!("there is no field in Variants::Empty types"), // Discriminant field for enums (where applicable). Variants::Multiple { tag, .. } => { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index e47b0cf0b3b..5e929fbec0b 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -33,9 +33,9 @@ use rustc_data_structures::intern::Interned; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::steal::Steal; use rustc_errors::{Diag, ErrorGuaranteed, StashKey}; +use rustc_hir::LangItem; use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap}; -use rustc_hir::{LangItem, Safety}; use rustc_index::IndexVec; use rustc_macros::{ Decodable, Encodable, HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable, @@ -46,13 +46,12 @@ use rustc_serialize::{Decodable, Encodable}; use rustc_session::lint::LintBuffer; pub use rustc_session::lint::RegisteredTools; use rustc_span::hygiene::MacroKind; -use rustc_span::symbol::{Ident, Symbol, kw, sym}; -use rustc_span::{ExpnId, ExpnKind, Span}; +use rustc_span::{ExpnId, ExpnKind, Ident, Span, Symbol, kw, sym}; pub use rustc_type_ir::relate::VarianceDiagInfo; pub use rustc_type_ir::*; use tracing::{debug, instrument}; pub use vtable::*; -use {rustc_ast as ast, rustc_attr as attr, rustc_hir as hir}; +use {rustc_ast as ast, rustc_attr_parsing as attr, rustc_hir as hir}; pub use self::closure::{ BorrowKind, CAPTURE_STRUCT_LOCAL, CaptureInfo, CapturedPlace, ClosureTypeInfo, @@ -84,7 +83,8 @@ pub use self::predicate::{ TypeOutlivesPredicate, }; pub use self::region::{ - BoundRegion, BoundRegionKind, EarlyParamRegion, LateParamRegion, Region, RegionKind, RegionVid, + BoundRegion, BoundRegionKind, EarlyParamRegion, LateParamRegion, LateParamRegionKind, Region, + RegionKind, RegionVid, }; pub use self::rvalue_scopes::RvalueScopes; pub use self::sty::{ @@ -972,7 +972,7 @@ pub struct ParamEnv<'tcx> { } impl<'tcx> rustc_type_ir::inherent::ParamEnv<TyCtxt<'tcx>> for ParamEnv<'tcx> { - fn caller_bounds(self) -> impl IntoIterator<Item = ty::Clause<'tcx>> { + fn caller_bounds(self) -> impl inherent::SliceLike<Item = ty::Clause<'tcx>> { self.caller_bounds() } } @@ -1281,7 +1281,7 @@ impl VariantDef { /// Returns whether this variant has unsafe fields. pub fn has_unsafe_fields(&self) -> bool { - self.fields.iter().any(|x| x.safety == Safety::Unsafe) + self.fields.iter().any(|x| x.safety.is_unsafe()) } } @@ -1745,11 +1745,11 @@ impl<'tcx> TyCtxt<'tcx> { } // FIXME(@lcnr): Remove this function. - pub fn get_attrs_unchecked(self, did: DefId) -> &'tcx [ast::Attribute] { + pub fn get_attrs_unchecked(self, did: DefId) -> &'tcx [hir::Attribute] { if let Some(did) = did.as_local() { self.hir().attrs(self.local_def_id_to_hir_id(did)) } else { - self.item_attrs(did) + self.attrs_for_def(did) } } @@ -1758,14 +1758,14 @@ impl<'tcx> TyCtxt<'tcx> { self, did: impl Into<DefId>, attr: Symbol, - ) -> impl Iterator<Item = &'tcx ast::Attribute> { + ) -> impl Iterator<Item = &'tcx hir::Attribute> { let did: DefId = did.into(); - let filter_fn = move |a: &&ast::Attribute| a.has_name(attr); + let filter_fn = move |a: &&hir::Attribute| a.has_name(attr); if let Some(did) = did.as_local() { self.hir().attrs(self.local_def_id_to_hir_id(did)).iter().filter(filter_fn) } else { debug_assert!(rustc_feature::encode_cross_crate(attr)); - self.item_attrs(did).iter().filter(filter_fn) + self.attrs_for_def(did).iter().filter(filter_fn) } } @@ -1781,7 +1781,7 @@ impl<'tcx> TyCtxt<'tcx> { self, did: impl Into<DefId>, attr: Symbol, - ) -> Option<&'tcx ast::Attribute> { + ) -> Option<&'tcx hir::Attribute> { let did: DefId = did.into(); if did.as_local().is_some() { // it's a crate local item, we need to check feature flags @@ -1794,7 +1794,7 @@ impl<'tcx> TyCtxt<'tcx> { // we filter out unstable diagnostic attributes before // encoding attributes debug_assert!(rustc_feature::encode_cross_crate(attr)); - self.item_attrs(did) + self.attrs_for_def(did) .iter() .find(|a| matches!(a.path().as_ref(), [sym::diagnostic, a] if *a == attr)) } @@ -1804,19 +1804,19 @@ impl<'tcx> TyCtxt<'tcx> { self, did: DefId, attr: &'attr [Symbol], - ) -> impl Iterator<Item = &'tcx ast::Attribute> + 'attr + ) -> impl Iterator<Item = &'tcx hir::Attribute> + 'attr where 'tcx: 'attr, { - let filter_fn = move |a: &&ast::Attribute| a.path_matches(attr); + let filter_fn = move |a: &&hir::Attribute| a.path_matches(attr); if let Some(did) = did.as_local() { self.hir().attrs(self.local_def_id_to_hir_id(did)).iter().filter(filter_fn) } else { - self.item_attrs(did).iter().filter(filter_fn) + self.attrs_for_def(did).iter().filter(filter_fn) } } - pub fn get_attr(self, did: impl Into<DefId>, attr: Symbol) -> Option<&'tcx ast::Attribute> { + pub fn get_attr(self, did: impl Into<DefId>, attr: Symbol) -> Option<&'tcx hir::Attribute> { if cfg!(debug_assertions) && !rustc_feature::is_valid_for_get_attr(attr) { let did: DefId = did.into(); bug!("get_attr: unexpected called with DefId `{:?}`, attr `{:?}`", did, attr); diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index f7322217aa3..86a95827e84 100644 --- a/compiler/rustc_middle/src/ty/parameterized.rs +++ b/compiler/rustc_middle/src/ty/parameterized.rs @@ -80,10 +80,10 @@ trivially_parameterized_over_tcx! { rustc_ast::Attribute, rustc_ast::DelimArgs, rustc_ast::expand::StrippedCfgItem<rustc_hir::def_id::DefIndex>, - rustc_attr::ConstStability, - rustc_attr::DefaultBodyStability, - rustc_attr::Deprecation, - rustc_attr::Stability, + rustc_attr_parsing::ConstStability, + rustc_attr_parsing::DefaultBodyStability, + rustc_attr_parsing::Deprecation, + rustc_attr_parsing::Stability, rustc_hir::Constness, rustc_hir::Defaultness, rustc_hir::Safety, @@ -109,8 +109,9 @@ trivially_parameterized_over_tcx! { rustc_span::Symbol, rustc_span::def_id::DefPathHash, rustc_span::hygiene::SyntaxContextData, - rustc_span::symbol::Ident, + rustc_span::Ident, rustc_type_ir::Variance, + rustc_hir::Attribute, } // HACK(compiler-errors): This macro rule can only take a fake path, @@ -140,5 +141,5 @@ parameterized_over_tcx! { ty::Predicate, ty::Clause, ty::ClauseKind, - ty::ImplTraitHeader + ty::ImplTraitHeader, } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 40e0fb0087f..a089eac5d7e 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -16,8 +16,7 @@ use rustc_hir::definitions::{DefKey, DefPathDataName}; use rustc_macros::{Lift, extension}; use rustc_session::Limit; use rustc_session::cstore::{ExternCrate, ExternCrateSource}; -use rustc_span::symbol::{Ident, Symbol, kw}; -use rustc_span::{FileNameDisplayPreference, sym}; +use rustc_span::{FileNameDisplayPreference, Ident, Symbol, kw, sym}; use rustc_type_ir::{Upcast as _, elaborate}; use smallvec::SmallVec; @@ -2375,8 +2374,8 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> { match *region { ty::ReEarlyParam(ref data) => data.has_name(), + ty::ReLateParam(ty::LateParamRegion { kind, .. }) => kind.is_named(), ty::ReBound(_, ty::BoundRegion { kind: br, .. }) - | ty::ReLateParam(ty::LateParamRegion { bound_region: br, .. }) | ty::RePlaceholder(ty::Placeholder { bound: ty::BoundRegion { kind: br, .. }, .. }) => { @@ -2449,8 +2448,13 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { return Ok(()); } } + ty::ReLateParam(ty::LateParamRegion { kind, .. }) => { + if let Some(name) = kind.get_name() { + p!(write("{}", name)); + return Ok(()); + } + } ty::ReBound(_, ty::BoundRegion { kind: br, .. }) - | ty::ReLateParam(ty::LateParamRegion { bound_region: br, .. }) | ty::RePlaceholder(ty::Placeholder { bound: ty::BoundRegion { kind: br, .. }, .. }) => { diff --git a/compiler/rustc_middle/src/ty/region.rs b/compiler/rustc_middle/src/ty/region.rs index 60c2c322d4f..14a2e5befe6 100644 --- a/compiler/rustc_middle/src/ty/region.rs +++ b/compiler/rustc_middle/src/ty/region.rs @@ -4,8 +4,7 @@ use rustc_data_structures::intern::Interned; use rustc_errors::MultiSpan; use rustc_hir::def_id::DefId; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; -use rustc_span::symbol::{Symbol, kw, sym}; -use rustc_span::{DUMMY_SP, ErrorGuaranteed}; +use rustc_span::{DUMMY_SP, ErrorGuaranteed, Symbol, kw, sym}; use rustc_type_ir::RegionKind as IrRegionKind; pub use rustc_type_ir::RegionVid; use tracing::debug; @@ -70,9 +69,10 @@ impl<'tcx> Region<'tcx> { pub fn new_late_param( tcx: TyCtxt<'tcx>, scope: DefId, - bound_region: ty::BoundRegionKind, + kind: LateParamRegionKind, ) -> Region<'tcx> { - tcx.intern_region(ty::ReLateParam(ty::LateParamRegion { scope, bound_region })) + let data = LateParamRegion { scope, kind }; + tcx.intern_region(ty::ReLateParam(data)) } #[inline] @@ -125,8 +125,8 @@ impl<'tcx> Region<'tcx> { match kind { ty::ReEarlyParam(region) => Region::new_early_param(tcx, region), ty::ReBound(debruijn, region) => Region::new_bound(tcx, debruijn, region), - ty::ReLateParam(ty::LateParamRegion { scope, bound_region }) => { - Region::new_late_param(tcx, scope, bound_region) + ty::ReLateParam(ty::LateParamRegion { scope, kind }) => { + Region::new_late_param(tcx, scope, kind) } ty::ReStatic => tcx.lifetimes.re_static, ty::ReVar(vid) => Region::new_var(tcx, vid), @@ -166,7 +166,7 @@ impl<'tcx> Region<'tcx> { match *self { ty::ReEarlyParam(ebr) => Some(ebr.name), ty::ReBound(_, br) => br.kind.get_name(), - ty::ReLateParam(fr) => fr.bound_region.get_name(), + ty::ReLateParam(fr) => fr.kind.get_name(), ty::ReStatic => Some(kw::StaticLifetime), ty::RePlaceholder(placeholder) => placeholder.bound.kind.get_name(), _ => None, @@ -188,7 +188,7 @@ impl<'tcx> Region<'tcx> { match *self { ty::ReEarlyParam(ebr) => ebr.has_name(), ty::ReBound(_, br) => br.kind.is_named(), - ty::ReLateParam(fr) => fr.bound_region.is_named(), + ty::ReLateParam(fr) => fr.kind.is_named(), ty::ReStatic => true, ty::ReVar(..) => false, ty::RePlaceholder(placeholder) => placeholder.bound.kind.is_named(), @@ -311,7 +311,7 @@ impl<'tcx> Region<'tcx> { Some(tcx.generics_of(binding_item).region_param(ebr, tcx).def_id) } ty::ReLateParam(ty::LateParamRegion { - bound_region: ty::BoundRegionKind::Named(def_id, _), + kind: ty::LateParamRegionKind::Named(def_id, _), .. }) => Some(def_id), _ => None, @@ -359,7 +359,71 @@ impl std::fmt::Debug for EarlyParamRegion { /// different parameters apart. pub struct LateParamRegion { pub scope: DefId, - pub bound_region: BoundRegionKind, + pub kind: LateParamRegionKind, +} + +/// When liberating bound regions, we map their [`BoundRegionKind`] +/// to this as we need to track the index of anonymous regions. We +/// otherwise end up liberating multiple bound regions to the same +/// late-bound region. +#[derive(Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Copy)] +#[derive(HashStable)] +pub enum LateParamRegionKind { + /// An anonymous region parameter for a given fn (&T) + /// + /// Unlike [`BoundRegionKind::Anon`], this tracks the index of the + /// liberated bound region. + /// + /// We should ideally never liberate anonymous regions, but do so for the + /// sake of diagnostics in `FnCtxt::sig_of_closure_with_expectation`. + Anon(u32), + + /// Named region parameters for functions (a in &'a T) + /// + /// The `DefId` is needed to distinguish free regions in + /// the event of shadowing. + Named(DefId, Symbol), + + /// Anonymous region for the implicit env pointer parameter + /// to a closure + ClosureEnv, +} + +impl LateParamRegionKind { + pub fn from_bound(var: BoundVar, br: BoundRegionKind) -> LateParamRegionKind { + match br { + BoundRegionKind::Anon => LateParamRegionKind::Anon(var.as_u32()), + BoundRegionKind::Named(def_id, name) => LateParamRegionKind::Named(def_id, name), + BoundRegionKind::ClosureEnv => LateParamRegionKind::ClosureEnv, + } + } + + pub fn is_named(&self) -> bool { + match *self { + LateParamRegionKind::Named(_, name) => { + name != kw::UnderscoreLifetime && name != kw::Empty + } + _ => false, + } + } + + pub fn get_name(&self) -> Option<Symbol> { + if self.is_named() { + match *self { + LateParamRegionKind::Named(_, name) => return Some(name), + _ => unreachable!(), + } + } + + None + } + + pub fn get_id(&self) -> Option<DefId> { + match *self { + LateParamRegionKind::Named(id, _) => Some(id), + _ => None, + } + } } #[derive(Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Copy)] diff --git a/compiler/rustc_middle/src/ty/rvalue_scopes.rs b/compiler/rustc_middle/src/ty/rvalue_scopes.rs index 57c2d7623d2..b00c8169a36 100644 --- a/compiler/rustc_middle/src/ty/rvalue_scopes.rs +++ b/compiler/rustc_middle/src/ty/rvalue_scopes.rs @@ -35,7 +35,7 @@ impl RvalueScopes { // if there's one. Static items, for instance, won't // have an enclosing scope, hence no scope will be // returned. - let mut id = Scope { id: expr_id, data: ScopeData::Node }; + let mut id = Scope { local_id: expr_id, data: ScopeData::Node }; let mut backwards_incompatible = None; while let Some(&(p, _)) = region_scope_tree.parent_map.get(&id) { @@ -60,7 +60,7 @@ impl RvalueScopes { if backwards_incompatible.is_none() { backwards_incompatible = region_scope_tree .backwards_incompatible_scope - .get(&p.item_local_id()) + .get(&p.local_id) .copied(); } id = p @@ -76,7 +76,7 @@ impl RvalueScopes { pub fn record_rvalue_scope(&mut self, var: hir::ItemLocalId, lifetime: Option<Scope>) { debug!("record_rvalue_scope(var={var:?}, lifetime={lifetime:?})"); if let Some(lifetime) = lifetime { - assert!(var != lifetime.item_local_id()); + assert!(var != lifetime.local_id); } self.map.insert(var, lifetime); } diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 0af0a5f170d..f38454ceac0 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -77,7 +77,23 @@ impl fmt::Debug for ty::BoundRegionKind { impl fmt::Debug for ty::LateParamRegion { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "ReLateParam({:?}, {:?})", self.scope, self.bound_region) + write!(f, "ReLateParam({:?}, {:?})", self.scope, self.kind) + } +} + +impl fmt::Debug for ty::LateParamRegionKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + ty::LateParamRegionKind::Anon(idx) => write!(f, "BrAnon({idx})"), + ty::LateParamRegionKind::Named(did, name) => { + if did.is_crate_root() { + write!(f, "BrNamed({name})") + } else { + write!(f, "BrNamed({did:?}, {name})") + } + } + ty::LateParamRegionKind::ClosureEnv => write!(f, "BrEnv"), + } } } @@ -224,7 +240,6 @@ TrivialTypeTraversalImpls! { ::rustc_ast::InlineAsmOptions, ::rustc_ast::InlineAsmTemplatePiece, ::rustc_ast::NodeId, - ::rustc_span::symbol::Symbol, ::rustc_hir::def::Res, ::rustc_hir::def_id::LocalDefId, ::rustc_hir::ByRef, @@ -252,8 +267,9 @@ TrivialTypeTraversalImpls! { crate::ty::Placeholder<ty::BoundVar>, crate::ty::LateParamRegion, crate::ty::adjustment::PointerCoercion, + ::rustc_span::Ident, ::rustc_span::Span, - ::rustc_span::symbol::Ident, + ::rustc_span::Symbol, ty::BoundVar, ty::ValTree<'tcx>, } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 3fbc23924f5..045c483d6a5 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -15,8 +15,7 @@ use rustc_hir as hir; use rustc_hir::LangItem; use rustc_hir::def_id::DefId; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, extension}; -use rustc_span::symbol::{Symbol, sym}; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Span, Symbol, sym}; use rustc_type_ir::TyKind::*; use rustc_type_ir::visit::TypeVisitableExt; use rustc_type_ir::{self as ir, BoundVar, CollectAndApply, DynKind}; diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index 188107e5d54..743ea33b20a 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -70,12 +70,12 @@ pub struct TraitDef { /// Whether to add a builtin `dyn Trait: Trait` implementation. /// This is enabled for all traits except ones marked with - /// `#[rustc_deny_explicit_impl(implement_via_object = false)]`. + /// `#[rustc_do_not_implement_via_object]`. pub implement_via_object: bool, /// Whether a trait is fully built-in, and any implementation is disallowed. /// This only applies to built-in traits, and is marked via - /// `#[rustc_deny_explicit_impl(implement_via_object = ...)]`. + /// `#[rustc_deny_explicit_impl]`. pub deny_explicit_impl: bool, } diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 551c113aa59..f94f52e4f61 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<BindingMode>, /// 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<bool>, + /// migration lint. Problematic subpatterns are stored in the `Vec` for the lint to highlight. + rust_2024_migration_desugared_pats: ItemLocalMap<Vec<(Span, String)>>, /// 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_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index fda9898ed56..fc3530e3dde 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -1291,7 +1291,7 @@ impl<'tcx> Ty<'tcx> { /// Checks whether this type is an ADT that has unsafe fields. pub fn has_unsafe_fields(self) -> bool { if let ty::Adt(adt_def, ..) = self.kind() { - adt_def.all_fields().any(|x| x.safety == hir::Safety::Unsafe) + adt_def.all_fields().any(|x| x.safety.is_unsafe()) } else { false } diff --git a/compiler/rustc_middle/src/util/call_kind.rs b/compiler/rustc_middle/src/util/call_kind.rs index df5b73ac1bd..0e395331687 100644 --- a/compiler/rustc_middle/src/util/call_kind.rs +++ b/compiler/rustc_middle/src/util/call_kind.rs @@ -4,8 +4,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::{LangItem, lang_items}; -use rustc_span::symbol::Ident; -use rustc_span::{DesugaringKind, Span, sym}; +use rustc_span::{DesugaringKind, Ident, Span, sym}; use tracing::debug; use crate::ty::{AssocItemContainer, GenericArgsRef, Instance, Ty, TyCtxt, TypingEnv}; diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index f647486f62a..edba247c7b0 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/build/block.rs b/compiler/rustc_mir_build/src/builder/block.rs index 89e64015bc4..ba63a97de89 100644 --- a/compiler/rustc_mir_build/src/build/block.rs +++ b/compiler/rustc_mir_build/src/builder/block.rs @@ -5,9 +5,9 @@ use rustc_middle::{span_bug, ty}; use rustc_span::Span; use tracing::debug; -use crate::build::ForGuard::OutsideGuard; -use crate::build::matches::{DeclareLetBindings, EmitStorageLive, ScheduleDrops}; -use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder}; +use crate::builder::ForGuard::OutsideGuard; +use crate::builder::matches::{DeclareLetBindings, EmitStorageLive, ScheduleDrops}; +use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder}; impl<'a, 'tcx> Builder<'a, 'tcx> { pub(crate) fn ast_block( diff --git a/compiler/rustc_mir_build/src/build/cfg.rs b/compiler/rustc_mir_build/src/builder/cfg.rs index 9c5ee5b0996..42212f2518b 100644 --- a/compiler/rustc_mir_build/src/build/cfg.rs +++ b/compiler/rustc_mir_build/src/builder/cfg.rs @@ -4,7 +4,7 @@ use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; use tracing::debug; -use crate::build::CFG; +use crate::builder::CFG; impl<'tcx> CFG<'tcx> { pub(crate) fn block_data(&self, blk: BasicBlock) -> &BasicBlockData<'tcx> { @@ -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/build/coverageinfo.rs b/compiler/rustc_mir_build/src/builder/coverageinfo.rs index 52a4a4b4b51..a80bd4f3c80 100644 --- a/compiler/rustc_mir_build/src/build/coverageinfo.rs +++ b/compiler/rustc_mir_build/src/builder/coverageinfo.rs @@ -8,8 +8,8 @@ use rustc_middle::thir::{ExprId, ExprKind, Pat, Thir}; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::LocalDefId; -use crate::build::coverageinfo::mcdc::MCDCInfoBuilder; -use crate::build::{Builder, CFG}; +use crate::builder::coverageinfo::mcdc::MCDCInfoBuilder; +use crate::builder::{Builder, CFG}; mod mcdc; diff --git a/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs b/compiler/rustc_mir_build/src/builder/coverageinfo/mcdc.rs index 343d4000043..6b4871dc1fc 100644 --- a/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs +++ b/compiler/rustc_mir_build/src/builder/coverageinfo/mcdc.rs @@ -9,7 +9,7 @@ use rustc_middle::thir::LogicalOp; use rustc_middle::ty::TyCtxt; use rustc_span::Span; -use crate::build::Builder; +use crate::builder::Builder; use crate::errors::MCDCExceedsConditionLimit; /// LLVM uses `i16` to represent condition id. Hence `i16::MAX` is the hard limit for number of diff --git a/compiler/rustc_mir_build/src/build/custom/mod.rs b/compiler/rustc_mir_build/src/builder/custom/mod.rs index e809c9a23f3..ca2e1dd7a1a 100644 --- a/compiler/rustc_mir_build/src/build/custom/mod.rs +++ b/compiler/rustc_mir_build/src/builder/custom/mod.rs @@ -17,10 +17,9 @@ //! terminators, and everything below can be found in the `parse::instruction` submodule. //! -use rustc_ast::Attribute; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::HirId; use rustc_hir::def_id::DefId; +use rustc_hir::{Attribute, HirId}; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::*; use rustc_middle::span_bug; @@ -65,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/build/custom/parse.rs b/compiler/rustc_mir_build/src/builder/custom/parse.rs index 538068e1fac..91e284604b6 100644 --- a/compiler/rustc_mir_build/src/build/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/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs index 67114efdff5..59f440432eb 100644 --- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs @@ -9,8 +9,8 @@ use rustc_span::Span; use rustc_span::source_map::Spanned; use super::{PResult, ParseCtxt, parse_by_kind}; -use crate::build::custom::ParseError; -use crate::build::expr::as_constant::as_constant_inner; +use crate::builder::custom::ParseError; +use crate::builder::expr::as_constant::as_constant_inner; impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { pub(crate) fn parse_statement(&self, expr_id: ExprId) -> PResult<StatementKind<'tcx>> { diff --git a/compiler/rustc_mir_build/src/build/expr/as_constant.rs b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs index 640408cb9c8..177c1e33a83 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs @@ -14,7 +14,7 @@ use rustc_middle::ty::{ use rustc_middle::{bug, mir, span_bug}; use tracing::{instrument, trace}; -use crate::build::{Builder, parse_float_into_constval}; +use crate::builder::{Builder, parse_float_into_constval}; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Compile `expr`, yielding a compile-time constant. Assumes that diff --git a/compiler/rustc_mir_build/src/build/expr/as_operand.rs b/compiler/rustc_mir_build/src/builder/expr/as_operand.rs index 777ff9e68f0..63e9b1dc6cd 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_operand.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_operand.rs @@ -4,8 +4,8 @@ use rustc_middle::mir::*; use rustc_middle::thir::*; use tracing::{debug, instrument}; -use crate::build::expr::category::Category; -use crate::build::{BlockAnd, BlockAndExtension, Builder, NeedsTemporary}; +use crate::builder::expr::category::Category; +use crate::builder::{BlockAnd, BlockAndExtension, Builder, NeedsTemporary}; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Construct a temporary lifetime restricted to just the local scope diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/builder/expr/as_place.rs index 70a74910a68..01aec70f437 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs @@ -14,9 +14,9 @@ use rustc_middle::{bug, span_bug}; use rustc_span::{DesugaringKind, Span}; use tracing::{debug, instrument, trace}; -use crate::build::ForGuard::{OutsideGuard, RefWithinGuard}; -use crate::build::expr::category::Category; -use crate::build::{BlockAnd, BlockAndExtension, Builder, Capture, CaptureMap}; +use crate::builder::ForGuard::{OutsideGuard, RefWithinGuard}; +use crate::builder::expr::category::Category; +use crate::builder::{BlockAnd, BlockAndExtension, Builder, Capture, CaptureMap}; /// The "outermost" place that holds this value. #[derive(Copy, Clone, Debug, PartialEq)] @@ -68,7 +68,7 @@ pub(crate) enum PlaceBase { /// This is used internally when building a place for an expression like `a.b.c`. The fields `b` /// and `c` can be progressively pushed onto the place builder that is created when converting `a`. #[derive(Clone, Debug, PartialEq)] -pub(in crate::build) struct PlaceBuilder<'tcx> { +pub(in crate::builder) struct PlaceBuilder<'tcx> { base: PlaceBase, projection: Vec<PlaceElem<'tcx>>, } @@ -249,7 +249,7 @@ fn strip_prefix<'a, 'tcx>( } impl<'tcx> PlaceBuilder<'tcx> { - pub(in crate::build) fn to_place(&self, cx: &Builder<'_, 'tcx>) -> Place<'tcx> { + pub(in crate::builder) fn to_place(&self, cx: &Builder<'_, 'tcx>) -> Place<'tcx> { self.try_to_place(cx).unwrap_or_else(|| match self.base { PlaceBase::Local(local) => span_bug!( cx.local_decls[local].source_info.span, @@ -265,7 +265,7 @@ impl<'tcx> PlaceBuilder<'tcx> { } /// Creates a `Place` or returns `None` if an upvar cannot be resolved - pub(in crate::build) fn try_to_place(&self, cx: &Builder<'_, 'tcx>) -> Option<Place<'tcx>> { + pub(in crate::builder) fn try_to_place(&self, cx: &Builder<'_, 'tcx>) -> Option<Place<'tcx>> { let resolved = self.resolve_upvar(cx); let builder = resolved.as_ref().unwrap_or(self); let PlaceBase::Local(local) = builder.base else { return None }; @@ -283,7 +283,7 @@ impl<'tcx> PlaceBuilder<'tcx> { /// not captured. This can happen because the final mir that will be /// generated doesn't require a read for this place. Failures will only /// happen inside closures. - pub(in crate::build) fn resolve_upvar( + pub(in crate::builder) fn resolve_upvar( &self, cx: &Builder<'_, 'tcx>, ) -> Option<PlaceBuilder<'tcx>> { @@ -647,13 +647,31 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { match place_ty.kind() { ty::Array(_elem_ty, len_const) => { - // We know how long an array is, so just use that as a constant - // directly -- no locals needed. We do need one statement so - // that borrow- and initialization-checking consider it used, - // though. FIXME: Do we really *need* to count this as a use? - // Could partial array tracking work off something else instead? - self.cfg.push_fake_read(block, source_info, FakeReadCause::ForIndex, place); - let const_ = Const::from_ty_const(*len_const, usize_ty, self.tcx); + let ty_const = if let Some((_, len_ty)) = len_const.try_to_valtree() + && len_ty != self.tcx.types.usize + { + // Bad const generics can give us a constant from the type that's + // not actually a `usize`, so in that case give an error instead. + // FIXME: It'd be nice if the type checker made sure this wasn't + // possible, instead. + let err = self.tcx.dcx().span_delayed_bug( + span, + format!( + "Array length should have already been a type error, as it's {len_ty:?}" + ), + ); + ty::Const::new_error(self.tcx, err) + } else { + // We know how long an array is, so just use that as a constant + // directly -- no locals needed. We do need one statement so + // that borrow- and initialization-checking consider it used, + // though. FIXME: Do we really *need* to count this as a use? + // Could partial array tracking work off something else instead? + self.cfg.push_fake_read(block, source_info, FakeReadCause::ForIndex, place); + *len_const + }; + + let const_ = Const::from_ty_const(ty_const, usize_ty, self.tcx); Operand::Constant(Box::new(ConstOperand { span, user_ty: None, const_ })) } ty::Slice(_elem_ty) => { diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs index c66af118453..9961c2488ef 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs @@ -16,9 +16,9 @@ use rustc_span::source_map::Spanned; use rustc_span::{DUMMY_SP, Span}; use tracing::debug; -use crate::build::expr::as_place::PlaceBase; -use crate::build::expr::category::{Category, RvalueFunc}; -use crate::build::{BlockAnd, BlockAndExtension, Builder, NeedsTemporary}; +use crate::builder::expr::as_place::PlaceBase; +use crate::builder::expr::category::{Category, RvalueFunc}; +use crate::builder::{BlockAnd, BlockAndExtension, Builder, NeedsTemporary}; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Returns an rvalue suitable for use until the end of the current diff --git a/compiler/rustc_mir_build/src/build/expr/as_temp.rs b/compiler/rustc_mir_build/src/builder/expr/as_temp.rs index 466f67b1ba4..2927f5b0c45 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_temp.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_temp.rs @@ -7,8 +7,8 @@ use rustc_middle::mir::*; use rustc_middle::thir::*; use tracing::{debug, instrument}; -use crate::build::scope::DropKind; -use crate::build::{BlockAnd, BlockAndExtension, Builder}; +use crate::builder::scope::DropKind; +use crate::builder::{BlockAnd, BlockAndExtension, Builder}; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Compile `expr` into a fresh temporary. This is used when building @@ -75,11 +75,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { LocalInfo::BlockTailTemp(tail_info) } - _ if let Some(Scope { data: ScopeData::IfThenRescope, id }) = + _ if let Some(Scope { data: ScopeData::IfThenRescope, local_id }) = temp_lifetime.temp_lifetime => { LocalInfo::IfThenRescopeTemp { - if_then: HirId { owner: this.hir_id.owner, local_id: id }, + if_then: HirId { owner: this.hir_id.owner, local_id }, } } diff --git a/compiler/rustc_mir_build/src/build/expr/category.rs b/compiler/rustc_mir_build/src/builder/expr/category.rs index e0349e3e3f6..e0349e3e3f6 100644 --- a/compiler/rustc_mir_build/src/build/expr/category.rs +++ b/compiler/rustc_mir_build/src/builder/expr/category.rs diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index a3d5376dcd4..88f63d4e22c 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -11,9 +11,9 @@ use rustc_middle::ty::CanonicalUserTypeAnnotation; use rustc_span::source_map::Spanned; use tracing::{debug, instrument}; -use crate::build::expr::category::{Category, RvalueFunc}; -use crate::build::matches::DeclareLetBindings; -use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder, NeedsTemporary}; +use crate::builder::expr::category::{Category, RvalueFunc}; +use crate::builder::matches::DeclareLetBindings; +use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder, NeedsTemporary}; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Compile `expr`, storing the result into `destination`, which @@ -367,14 +367,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { .collect() } AdtExprBase::DefaultFields(field_types) => { - itertools::zip_eq(field_names, &**field_types) - .map(|(n, ty)| match fields_map.get(&n) { + itertools::zip_eq(field_names, field_types) + .map(|(n, &ty)| match fields_map.get(&n) { Some(v) => v.clone(), None => match variant.fields[n].value { Some(def) => { - let value = Const::from_unevaluated(this.tcx, def) - .instantiate(this.tcx, args); - this.literal_operand(expr_span, value) + let value = Const::Unevaluated( + UnevaluatedConst::new(def, args), + ty, + ); + Operand::Constant(Box::new(ConstOperand { + span: expr_span, + user_ty: None, + const_: value, + })) } None => { let name = variant.fields[n].name; diff --git a/compiler/rustc_mir_build/src/build/expr/mod.rs b/compiler/rustc_mir_build/src/builder/expr/mod.rs index 3de43a3370f..3de43a3370f 100644 --- a/compiler/rustc_mir_build/src/build/expr/mod.rs +++ b/compiler/rustc_mir_build/src/builder/expr/mod.rs diff --git a/compiler/rustc_mir_build/src/build/expr/stmt.rs b/compiler/rustc_mir_build/src/builder/expr/stmt.rs index 15ee6dd014c..4ae3536d9c2 100644 --- a/compiler/rustc_mir_build/src/build/expr/stmt.rs +++ b/compiler/rustc_mir_build/src/builder/expr/stmt.rs @@ -5,8 +5,8 @@ use rustc_middle::thir::*; use rustc_span::source_map::Spanned; use tracing::debug; -use crate::build::scope::BreakableTarget; -use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder}; +use crate::builder::scope::BreakableTarget; +use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder}; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Builds a block of MIR statements to evaluate the THIR `expr`. diff --git a/compiler/rustc_mir_build/src/build/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index 33fbd7b1a3f..9d59ffc88ba 100644 --- a/compiler/rustc_mir_build/src/build/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -2,9 +2,9 @@ use rustc_middle::mir::*; use rustc_middle::thir::{self, *}; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; -use crate::build::Builder; -use crate::build::expr::as_place::{PlaceBase, PlaceBuilder}; -use crate::build::matches::{FlatPat, MatchPairTree, TestCase}; +use crate::builder::Builder; +use crate::builder::expr::as_place::{PlaceBase, PlaceBuilder}; +use crate::builder::matches::{FlatPat, MatchPairTree, TestCase}; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Builds and returns [`MatchPairTree`] subtrees, one for each pattern in @@ -86,7 +86,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { impl<'pat, 'tcx> MatchPairTree<'pat, 'tcx> { /// Recursively builds a match pair tree for the given pattern and its /// subpatterns. - pub(in crate::build) fn for_pattern( + pub(in crate::builder) fn for_pattern( mut place_builder: PlaceBuilder<'tcx>, pattern: &'pat Pat<'tcx>, cx: &mut Builder<'_, 'tcx>, diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 5791460a6b1..b944d13fb0d 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -14,14 +14,13 @@ use rustc_middle::middle::region; use rustc_middle::mir::{self, *}; use rustc_middle::thir::{self, *}; use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty}; -use rustc_span::symbol::Symbol; -use rustc_span::{BytePos, Pos, Span}; +use rustc_span::{BytePos, Pos, Span, Symbol}; use tracing::{debug, instrument}; -use crate::build::ForGuard::{self, OutsideGuard, RefWithinGuard}; -use crate::build::expr::as_place::PlaceBuilder; -use crate::build::scope::DropKind; -use crate::build::{ +use crate::builder::ForGuard::{self, OutsideGuard, RefWithinGuard}; +use crate::builder::expr::as_place::PlaceBuilder; +use crate::builder::scope::DropKind; +use crate::builder::{ BlockAnd, BlockAndExtension, Builder, GuardFrame, GuardFrameLocal, LocalsForNode, }; diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/builder/matches/simplify.rs index 5b402604395..ebaed1e431b 100644 --- a/compiler/rustc_mir_build/src/build/matches/simplify.rs +++ b/compiler/rustc_mir_build/src/builder/matches/simplify.rs @@ -16,8 +16,8 @@ use std::mem; use tracing::{debug, instrument}; -use crate::build::Builder; -use crate::build::matches::{MatchPairTree, PatternExtraData, TestCase}; +use crate::builder::Builder; +use crate::builder::matches::{MatchPairTree, PatternExtraData, TestCase}; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Simplify a list of match pairs so they all require a test. Stores relevant bindings and diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs index 4f7bbc4ce3e..8cca84d7fcc 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/builder/matches/test.rs @@ -16,12 +16,11 @@ use rustc_middle::ty::{self, GenericArg, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::DefId; use rustc_span::source_map::Spanned; -use rustc_span::symbol::{Symbol, sym}; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Span, Symbol, sym}; use tracing::{debug, instrument}; -use crate::build::Builder; -use crate::build::matches::{Candidate, MatchPairTree, Test, TestBranch, TestCase, TestKind}; +use crate::builder::Builder; +use crate::builder::matches::{Candidate, MatchPairTree, Test, TestBranch, TestCase, TestKind}; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Identifies what test is needed to decide if `match_pair` is applicable. diff --git a/compiler/rustc_mir_build/src/build/matches/util.rs b/compiler/rustc_mir_build/src/builder/matches/util.rs index 555684ded81..1bd399e511b 100644 --- a/compiler/rustc_mir_build/src/build/matches/util.rs +++ b/compiler/rustc_mir_build/src/builder/matches/util.rs @@ -4,9 +4,9 @@ use rustc_middle::ty::Ty; use rustc_span::Span; use tracing::debug; -use crate::build::Builder; -use crate::build::expr::as_place::PlaceBase; -use crate::build::matches::{Binding, Candidate, FlatPat, MatchPairTree, TestCase}; +use crate::builder::Builder; +use crate::builder::expr::as_place::PlaceBase; +use crate::builder::matches::{Binding, Candidate, FlatPat, MatchPairTree, TestCase}; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Creates a false edge to `imaginary_target` and a real edge to diff --git a/compiler/rustc_mir_build/src/build/misc.rs b/compiler/rustc_mir_build/src/builder/misc.rs index a14dcad6573..9ea56a9574f 100644 --- a/compiler/rustc_mir_build/src/build/misc.rs +++ b/compiler/rustc_mir_build/src/builder/misc.rs @@ -7,7 +7,7 @@ use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; use tracing::debug; -use crate::build::Builder; +use crate::builder::Builder; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Adds a new temporary value of type `ty` storing the result of @@ -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/build/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index f43c29d8f5d..0a60899248a 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -1,3 +1,8 @@ +//! This module used to be named `build`, but that was causing GitHub's +//! "Go to file" feature to silently ignore all files in the module, probably +//! because it assumes that "build" is a build-output directory. +//! See <https://github.com/rust-lang/rust/pull/134365>. + use itertools::Itertools; use rustc_abi::{ExternAbi, FieldIdx}; use rustc_apfloat::Float; @@ -19,12 +24,11 @@ use rustc_middle::query::TyCtxtAt; use rustc_middle::thir::{self, ExprId, LintLevel, LocalVarId, Param, ParamId, PatKind, Thir}; use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt, TypeVisitableExt, TypingMode}; use rustc_middle::{bug, span_bug}; -use rustc_span::symbol::sym; -use rustc_span::{Span, Symbol}; +use rustc_span::{Span, Symbol, sym}; use super::lints; -use crate::build::expr::as_place::PlaceBuilder; -use crate::build::scope::DropKind; +use crate::builder::expr::as_place::PlaceBuilder; +use crate::builder::scope::DropKind; pub(crate) fn closure_saved_names_of_captured_variables<'tcx>( tcx: TyCtxt<'tcx>, @@ -527,9 +531,9 @@ fn construct_fn<'tcx>( ); let call_site_scope = - region::Scope { id: body.id().hir_id.local_id, data: region::ScopeData::CallSite }; + region::Scope { local_id: body.id().hir_id.local_id, data: region::ScopeData::CallSite }; let arg_scope = - region::Scope { id: body.id().hir_id.local_id, data: region::ScopeData::Arguments }; + region::Scope { local_id: body.id().hir_id.local_id, data: region::ScopeData::Arguments }; let source_info = builder.source_info(span); let call_site_s = (call_site_scope, source_info); let _: BlockAnd<()> = builder.in_scope(call_site_s, LintLevel::Inherited, |builder| { diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index 636e47b7ad2..35c98037827 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -89,13 +89,13 @@ use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::middle::region; use rustc_middle::mir::*; use rustc_middle::thir::{ExprId, LintLevel}; -use rustc_middle::{bug, span_bug, ty}; +use rustc_middle::{bug, span_bug}; use rustc_session::lint::Level; use rustc_span::source_map::Spanned; use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument}; -use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder, CFG}; +use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder, CFG}; #[derive(Debug)] pub(crate) struct Scopes<'tcx> { @@ -151,15 +151,13 @@ struct DropData { /// Whether this is a value Drop or a StorageDead. kind: DropKind, - - /// Whether this is a backwards-incompatible drop lint - backwards_incompatible_lint: bool, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub(crate) enum DropKind { Value, Storage, + ForLint, } #[derive(Debug)] @@ -248,7 +246,7 @@ impl Scope { /// use of optimizations in the MIR coroutine transform. fn needs_cleanup(&self) -> bool { self.drops.iter().any(|drop| match drop.kind { - DropKind::Value => true, + DropKind::Value | DropKind::ForLint => true, DropKind::Storage => false, }) } @@ -277,12 +275,8 @@ impl DropTree { // represents the block in the tree that should be jumped to once all // of the required drops have been performed. let fake_source_info = SourceInfo::outermost(DUMMY_SP); - let fake_data = DropData { - source_info: fake_source_info, - local: Local::MAX, - kind: DropKind::Storage, - backwards_incompatible_lint: false, - }; + let fake_data = + DropData { source_info: fake_source_info, local: Local::MAX, kind: DropKind::Storage }; let drops = IndexVec::from_raw(vec![DropNode { data: fake_data, next: DropIdx::MAX }]); Self { drops, entry_points: Vec::new(), existing_drops_map: FxHashMap::default() } } @@ -411,6 +405,27 @@ impl DropTree { }; cfg.terminate(block, drop_node.data.source_info, terminator); } + DropKind::ForLint => { + let stmt = Statement { + source_info: drop_node.data.source_info, + kind: StatementKind::BackwardIncompatibleDropHint { + place: Box::new(drop_node.data.local.into()), + reason: BackwardIncompatibleDropReason::Edition2024, + }, + }; + cfg.push(block, stmt); + let target = blocks[drop_node.next].unwrap(); + if target != block { + // Diagnostics don't use this `Span` but debuginfo + // might. Since we don't want breakpoints to be placed + // here, especially when this is on an unwind path, we + // use `DUMMY_SP`. + let source_info = + SourceInfo { span: DUMMY_SP, ..drop_node.data.source_info }; + let terminator = TerminatorKind::Goto { target }; + cfg.terminate(block, source_info, terminator); + } + } // Root nodes don't correspond to a drop. DropKind::Storage if drop_idx == ROOT_NODE => {} DropKind::Storage => { @@ -770,12 +785,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let local = place.as_local().unwrap_or_else(|| bug!("projection in tail call args")); - Some(DropData { - source_info, - local, - kind: DropKind::Value, - backwards_incompatible_lint: false, - }) + Some(DropData { source_info, local, kind: DropKind::Value }) } Operand::Constant(_) => None, }) @@ -822,6 +832,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }); block = next; } + DropKind::ForLint => { + self.cfg.push(block, Statement { + source_info, + kind: StatementKind::BackwardIncompatibleDropHint { + place: Box::new(local.into()), + reason: BackwardIncompatibleDropReason::Edition2024, + }, + }); + } DropKind::Storage => { // Only temps and vars need their storage dead. assert!(local.index() > self.arg_count); @@ -1021,7 +1040,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { drop_kind: DropKind, ) { let needs_drop = match drop_kind { - DropKind::Value => { + DropKind::Value | DropKind::ForLint => { if !self.local_decls[local].ty.needs_drop(self.tcx, self.typing_env()) { return; } @@ -1101,7 +1120,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { source_info: SourceInfo { span: scope_end, scope: scope.source_scope }, local, kind: drop_kind, - backwards_incompatible_lint: false, }); return; @@ -1119,10 +1137,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { region_scope: region::Scope, local: Local, ) { - if !self.local_decls[local].ty.has_significant_drop(self.tcx, ty::TypingEnv { - typing_mode: ty::TypingMode::non_body_analysis(), - param_env: self.param_env, - }) { + if !self.local_decls[local].ty.has_significant_drop(self.tcx, self.typing_env()) { return; } for scope in self.scopes.scopes.iter_mut().rev() { @@ -1135,8 +1150,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { scope.drops.push(DropData { source_info: SourceInfo { span: scope_end, scope: scope.source_scope }, local, - kind: DropKind::Value, - backwards_incompatible_lint: true, + kind: DropKind::ForLint, }); return; @@ -1379,12 +1393,23 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } /// Builds drops for `pop_scope` and `leave_top_scope`. +/// +/// # Parameters +/// +/// * `unwind_drops`, the drop tree data structure storing what needs to be cleaned up if unwind occurs +/// * `scope`, describes the drops that will occur on exiting the scope in regular execution +/// * `block`, the block to branch to once drops are complete (assuming no unwind occurs) +/// * `unwind_to`, describes the drops that would occur at this point in the code if a +/// panic occurred (a subset of the drops in `scope`, since we sometimes elide StorageDead and other +/// instructions on unwinding) +/// * `storage_dead_on_unwind`, if true, then we should emit `StorageDead` even when unwinding +/// * `arg_count`, number of MIR local variables corresponding to fn arguments (used to assert that we don't drop those) fn build_scope_drops<'tcx>( cfg: &mut CFG<'tcx>, unwind_drops: &mut DropTree, scope: &Scope, - mut block: BasicBlock, - mut unwind_to: DropIdx, + block: BasicBlock, + unwind_to: DropIdx, storage_dead_on_unwind: bool, arg_count: usize, ) -> BlockAnd<()> { @@ -1409,6 +1434,18 @@ fn build_scope_drops<'tcx>( // drops for the unwind path should have already been generated by // `diverge_cleanup_gen`. + // `unwind_to` indicates what needs to be dropped should unwinding occur. + // This is a subset of what needs to be dropped when exiting the scope. + // As we unwind the scope, we will also move `unwind_to` backwards to match, + // so that we can use it should a destructor panic. + let mut unwind_to = unwind_to; + + // The block that we should jump to after drops complete. We start by building the final drop (`drops[n]` + // in the diagram above) and then build the drops (e.g., `drop[1]`, `drop[0]`) that come before it. + // block begins as the successor of `drops[n]` and then becomes `drops[n]` so that `drops[n-1]` + // will branch to `drops[n]`. + let mut block = block; + for drop_data in scope.drops.iter().rev() { let source_info = drop_data.source_info; let local = drop_data.local; @@ -1418,6 +1455,9 @@ fn build_scope_drops<'tcx>( // `unwind_to` should drop the value that we're about to // schedule. If dropping this value panics, then we continue // with the *next* value on the unwind path. + // + // We adjust this BEFORE we create the drop (e.g., `drops[n]`) + // because `drops[n]` should unwind to `drops[n-1]`. debug_assert_eq!(unwind_drops.drops[unwind_to].data.local, drop_data.local); debug_assert_eq!(unwind_drops.drops[unwind_to].data.kind, drop_data.kind); unwind_to = unwind_drops.drops[unwind_to].next; @@ -1430,27 +1470,50 @@ fn build_scope_drops<'tcx>( continue; } - if drop_data.backwards_incompatible_lint { - cfg.push(block, Statement { - source_info, - kind: StatementKind::BackwardIncompatibleDropHint { - place: Box::new(local.into()), - reason: BackwardIncompatibleDropReason::Edition2024, - }, - }); - } else { - unwind_drops.add_entry_point(block, unwind_to); - let next = cfg.start_new_block(); - cfg.terminate(block, source_info, TerminatorKind::Drop { - place: local.into(), - target: next, - unwind: UnwindAction::Continue, - replace: false, - }); - block = next; + unwind_drops.add_entry_point(block, unwind_to); + let next = cfg.start_new_block(); + cfg.terminate(block, source_info, TerminatorKind::Drop { + place: local.into(), + target: next, + unwind: UnwindAction::Continue, + replace: false, + }); + block = next; + } + DropKind::ForLint => { + // As in the `DropKind::Storage` case below: + // normally lint-related drops are not emitted for unwind, + // so we can just leave `unwind_to` unmodified, but in some + // cases we emit things ALSO on the unwind path, so we need to adjust + // `unwind_to` in that case. + if storage_dead_on_unwind { + debug_assert_eq!(unwind_drops.drops[unwind_to].data.local, drop_data.local); + debug_assert_eq!(unwind_drops.drops[unwind_to].data.kind, drop_data.kind); + unwind_to = unwind_drops.drops[unwind_to].next; } + + // If the operand has been moved, and we are not on an unwind + // path, then don't generate the drop. (We only take this into + // account for non-unwind paths so as not to disturb the + // caching mechanism.) + if scope.moved_locals.iter().any(|&o| o == local) { + continue; + } + + cfg.push(block, Statement { + source_info, + kind: StatementKind::BackwardIncompatibleDropHint { + place: Box::new(local.into()), + reason: BackwardIncompatibleDropReason::Edition2024, + }, + }); } DropKind::Storage => { + // Ordinarily, storage-dead nodes are not emitted on unwind, so we don't + // need to adjust `unwind_to` on this path. However, in some specific cases + // we *do* emit storage-dead nodes on the unwind path, and in that case now that + // the storage-dead has completed, we need to adjust the `unwind_to` pointer + // so that any future drops we emit will not register storage-dead. if storage_dead_on_unwind { debug_assert_eq!(unwind_drops.drops[unwind_to].data.local, drop_data.local); debug_assert_eq!(unwind_drops.drops[unwind_to].data.kind, drop_data.kind); @@ -1489,7 +1552,7 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> { let mut unwind_indices = IndexVec::from_elem_n(unwind_target, 1); for (drop_idx, drop_node) in drops.drops.iter_enumerated().skip(1) { match drop_node.data.kind { - DropKind::Storage => { + DropKind::Storage | DropKind::ForLint => { if is_coroutine { let unwind_drop = self .scopes diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index f37b3f977fa..f7071eb139f 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -4,7 +4,7 @@ use std::ops::Bound; use rustc_errors::DiagArgValue; use rustc_hir::def::DefKind; -use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability, Safety}; +use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability}; use rustc_middle::middle::codegen_fn_attrs::TargetFeature; use rustc_middle::mir::BorrowKind; use rustc_middle::span_bug; @@ -15,10 +15,9 @@ use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::lint::Level; use rustc_session::lint::builtin::{DEPRECATED_SAFE_2024, UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE}; use rustc_span::def_id::{DefId, LocalDefId}; -use rustc_span::symbol::Symbol; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol, sym}; -use crate::build::ExprCategory; +use crate::builder::ExprCategory; use crate::errors::*; struct UnsafetyVisitor<'a, 'tcx> { @@ -342,7 +341,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { PatKind::Leaf { subpatterns, .. } => { if let ty::Adt(adt_def, ..) = pat.ty.kind() { for pat in subpatterns { - if adt_def.non_enum_variant().fields[pat.field].safety == Safety::Unsafe { + if adt_def.non_enum_variant().fields[pat.field].safety.is_unsafe() { self.requires_unsafe(pat.pattern.span, UseOfUnsafeField); } } @@ -367,7 +366,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { PatKind::Variant { adt_def, args: _, variant_index, subpatterns } => { for pat in subpatterns { let field = &pat.field; - if adt_def.variant(*variant_index).fields[*field].safety == Safety::Unsafe { + if adt_def.variant(*variant_index).fields[*field].safety.is_unsafe() { self.requires_unsafe(pat.pattern.span, UseOfUnsafeField); } } @@ -479,7 +478,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { return; // don't visit the whole expression } ExprKind::Call { fun, ty: _, args: _, from_hir_call: _, fn_span: _ } => { - if self.thir[fun].ty.fn_sig(self.tcx).safety() == hir::Safety::Unsafe { + if self.thir[fun].ty.fn_sig(self.tcx).safety().is_unsafe() { let func_id = if let ty::FnDef(func_id, _) = self.thir[fun].ty.kind() { Some(*func_id) } else { @@ -623,7 +622,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { ExprKind::Field { lhs, variant_index, name } => { let lhs = &self.thir[lhs]; if let ty::Adt(adt_def, _) = lhs.ty.kind() { - if adt_def.variant(variant_index).fields[name].safety == Safety::Unsafe { + if adt_def.variant(variant_index).fields[name].safety.is_unsafe() { self.requires_unsafe(expr.span, UseOfUnsafeField); } else if adt_def.is_union() { if let Some(assigned_ty) = self.assignment_info { @@ -1112,11 +1111,7 @@ pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) { let hir_id = tcx.local_def_id_to_hir_id(def); let safety_context = tcx.hir().fn_sig_by_hir_id(hir_id).map_or(SafetyContext::Safe, |fn_sig| { - if fn_sig.header.safety == hir::Safety::Unsafe { - SafetyContext::UnsafeFn - } else { - SafetyContext::Safe - } + if fn_sig.header.safety.is_unsafe() { SafetyContext::UnsafeFn } else { SafetyContext::Safe } }); let body_target_features = &tcx.body_codegen_attrs(def.to_def_id()).target_features; let mut warnings = Vec::new(); diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 3632da943e1..be5f8bdffb5 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -1,14 +1,13 @@ 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}; use rustc_pattern_analysis::errors::Uncovered; use rustc_pattern_analysis::rustc::RustcPatCtxt; -use rustc_span::Span; -use rustc_span::symbol::Symbol; +use rustc_span::{Span, Symbol}; use crate::fluent_generated as fluent; @@ -1089,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<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( self, diag: &mut Diag<'_, G>, @@ -1112,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/lib.rs b/compiler/rustc_mir_build/src/lib.rs index 833e5019865..467725841dc 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs @@ -11,7 +11,10 @@ #![warn(unreachable_pub)] // tidy-alphabetical-end -mod build; +// The `builder` module used to be named `build`, but that was causing GitHub's +// "Go to file" feature to silently ignore all files in the module, probably +// because it assumes that "build" is a build-output directory. See #134365. +mod builder; mod check_tail_calls; mod check_unsafety; mod errors; @@ -25,9 +28,9 @@ rustc_fluent_macro::fluent_messages! { "../messages.ftl" } pub fn provide(providers: &mut Providers) { providers.check_match = thir::pattern::check_match; providers.lit_to_const = thir::constant::lit_to_const; - providers.hooks.build_mir = build::mir_build; + providers.hooks.build_mir = builder::mir_build; providers.closure_saved_names_of_captured_variables = - build::closure_saved_names_of_captured_variables; + builder::closure_saved_names_of_captured_variables; providers.check_unsafety = check_unsafety::check_unsafety; providers.check_tail_calls = check_tail_calls::check_tail_calls; providers.thir_body = thir::cx::thir_body; diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index 30b6718683b..ce1c635d1b9 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -5,7 +5,7 @@ use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; use rustc_middle::ty::{self, ScalarInt, TyCtxt, TypeVisitableExt as _}; use tracing::trace; -use crate::build::parse_float_into_scalar; +use crate::builder::parse_float_into_scalar; pub(crate) fn lit_to_const<'tcx>( tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_mir_build/src/thir/cx/block.rs b/compiler/rustc_mir_build/src/thir/cx/block.rs index 069c2e7881e..c9df027687a 100644 --- a/compiler/rustc_mir_build/src/thir/cx/block.rs +++ b/compiler/rustc_mir_build/src/thir/cx/block.rs @@ -16,7 +16,7 @@ impl<'tcx> Cx<'tcx> { let block = Block { targeted_by_break: block.targeted_by_break, region_scope: region::Scope { - id: block.hir_id.local_id, + local_id: block.hir_id.local_id, data: region::ScopeData::Node, }, span: block.span, @@ -51,7 +51,7 @@ impl<'tcx> Cx<'tcx> { let stmt = Stmt { kind: StmtKind::Expr { scope: region::Scope { - id: hir_id.local_id, + local_id: hir_id.local_id, data: region::ScopeData::Node, }, expr: self.mirror_expr(expr), @@ -65,7 +65,7 @@ impl<'tcx> Cx<'tcx> { } hir::StmtKind::Let(local) => { let remainder_scope = region::Scope { - id: block_id, + local_id: block_id, data: region::ScopeData::Remainder(region::FirstStatementIndex::new( index, )), @@ -108,7 +108,7 @@ impl<'tcx> Cx<'tcx> { kind: StmtKind::Let { remainder_scope, init_scope: region::Scope { - id: hir_id.local_id, + local_id: hir_id.local_id, data: region::ScopeData::Node, }, pattern, diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 6770e562d50..0338ac674e5 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -25,6 +25,14 @@ use crate::thir::cx::Cx; use crate::thir::util::UserAnnotatedTyHelpers; impl<'tcx> Cx<'tcx> { + /// Create a THIR expression for the given HIR expression. This expands all + /// adjustments and directly adds the type information from the + /// `typeck_results`. See the [dev-guide] for more details. + /// + /// (The term "mirror" in this case does not refer to "flipped" or + /// "reversed".) + /// + /// [dev-guide]: https://rustc-dev-guide.rust-lang.org/thir.html pub(crate) fn mirror_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) -> ExprId { // `mirror_expr` is recursing very deep. Make sure the stack doesn't overflow. ensure_sufficient_stack(|| self.mirror_expr_inner(expr)) @@ -37,7 +45,7 @@ impl<'tcx> Cx<'tcx> { #[instrument(level = "trace", skip(self, hir_expr))] pub(super) fn mirror_expr_inner(&mut self, hir_expr: &'tcx hir::Expr<'tcx>) -> ExprId { let expr_scope = - region::Scope { id: hir_expr.hir_id.local_id, data: region::ScopeData::Node }; + region::Scope { local_id: hir_expr.hir_id.local_id, data: region::ScopeData::Node }; trace!(?hir_expr.hir_id, ?hir_expr.span); @@ -806,14 +814,20 @@ impl<'tcx> Cx<'tcx> { hir::ExprKind::Become(call) => ExprKind::Become { value: self.mirror_expr(call) }, hir::ExprKind::Break(dest, ref value) => match dest.target_id { Ok(target_id) => ExprKind::Break { - label: region::Scope { id: target_id.local_id, data: region::ScopeData::Node }, + label: region::Scope { + local_id: target_id.local_id, + data: region::ScopeData::Node, + }, value: value.map(|value| self.mirror_expr(value)), }, Err(err) => bug!("invalid loop id for break: {}", err), }, hir::ExprKind::Continue(dest) => match dest.target_id { Ok(loop_id) => ExprKind::Continue { - label: region::Scope { id: loop_id.local_id, data: region::ScopeData::Node }, + label: region::Scope { + local_id: loop_id.local_id, + data: region::ScopeData::Node, + }, }, Err(err) => bug!("invalid loop id for continue: {}", err), }, @@ -823,7 +837,7 @@ impl<'tcx> Cx<'tcx> { }, hir::ExprKind::If(cond, then, else_opt) => ExprKind::If { if_then_scope: region::Scope { - id: then.hir_id.local_id, + local_id: then.hir_id.local_id, data: { if expr.span.at_least_rust_2024() { region::ScopeData::IfThenRescope @@ -1013,7 +1027,7 @@ impl<'tcx> Cx<'tcx> { guard: arm.guard.as_ref().map(|g| self.mirror_expr(g)), body: self.mirror_expr(arm.body), lint_level: LintLevel::Explicit(arm.hir_id), - scope: region::Scope { id: arm.hir_id.local_id, data: region::ScopeData::Node }, + scope: region::Scope { local_id: arm.hir_id.local_id, data: region::ScopeData::Node }, span: arm.span, }; self.thir.arms.push(arm) @@ -1189,7 +1203,7 @@ impl<'tcx> Cx<'tcx> { .temporary_scope(self.region_scope_tree, closure_expr.hir_id.local_id); let var_ty = place.base_ty; - // The result of capture analysis in `rustc_hir_analysis/check/upvar.rs`represents a captured path + // The result of capture analysis in `rustc_hir_typeck/src/upvar.rs` represents a captured path // as it's seen for use within the closure and not at the time of closure creation. // // That is we see expect to see it start from a captured upvar and not something that is local diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 2dbc8b7b573..bdf243c87b6 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<Rust2024IncompatiblePatSugg>, + rust_2024_migration_suggestion: Option<Rust2024IncompatiblePatSugg<'a>>, } 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/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs index 9d943ebe327..6427fd0fd29 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -1,8 +1,6 @@ use std::ops::RangeInclusive; -use rustc_middle::mir::{ - self, BasicBlock, CallReturnPlaces, Location, SwitchTargets, TerminatorEdges, -}; +use rustc_middle::mir::{self, BasicBlock, CallReturnPlaces, Location, TerminatorEdges}; use super::visitor::ResultsVisitor; use super::{Analysis, Effect, EffectIndex, Results, SwitchIntTarget}; @@ -78,8 +76,6 @@ impl Direction for Backward { for pred in body.basic_blocks.predecessors()[block].iter().copied() { match body[pred].terminator().kind { // Apply terminator-specific edge effects. - // - // FIXME(ecstaticmorse): Avoid cloning the exit state unconditionally. mir::TerminatorKind::Call { destination, target: Some(dest), .. } if dest == block => { @@ -115,18 +111,18 @@ impl Direction for Backward { } mir::TerminatorKind::SwitchInt { targets: _, ref discr } => { - let mut applier = BackwardSwitchIntEdgeEffectsApplier { - body, - pred, - exit_state, - block, - propagate: &mut propagate, - effects_applied: false, - }; - - analysis.apply_switch_int_edge_effects(pred, discr, &mut applier); - - if !applier.effects_applied { + if let Some(mut data) = analysis.get_switch_int_data(block, discr) { + let values = &body.basic_blocks.switch_sources()[&(block, pred)]; + let targets = + values.iter().map(|&value| SwitchIntTarget { value, target: block }); + + let mut tmp = analysis.bottom_value(body); + for target in targets { + tmp.clone_from(&exit_state); + analysis.apply_switch_int_edge_effect(&mut data, &mut tmp, target); + propagate(pred, &tmp); + } + } else { propagate(pred, exit_state) } } @@ -245,37 +241,6 @@ impl Direction for Backward { } } -struct BackwardSwitchIntEdgeEffectsApplier<'mir, 'tcx, D, F> { - body: &'mir mir::Body<'tcx>, - pred: BasicBlock, - exit_state: &'mir mut D, - block: BasicBlock, - propagate: &'mir mut F, - effects_applied: bool, -} - -impl<D, F> super::SwitchIntEdgeEffects<D> for BackwardSwitchIntEdgeEffectsApplier<'_, '_, D, F> -where - D: Clone, - F: FnMut(BasicBlock, &D), -{ - fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) { - assert!(!self.effects_applied); - - let values = &self.body.basic_blocks.switch_sources()[&(self.block, self.pred)]; - let targets = values.iter().map(|&value| SwitchIntTarget { value, target: self.block }); - - let mut tmp = None; - for target in targets { - let tmp = opt_clone_from_or_clone(&mut tmp, self.exit_state); - apply_edge_effect(tmp, target); - (self.propagate)(self.pred, tmp); - } - - self.effects_applied = true; - } -} - /// Dataflow that runs from the entry of a block (the first statement), to its exit (terminator). pub struct Forward; @@ -284,7 +249,7 @@ impl Direction for Forward { fn apply_effects_in_block<'mir, 'tcx, A>( analysis: &mut A, - _body: &mir::Body<'tcx>, + body: &mir::Body<'tcx>, state: &mut A::Domain, block: BasicBlock, block_data: &'mir mir::BasicBlockData<'tcx>, @@ -324,23 +289,28 @@ impl Direction for Forward { } } TerminatorEdges::SwitchInt { targets, discr } => { - let mut applier = ForwardSwitchIntEdgeEffectsApplier { - exit_state, - targets, - propagate, - effects_applied: false, - }; - - analysis.apply_switch_int_edge_effects(block, discr, &mut applier); - - let ForwardSwitchIntEdgeEffectsApplier { - exit_state, - mut propagate, - effects_applied, - .. - } = applier; - - if !effects_applied { + if let Some(mut data) = analysis.get_switch_int_data(block, discr) { + let mut tmp = analysis.bottom_value(body); + for (value, target) in targets.iter() { + tmp.clone_from(&exit_state); + analysis.apply_switch_int_edge_effect( + &mut data, + &mut tmp, + SwitchIntTarget { value: Some(value), target }, + ); + propagate(target, &tmp); + } + + // Once we get to the final, "otherwise" branch, there is no need to preserve + // `exit_state`, so pass it directly to `apply_switch_int_edge_effect` to save + // a clone of the dataflow state. + let otherwise = targets.otherwise(); + analysis.apply_switch_int_edge_effect(&mut data, exit_state, SwitchIntTarget { + value: None, + target: otherwise, + }); + propagate(otherwise, exit_state); + } else { for target in targets.all_targets() { propagate(*target, exit_state); } @@ -454,54 +424,3 @@ impl Direction for Forward { vis.visit_block_end(state); } } - -struct ForwardSwitchIntEdgeEffectsApplier<'mir, D, F> { - exit_state: &'mir mut D, - targets: &'mir SwitchTargets, - propagate: F, - - effects_applied: bool, -} - -impl<D, F> super::SwitchIntEdgeEffects<D> for ForwardSwitchIntEdgeEffectsApplier<'_, D, F> -where - D: Clone, - F: FnMut(BasicBlock, &D), -{ - fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) { - assert!(!self.effects_applied); - - let mut tmp = None; - for (value, target) in self.targets.iter() { - let tmp = opt_clone_from_or_clone(&mut tmp, self.exit_state); - apply_edge_effect(tmp, SwitchIntTarget { value: Some(value), target }); - (self.propagate)(target, tmp); - } - - // Once we get to the final, "otherwise" branch, there is no need to preserve `exit_state`, - // so pass it directly to `apply_edge_effect` to save a clone of the dataflow state. - let otherwise = self.targets.otherwise(); - apply_edge_effect(self.exit_state, SwitchIntTarget { value: None, target: otherwise }); - (self.propagate)(otherwise, self.exit_state); - - self.effects_applied = true; - } -} - -/// An analogue of `Option::get_or_insert_with` that stores a clone of `val` into `opt`, but uses -/// the more efficient `clone_from` if `opt` was `Some`. -/// -/// Returns a mutable reference to the new clone that resides in `opt`. -// -// FIXME: Figure out how to express this using `Option::clone_from`, or maybe lift it into the -// standard library? -fn opt_clone_from_or_clone<'a, T: Clone>(opt: &'a mut Option<T>, val: &T) -> &'a mut T { - if opt.is_some() { - let ret = opt.as_mut().unwrap(); - ret.clone_from(val); - ret - } else { - *opt = Some(val.clone()); - opt.as_mut().unwrap() - } -} diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index 5b2b128e035..60c97710c7f 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -16,7 +16,7 @@ use rustc_middle::mir::{ }; use rustc_middle::ty::TyCtxt; use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::{Symbol, sym}; use tracing::debug; use {rustc_ast as ast, rustc_graphviz as dot}; diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs index 41df5fae0de..3de2c6e3f47 100644 --- a/compiler/rustc_mir_dataflow/src/framework/mod.rs +++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs @@ -103,6 +103,9 @@ pub trait Analysis<'tcx> { /// The direction of this analysis. Either `Forward` or `Backward`. type Direction: Direction = Forward; + /// Auxiliary data used for analyzing `SwitchInt` terminators, if necessary. + type SwitchIntData = !; + /// A descriptive name for this analysis. Used only for debugging. /// /// This name should be brief and contain no spaces, periods or other characters that are not @@ -190,25 +193,36 @@ pub trait Analysis<'tcx> { ) { } - /// Updates the current dataflow state with the effect of taking a particular branch in a - /// `SwitchInt` terminator. + /// Used to update the current dataflow state with the effect of taking a particular branch in + /// a `SwitchInt` terminator. /// /// Unlike the other edge-specific effects, which are allowed to mutate `Self::Domain` - /// directly, overriders of this method must pass a callback to - /// `SwitchIntEdgeEffects::apply`. The callback will be run once for each outgoing edge and - /// will have access to the dataflow state that will be propagated along that edge. + /// directly, overriders of this method must return a `Self::SwitchIntData` value (wrapped in + /// `Some`). The `apply_switch_int_edge_effect` method will then be called once for each + /// outgoing edge and will have access to the dataflow state that will be propagated along that + /// edge, and also the `Self::SwitchIntData` value. /// /// This interface is somewhat more complex than the other visitor-like "effect" methods. /// However, it is both more ergonomic—callers don't need to recompute or cache information /// about a given `SwitchInt` terminator for each one of its edges—and more efficient—the /// engine doesn't need to clone the exit state for a block unless - /// `SwitchIntEdgeEffects::apply` is actually called. - fn apply_switch_int_edge_effects( + /// `get_switch_int_data` is actually called. + fn get_switch_int_data( &mut self, - _block: BasicBlock, + _block: mir::BasicBlock, _discr: &mir::Operand<'tcx>, - _apply_edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>, + ) -> Option<Self::SwitchIntData> { + None + } + + /// See comments on `get_switch_int_data`. + fn apply_switch_int_edge_effect( + &mut self, + _data: &mut Self::SwitchIntData, + _state: &mut Self::Domain, + _edge: SwitchIntTarget, ) { + unreachable!(); } /* Extension methods */ @@ -421,12 +435,5 @@ pub struct SwitchIntTarget { pub target: BasicBlock, } -/// A type that records the edge-specific effects for a `SwitchInt` terminator. -pub trait SwitchIntEdgeEffects<D> { - /// Calls `apply_edge_effect` for each outgoing edge from a `SwitchInt` terminator and - /// records the results. - fn apply(&mut self, apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)); -} - #[cfg(test)] mod tests; diff --git a/compiler/rustc_mir_dataflow/src/framework/visitor.rs b/compiler/rustc_mir_dataflow/src/framework/visitor.rs index d18e9fa33f0..a03aecee7be 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_dataflow/src/impls/initialized.rs b/compiler/rustc_mir_dataflow/src/impls/initialized.rs index fb02408e17d..769f9c7cfc3 100644 --- a/compiler/rustc_mir_dataflow/src/impls/initialized.rs +++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs @@ -1,20 +1,96 @@ use std::assert_matches::assert_matches; +use rustc_abi::VariantIdx; use rustc_index::Idx; use rustc_index::bit_set::{BitSet, MixedBitSet}; use rustc_middle::bug; use rustc_middle::mir::{self, Body, CallReturnPlaces, Location, TerminatorEdges}; +use rustc_middle::ty::util::Discr; use rustc_middle::ty::{self, TyCtxt}; use tracing::{debug, instrument}; use crate::elaborate_drops::DropFlagState; -use crate::framework::SwitchIntEdgeEffects; +use crate::framework::SwitchIntTarget; use crate::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex}; use crate::{ Analysis, GenKill, MaybeReachable, drop_flag_effects, drop_flag_effects_for_function_entry, drop_flag_effects_for_location, on_all_children_bits, on_lookup_result_bits, }; +// Used by both `MaybeInitializedPlaces` and `MaybeUninitializedPlaces`. +pub struct MaybePlacesSwitchIntData<'tcx> { + enum_place: mir::Place<'tcx>, + discriminants: Vec<(VariantIdx, Discr<'tcx>)>, + index: usize, +} + +impl<'tcx> MaybePlacesSwitchIntData<'tcx> { + // The discriminant order in the `SwitchInt` targets should match the order yielded by + // `AdtDef::discriminants`. We rely on this to match each discriminant in the targets to its + // corresponding variant in linear time. + fn next_discr(&mut self, value: u128) -> VariantIdx { + // An out-of-bounds abort will occur if the discriminant ordering isn't as described above. + loop { + let (variant, discr) = self.discriminants[self.index]; + self.index += 1; + if discr.val == value { + return variant; + } + } + } +} + +impl<'tcx> MaybePlacesSwitchIntData<'tcx> { + fn new( + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + block: mir::BasicBlock, + discr: &mir::Operand<'tcx>, + ) -> Option<Self> { + let Some(discr) = discr.place() else { return None }; + + // Inspect a `SwitchInt`-terminated basic block to see if the condition of that `SwitchInt` + // is an enum discriminant. + // + // We expect such blocks to have a call to `discriminant` as their last statement like so: + // ```text + // ... + // _42 = discriminant(_1) + // SwitchInt(_42, ..) + // ``` + // If the basic block matches this pattern, this function gathers the place corresponding + // to the enum (`_1` in the example above) as well as the discriminants. + let block_data = &body[block]; + for statement in block_data.statements.iter().rev() { + match statement.kind { + mir::StatementKind::Assign(box (lhs, mir::Rvalue::Discriminant(enum_place))) + if lhs == discr => + { + match enum_place.ty(body, tcx).ty.kind() { + ty::Adt(enum_def, _) => { + return Some(MaybePlacesSwitchIntData { + enum_place, + discriminants: enum_def.discriminants(tcx).collect(), + index: 0, + }); + } + + // `Rvalue::Discriminant` is also used to get the active yield point for a + // coroutine, but we do not need edge-specific effects in that case. This + // may change in the future. + ty::Coroutine(..) => break, + + t => bug!("`discriminant` called on unexpected type {:?}", t), + } + } + mir::StatementKind::Coverage(_) => continue, + _ => break, + } + } + None + } +} + /// `MaybeInitializedPlaces` tracks all places that might be /// initialized upon reaching a particular point in the control flow /// for a function. @@ -247,6 +323,8 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { /// We use a mixed bitset to avoid paying too high a memory footprint. type Domain = MaybeReachable<MixedBitSet<MovePathIndex>>; + type SwitchIntData = MaybePlacesSwitchIntData<'tcx>; + const NAME: &'static str = "maybe_init"; fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { @@ -293,6 +371,8 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { terminator: &'mir mir::Terminator<'tcx>, location: Location, ) -> TerminatorEdges<'mir, 'tcx> { + // Note: `edges` must be computed first because `drop_flag_effects_for_location` can change + // the result of `is_unwind_dead`. let mut edges = terminator.edges(); if self.skip_unreachable_unwind && let mir::TerminatorKind::Drop { target, unwind, place, replace: _ } = terminator.kind @@ -326,46 +406,34 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { }); } - fn apply_switch_int_edge_effects( + fn get_switch_int_data( &mut self, block: mir::BasicBlock, discr: &mir::Operand<'tcx>, - edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>, - ) { + ) -> Option<Self::SwitchIntData> { if !self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration { - return; + return None; } - let enum_ = discr.place().and_then(|discr| { - switch_on_enum_discriminant(self.tcx, self.body, &self.body[block], discr) - }); - - let Some((enum_place, enum_def)) = enum_ else { - return; - }; - - let mut discriminants = enum_def.discriminants(self.tcx); - edge_effects.apply(|state, edge| { - let Some(value) = edge.value else { - return; - }; - - // MIR building adds discriminants to the `values` array in the same order as they - // are yielded by `AdtDef::discriminants`. We rely on this to match each - // discriminant in `values` to its corresponding variant in linear time. - let (variant, _) = discriminants - .find(|&(_, discr)| discr.val == value) - .expect("Order of `AdtDef::discriminants` differed from `SwitchInt::values`"); + MaybePlacesSwitchIntData::new(self.tcx, self.body, block, discr) + } + fn apply_switch_int_edge_effect( + &mut self, + data: &mut Self::SwitchIntData, + state: &mut Self::Domain, + edge: SwitchIntTarget, + ) { + if let Some(value) = edge.value { // Kill all move paths that correspond to variants we know to be inactive along this // particular outgoing edge of a `SwitchInt`. drop_flag_effects::on_all_inactive_variants( - self.move_data(), - enum_place, - variant, + self.move_data, + data.enum_place, + data.next_discr(value), |mpi| state.kill(mpi), ); - }); + } } } @@ -376,6 +444,8 @@ pub type MaybeUninitializedPlacesDomain = MixedBitSet<MovePathIndex>; impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { type Domain = MaybeUninitializedPlacesDomain; + type SwitchIntData = MaybePlacesSwitchIntData<'tcx>; + const NAME: &'static str = "maybe_uninit"; fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { @@ -445,50 +515,38 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { }); } - fn apply_switch_int_edge_effects( + fn get_switch_int_data( &mut self, block: mir::BasicBlock, discr: &mir::Operand<'tcx>, - edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>, - ) { + ) -> Option<Self::SwitchIntData> { if !self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration { - return; + return None; } if !self.mark_inactive_variants_as_uninit { - return; + return None; } - let enum_ = discr.place().and_then(|discr| { - switch_on_enum_discriminant(self.tcx, self.body, &self.body[block], discr) - }); - - let Some((enum_place, enum_def)) = enum_ else { - return; - }; - - let mut discriminants = enum_def.discriminants(self.tcx); - edge_effects.apply(|state, edge| { - let Some(value) = edge.value else { - return; - }; - - // MIR building adds discriminants to the `values` array in the same order as they - // are yielded by `AdtDef::discriminants`. We rely on this to match each - // discriminant in `values` to its corresponding variant in linear time. - let (variant, _) = discriminants - .find(|&(_, discr)| discr.val == value) - .expect("Order of `AdtDef::discriminants` differed from `SwitchInt::values`"); + MaybePlacesSwitchIntData::new(self.tcx, self.body, block, discr) + } + fn apply_switch_int_edge_effect( + &mut self, + data: &mut Self::SwitchIntData, + state: &mut Self::Domain, + edge: SwitchIntTarget, + ) { + if let Some(value) = edge.value { // Mark all move paths that correspond to variants other than this one as maybe // uninitialized (in reality, they are *definitely* uninitialized). drop_flag_effects::on_all_inactive_variants( - self.move_data(), - enum_place, - variant, + self.move_data, + data.enum_place, + data.next_discr(value), |mpi| state.gen_(mpi), ); - }); + } } } @@ -578,45 +636,3 @@ impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { } } } - -/// Inspect a `SwitchInt`-terminated basic block to see if the condition of that `SwitchInt` is -/// an enum discriminant. -/// -/// We expect such blocks to have a call to `discriminant` as their last statement like so: -/// -/// ```text -/// ... -/// _42 = discriminant(_1) -/// SwitchInt(_42, ..) -/// ``` -/// -/// If the basic block matches this pattern, this function returns the place corresponding to the -/// enum (`_1` in the example above) as well as the `AdtDef` of that enum. -fn switch_on_enum_discriminant<'mir, 'tcx>( - tcx: TyCtxt<'tcx>, - body: &'mir mir::Body<'tcx>, - block: &'mir mir::BasicBlockData<'tcx>, - switch_on: mir::Place<'tcx>, -) -> Option<(mir::Place<'tcx>, ty::AdtDef<'tcx>)> { - for statement in block.statements.iter().rev() { - match &statement.kind { - mir::StatementKind::Assign(box (lhs, mir::Rvalue::Discriminant(discriminated))) - if *lhs == switch_on => - { - match discriminated.ty(body, tcx).ty.kind() { - ty::Adt(def, _) => return Some((*discriminated, *def)), - - // `Rvalue::Discriminant` is also used to get the active yield point for a - // coroutine, but we do not need edge-specific effects in that case. This may - // change in the future. - ty::Coroutine(..) => return None, - - t => bug!("`discriminant` called on unexpected type {:?}", t), - } - } - mir::StatementKind::Coverage(_) => continue, - _ => return None, - } - } - None -} diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index 85255db5d9a..0cc79b0c939 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -5,6 +5,7 @@ #![feature(exact_size_is_empty)] #![feature(file_buffered)] #![feature(let_chains)] +#![feature(never_type)] #![feature(try_blocks)] #![warn(unreachable_pub)] // tidy-alphabetical-end @@ -19,8 +20,7 @@ pub use self::drop_flag_effects::{ }; pub use self::framework::{ Analysis, Backward, Direction, EntryStates, Forward, GenKill, JoinSemiLattice, MaybeReachable, - Results, ResultsCursor, ResultsVisitor, SwitchIntEdgeEffects, fmt, graphviz, lattice, - visit_results, + Results, ResultsCursor, ResultsVisitor, fmt, graphviz, lattice, visit_results, }; use self::move_paths::MoveData; diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs index 85cf8ca2104..399141aa921 100644 --- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs +++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs @@ -2,8 +2,7 @@ use rustc_ast::MetaItem; use rustc_hir::def_id::DefId; use rustc_middle::mir::{self, Body, Local, Location}; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::Span; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::{Span, Symbol, sym}; use tracing::{debug, info}; use crate::errors::{ diff --git a/compiler/rustc_mir_transform/Cargo.toml b/compiler/rustc_mir_transform/Cargo.toml index 4b648d21084..2f233f787f0 100644 --- a/compiler/rustc_mir_transform/Cargo.toml +++ b/compiler/rustc_mir_transform/Cargo.toml @@ -10,7 +10,7 @@ itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_const_eval = { path = "../rustc_const_eval" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 31d5245fb5c..a3320f99cc3 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -73,9 +73,8 @@ use rustc_mir_dataflow::impls::{ always_storage_live_locals, }; use rustc_mir_dataflow::{Analysis, Results, ResultsVisitor}; -use rustc_span::Span; use rustc_span::def_id::{DefId, LocalDefId}; -use rustc_span::symbol::sym; +use rustc_span::{Span, sym}; use rustc_target::spec::PanicStrategy; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::TyCtxtInferExt as _; diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs index ef61866e902..9f5bb015fa3 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -78,7 +78,7 @@ use rustc_middle::hir::place::{Projection, ProjectionKind}; use rustc_middle::mir::visit::MutVisitor; use rustc_middle::mir::{self, dump_mir}; use rustc_middle::ty::{self, InstanceKind, Ty, TyCtxt, TypeVisitableExt}; -use rustc_span::symbol::kw; +use rustc_span::kw; pub(crate) fn coroutine_by_move_body_def_id<'tcx>( tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_mir_transform/src/coverage/mappings.rs b/compiler/rustc_mir_transform/src/coverage/mappings.rs index 2db7c6cf1d6..4185b3f4d4d 100644 --- a/compiler/rustc_mir_transform/src/coverage/mappings.rs +++ b/compiler/rustc_mir_transform/src/coverage/mappings.rs @@ -80,7 +80,7 @@ pub(super) fn extract_all_mapping_info_from_mir<'tcx>( tcx: TyCtxt<'tcx>, mir_body: &mir::Body<'tcx>, hir_info: &ExtractedHirInfo, - basic_coverage_blocks: &CoverageGraph, + graph: &CoverageGraph, ) -> ExtractedMappings { let mut code_mappings = vec![]; let mut branch_pairs = vec![]; @@ -102,23 +102,23 @@ pub(super) fn extract_all_mapping_info_from_mir<'tcx>( } } else { // Extract coverage spans from MIR statements/terminators as normal. - extract_refined_covspans(mir_body, hir_info, basic_coverage_blocks, &mut code_mappings); + extract_refined_covspans(mir_body, hir_info, graph, &mut code_mappings); } - branch_pairs.extend(extract_branch_pairs(mir_body, hir_info, basic_coverage_blocks)); + branch_pairs.extend(extract_branch_pairs(mir_body, hir_info, graph)); extract_mcdc_mappings( mir_body, tcx, hir_info.body_span, - basic_coverage_blocks, + graph, &mut mcdc_bitmap_bits, &mut mcdc_degraded_branches, &mut mcdc_mappings, ); ExtractedMappings { - num_bcbs: basic_coverage_blocks.num_nodes(), + num_bcbs: graph.num_nodes(), code_mappings, branch_pairs, mcdc_bitmap_bits, @@ -211,7 +211,7 @@ fn resolve_block_markers( pub(super) fn extract_branch_pairs( mir_body: &mir::Body<'_>, hir_info: &ExtractedHirInfo, - basic_coverage_blocks: &CoverageGraph, + graph: &CoverageGraph, ) -> Vec<BranchPair> { let Some(coverage_info_hi) = mir_body.coverage_info_hi.as_deref() else { return vec![] }; @@ -228,8 +228,7 @@ pub(super) fn extract_branch_pairs( } let span = unexpand_into_body_span(raw_span, hir_info.body_span)?; - let bcb_from_marker = - |marker: BlockMarkerId| basic_coverage_blocks.bcb_from_bb(block_markers[marker]?); + let bcb_from_marker = |marker: BlockMarkerId| graph.bcb_from_bb(block_markers[marker]?); let true_bcb = bcb_from_marker(true_marker)?; let false_bcb = bcb_from_marker(false_marker)?; @@ -243,7 +242,7 @@ pub(super) fn extract_mcdc_mappings( mir_body: &mir::Body<'_>, tcx: TyCtxt<'_>, body_span: Span, - basic_coverage_blocks: &CoverageGraph, + graph: &CoverageGraph, mcdc_bitmap_bits: &mut usize, mcdc_degraded_branches: &mut impl Extend<MCDCBranch>, mcdc_mappings: &mut impl Extend<(MCDCDecision, Vec<MCDCBranch>)>, @@ -252,8 +251,7 @@ pub(super) fn extract_mcdc_mappings( let block_markers = resolve_block_markers(coverage_info_hi, mir_body); - let bcb_from_marker = - |marker: BlockMarkerId| basic_coverage_blocks.bcb_from_bb(block_markers[marker]?); + let bcb_from_marker = |marker: BlockMarkerId| graph.bcb_from_bb(block_markers[marker]?); let check_branch_bcb = |raw_span: Span, true_marker: BlockMarkerId, false_marker: BlockMarkerId| { diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 241c3c79114..57956448414 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -13,16 +13,15 @@ use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter; use rustc_middle::mir::coverage::{ - CoverageKind, DecisionInfo, FunctionCoverageInfo, Mapping, MappingKind, SourceRegion, + CoverageKind, DecisionInfo, FunctionCoverageInfo, Mapping, MappingKind, }; use rustc_middle::mir::{ self, BasicBlock, BasicBlockData, SourceInfo, Statement, StatementKind, Terminator, TerminatorKind, }; use rustc_middle::ty::TyCtxt; +use rustc_span::Span; use rustc_span::def_id::LocalDefId; -use rustc_span::source_map::SourceMap; -use rustc_span::{BytePos, Pos, SourceFile, Span}; use tracing::{debug, debug_span, trace}; use crate::coverage::counters::{CoverageCounters, Site}; @@ -72,16 +71,15 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir: let _span = debug_span!("instrument_function_for_coverage", ?def_id).entered(); let hir_info = extract_hir_info(tcx, def_id.expect_local()); - let basic_coverage_blocks = CoverageGraph::from_mir(mir_body); + + // Build the coverage graph, which is a simplified view of the MIR control-flow + // graph that ignores some details not relevant to coverage instrumentation. + let graph = CoverageGraph::from_mir(mir_body); //////////////////////////////////////////////////// // Extract coverage spans and other mapping info from MIR. - let extracted_mappings = mappings::extract_all_mapping_info_from_mir( - tcx, - mir_body, - &hir_info, - &basic_coverage_blocks, - ); + let extracted_mappings = + mappings::extract_all_mapping_info_from_mir(tcx, mir_body, &hir_info, &graph); //////////////////////////////////////////////////// // Create an optimized mix of `Counter`s and `Expression`s for the `CoverageGraph`. Ensure @@ -95,23 +93,18 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir: } let coverage_counters = - CoverageCounters::make_bcb_counters(&basic_coverage_blocks, &bcbs_with_counter_mappings); + CoverageCounters::make_bcb_counters(&graph, &bcbs_with_counter_mappings); - let mappings = create_mappings(tcx, &hir_info, &extracted_mappings, &coverage_counters); + let mappings = create_mappings(&extracted_mappings, &coverage_counters); if mappings.is_empty() { // No spans could be converted into valid mappings, so skip this function. debug!("no spans could be converted into valid mappings; skipping"); return; } - inject_coverage_statements( - mir_body, - &basic_coverage_blocks, - &extracted_mappings, - &coverage_counters, - ); + inject_coverage_statements(mir_body, &graph, &extracted_mappings, &coverage_counters); - inject_mcdc_statements(mir_body, &basic_coverage_blocks, &extracted_mappings); + inject_mcdc_statements(mir_body, &graph, &extracted_mappings); let mcdc_num_condition_bitmaps = extracted_mappings .mcdc_mappings @@ -136,18 +129,12 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir: /// /// Precondition: All BCBs corresponding to those spans have been given /// coverage counters. -fn create_mappings<'tcx>( - tcx: TyCtxt<'tcx>, - hir_info: &ExtractedHirInfo, +fn create_mappings( extracted_mappings: &ExtractedMappings, coverage_counters: &CoverageCounters, ) -> Vec<Mapping> { - let source_map = tcx.sess.source_map(); - let file = source_map.lookup_source_file(hir_info.body_span.lo()); - let term_for_bcb = |bcb| coverage_counters.term_for_bcb(bcb).expect("all BCBs with spans were given counters"); - let region_for_span = |span: Span| make_source_region(source_map, hir_info, &file, span); // Fully destructure the mappings struct to make sure we don't miss any kinds. let ExtractedMappings { @@ -160,22 +147,20 @@ fn create_mappings<'tcx>( } = extracted_mappings; let mut mappings = Vec::new(); - mappings.extend(code_mappings.iter().filter_map( + mappings.extend(code_mappings.iter().map( // Ordinary code mappings are the simplest kind. |&mappings::CodeMapping { span, bcb }| { - let source_region = region_for_span(span)?; let kind = MappingKind::Code(term_for_bcb(bcb)); - Some(Mapping { kind, source_region }) + Mapping { kind, span } }, )); - mappings.extend(branch_pairs.iter().filter_map( + mappings.extend(branch_pairs.iter().map( |&mappings::BranchPair { span, true_bcb, false_bcb }| { let true_term = term_for_bcb(true_bcb); let false_term = term_for_bcb(false_bcb); let kind = MappingKind::Branch { true_term, false_term }; - let source_region = region_for_span(span)?; - Some(Mapping { kind, source_region }) + Mapping { kind, span } }, )); @@ -183,7 +168,7 @@ fn create_mappings<'tcx>( |bcb| coverage_counters.term_for_bcb(bcb).expect("all BCBs with spans were given counters"); // MCDC branch mappings are appended with their decisions in case decisions were ignored. - mappings.extend(mcdc_degraded_branches.iter().filter_map( + mappings.extend(mcdc_degraded_branches.iter().map( |&mappings::MCDCBranch { span, true_bcb, @@ -192,10 +177,9 @@ fn create_mappings<'tcx>( true_index: _, false_index: _, }| { - let source_region = region_for_span(span)?; let true_term = term_for_bcb(true_bcb); let false_term = term_for_bcb(false_bcb); - Some(Mapping { kind: MappingKind::Branch { true_term, false_term }, source_region }) + Mapping { kind: MappingKind::Branch { true_term, false_term }, span } }, )); @@ -203,7 +187,7 @@ fn create_mappings<'tcx>( let num_conditions = branches.len() as u16; let conditions = branches .into_iter() - .filter_map( + .map( |&mappings::MCDCBranch { span, true_bcb, @@ -212,32 +196,28 @@ fn create_mappings<'tcx>( true_index: _, false_index: _, }| { - let source_region = region_for_span(span)?; let true_term = term_for_bcb(true_bcb); let false_term = term_for_bcb(false_bcb); - Some(Mapping { + Mapping { kind: MappingKind::MCDCBranch { true_term, false_term, mcdc_params: condition_info, }, - source_region, - }) + span, + } }, ) .collect::<Vec<_>>(); - if conditions.len() == num_conditions as usize - && let Some(source_region) = region_for_span(decision.span) - { + if conditions.len() == num_conditions as usize { // LLVM requires end index for counter mapping regions. let kind = MappingKind::MCDCDecision(DecisionInfo { bitmap_idx: (decision.bitmap_idx + decision.num_test_vectors) as u32, num_conditions, }); - mappings.extend( - std::iter::once(Mapping { kind, source_region }).chain(conditions.into_iter()), - ); + let span = decision.span; + mappings.extend(std::iter::once(Mapping { kind, span }).chain(conditions.into_iter())); } else { mappings.extend(conditions.into_iter().map(|mapping| { let MappingKind::MCDCBranch { true_term, false_term, mcdc_params: _ } = @@ -245,10 +225,7 @@ fn create_mappings<'tcx>( else { unreachable!("all mappings here are MCDCBranch as shown above"); }; - Mapping { - kind: MappingKind::Branch { true_term, false_term }, - source_region: mapping.source_region, - } + Mapping { kind: MappingKind::Branch { true_term, false_term }, span: mapping.span } })) } } @@ -260,7 +237,7 @@ fn create_mappings<'tcx>( /// inject any necessary coverage statements into MIR. fn inject_coverage_statements<'tcx>( mir_body: &mut mir::Body<'tcx>, - basic_coverage_blocks: &CoverageGraph, + graph: &CoverageGraph, extracted_mappings: &ExtractedMappings, coverage_counters: &CoverageCounters, ) { @@ -270,12 +247,12 @@ fn inject_coverage_statements<'tcx>( // For BCB nodes this is just their first block, but for edges we need // to create a new block between the two BCBs, and inject into that. let target_bb = match site { - Site::Node { bcb } => basic_coverage_blocks[bcb].leader_bb(), + Site::Node { bcb } => graph[bcb].leader_bb(), Site::Edge { from_bcb, to_bcb } => { // Create a new block between the last block of `from_bcb` and // the first block of `to_bcb`. - let from_bb = basic_coverage_blocks[from_bcb].last_bb(); - let to_bb = basic_coverage_blocks[to_bcb].leader_bb(); + let from_bb = graph[from_bcb].last_bb(); + let to_bb = graph[to_bcb].leader_bb(); let new_bb = inject_edge_counter_basic_block(mir_body, from_bb, to_bb); debug!( @@ -308,7 +285,7 @@ fn inject_coverage_statements<'tcx>( inject_statement( mir_body, CoverageKind::ExpressionUsed { id: expression_id }, - basic_coverage_blocks[bcb].leader_bb(), + graph[bcb].leader_bb(), ); } } @@ -317,13 +294,13 @@ fn inject_coverage_statements<'tcx>( /// For each decision inject statements to update test vector bitmap after it has been evaluated. fn inject_mcdc_statements<'tcx>( mir_body: &mut mir::Body<'tcx>, - basic_coverage_blocks: &CoverageGraph, + graph: &CoverageGraph, extracted_mappings: &ExtractedMappings, ) { for (decision, conditions) in &extracted_mappings.mcdc_mappings { // Inject test vector update first because `inject_statement` always insert new statement at head. for &end in &decision.end_bcbs { - let end_bb = basic_coverage_blocks[end].leader_bb(); + let end_bb = graph[end].leader_bb(); inject_statement( mir_body, CoverageKind::TestVectorBitmapUpdate { @@ -344,7 +321,7 @@ fn inject_mcdc_statements<'tcx>( } in conditions { for (index, bcb) in [(false_index, false_bcb), (true_index, true_bcb)] { - let bb = basic_coverage_blocks[bcb].leader_bb(); + let bb = graph[bcb].leader_bb(); inject_statement( mir_body, CoverageKind::CondBitmapUpdate { @@ -391,121 +368,6 @@ fn inject_statement(mir_body: &mut mir::Body<'_>, counter_kind: CoverageKind, bb data.statements.insert(0, statement); } -fn ensure_non_empty_span( - source_map: &SourceMap, - hir_info: &ExtractedHirInfo, - span: Span, -) -> Option<Span> { - if !span.is_empty() { - return Some(span); - } - - let lo = span.lo(); - let hi = span.hi(); - - // The span is empty, so try to expand it to cover an adjacent '{' or '}', - // but only within the bounds of the body span. - let try_next = hi < hir_info.body_span.hi(); - let try_prev = hir_info.body_span.lo() < lo; - if !(try_next || try_prev) { - return None; - } - - source_map - .span_to_source(span, |src, start, end| try { - // We're only checking for specific ASCII characters, so we don't - // have to worry about multi-byte code points. - if try_next && src.as_bytes()[end] == b'{' { - Some(span.with_hi(hi + BytePos(1))) - } else if try_prev && src.as_bytes()[start - 1] == b'}' { - Some(span.with_lo(lo - BytePos(1))) - } else { - None - } - }) - .ok()? -} - -/// Converts the span into its start line and column, and end line and column. -/// -/// Line numbers and column numbers are 1-based. Unlike most column numbers emitted by -/// the compiler, these column numbers are denoted in **bytes**, because that's what -/// LLVM's `llvm-cov` tool expects to see in coverage maps. -/// -/// Returns `None` if the conversion failed for some reason. This shouldn't happen, -/// but it's hard to rule out entirely (especially in the presence of complex macros -/// or other expansions), and if it does happen then skipping a span or function is -/// better than an ICE or `llvm-cov` failure that the user might have no way to avoid. -fn make_source_region( - source_map: &SourceMap, - hir_info: &ExtractedHirInfo, - file: &SourceFile, - span: Span, -) -> Option<SourceRegion> { - let span = ensure_non_empty_span(source_map, hir_info, span)?; - - let lo = span.lo(); - let hi = span.hi(); - - // Column numbers need to be in bytes, so we can't use the more convenient - // `SourceMap` methods for looking up file coordinates. - let line_and_byte_column = |pos: BytePos| -> Option<(usize, usize)> { - let rpos = file.relative_position(pos); - let line_index = file.lookup_line(rpos)?; - let line_start = file.lines()[line_index]; - // Line numbers and column numbers are 1-based, so add 1 to each. - Some((line_index + 1, (rpos - line_start).to_usize() + 1)) - }; - - let (mut start_line, start_col) = line_and_byte_column(lo)?; - let (mut end_line, end_col) = line_and_byte_column(hi)?; - - // Apply an offset so that code in doctests has correct line numbers. - // FIXME(#79417): Currently we have no way to offset doctest _columns_. - start_line = source_map.doctest_offset_line(&file.name, start_line); - end_line = source_map.doctest_offset_line(&file.name, end_line); - - check_source_region(SourceRegion { - start_line: start_line as u32, - start_col: start_col as u32, - end_line: end_line as u32, - end_col: end_col as u32, - }) -} - -/// If `llvm-cov` sees a source region that is improperly ordered (end < start), -/// it will immediately exit with a fatal error. To prevent that from happening, -/// discard regions that are improperly ordered, or might be interpreted in a -/// way that makes them improperly ordered. -fn check_source_region(source_region: SourceRegion) -> Option<SourceRegion> { - let SourceRegion { start_line, start_col, end_line, end_col } = source_region; - - // Line/column coordinates are supposed to be 1-based. If we ever emit - // coordinates of 0, `llvm-cov` might misinterpret them. - let all_nonzero = [start_line, start_col, end_line, end_col].into_iter().all(|x| x != 0); - // Coverage mappings use the high bit of `end_col` to indicate that a - // region is actually a "gap" region, so make sure it's unset. - let end_col_has_high_bit_unset = (end_col & (1 << 31)) == 0; - // If a region is improperly ordered (end < start), `llvm-cov` will exit - // with a fatal error, which is inconvenient for users and hard to debug. - let is_ordered = (start_line, start_col) <= (end_line, end_col); - - if all_nonzero && end_col_has_high_bit_unset && is_ordered { - Some(source_region) - } else { - debug!( - ?source_region, - ?all_nonzero, - ?end_col_has_high_bit_unset, - ?is_ordered, - "Skipping source region that would be misinterpreted or rejected by LLVM" - ); - // If this happens in a debug build, ICE to make it easier to notice. - debug_assert!(false, "Improper source region: {source_region:?}"); - None - } -} - /// Function information extracted from HIR by the coverage instrumentor. #[derive(Debug)] struct ExtractedHirInfo { diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index 085c738f1f9..314a86ea52f 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -17,14 +17,13 @@ mod from_mir; pub(super) fn extract_refined_covspans( mir_body: &mir::Body<'_>, hir_info: &ExtractedHirInfo, - basic_coverage_blocks: &CoverageGraph, + graph: &CoverageGraph, code_mappings: &mut impl Extend<mappings::CodeMapping>, ) { - let ExtractedCovspans { mut covspans } = - extract_covspans_from_mir(mir_body, hir_info, basic_coverage_blocks); + let ExtractedCovspans { mut covspans } = extract_covspans_from_mir(mir_body, hir_info, graph); // First, perform the passes that need macro information. - covspans.sort_by(|a, b| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb)); + covspans.sort_by(|a, b| graph.cmp_in_dominator_order(a.bcb, b.bcb)); remove_unwanted_expansion_spans(&mut covspans); split_visible_macro_spans(&mut covspans); @@ -34,7 +33,7 @@ pub(super) fn extract_refined_covspans( let compare_covspans = |a: &Covspan, b: &Covspan| { compare_spans(a.span, b.span) // After deduplication, we want to keep only the most-dominated BCB. - .then_with(|| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb).reverse()) + .then_with(|| graph.cmp_in_dominator_order(a.bcb, b.bcb).reverse()) }; covspans.sort_by(compare_covspans); diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs index 824d657e1fc..26ce743be36 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs @@ -22,13 +22,13 @@ pub(crate) struct ExtractedCovspans { pub(crate) fn extract_covspans_from_mir( mir_body: &mir::Body<'_>, hir_info: &ExtractedHirInfo, - basic_coverage_blocks: &CoverageGraph, + graph: &CoverageGraph, ) -> ExtractedCovspans { let &ExtractedHirInfo { body_span, .. } = hir_info; let mut covspans = vec![]; - for (bcb, bcb_data) in basic_coverage_blocks.iter_enumerated() { + for (bcb, bcb_data) in graph.iter_enumerated() { bcb_to_initial_coverage_spans(mir_body, body_span, bcb, bcb_data, &mut covspans); } diff --git a/compiler/rustc_mir_transform/src/coverage/tests.rs b/compiler/rustc_mir_transform/src/coverage/tests.rs index 233ca9981c5..b2ee50de50a 100644 --- a/compiler/rustc_mir_transform/src/coverage/tests.rs +++ b/compiler/rustc_mir_transform/src/coverage/tests.rs @@ -223,16 +223,12 @@ fn print_mir_graphviz(name: &str, mir_body: &Body<'_>) { } } -fn print_coverage_graphviz( - name: &str, - mir_body: &Body<'_>, - basic_coverage_blocks: &graph::CoverageGraph, -) { +fn print_coverage_graphviz(name: &str, mir_body: &Body<'_>, graph: &graph::CoverageGraph) { if PRINT_GRAPHS { println!( "digraph {} {{\n{}\n}}", name, - basic_coverage_blocks + graph .iter_enumerated() .map(|(bcb, bcb_data)| { format!( @@ -240,7 +236,7 @@ fn print_coverage_graphviz( bcb, bcb, mir_body[bcb_data.last_bb()].terminator().kind.name(), - basic_coverage_blocks + graph .successors(bcb) .map(|successor| { format!(" {:?} -> {:?};", bcb, successor) }) .join("\n") @@ -300,11 +296,11 @@ fn goto_switchint<'a>() -> Body<'a> { #[track_caller] fn assert_successors( - basic_coverage_blocks: &graph::CoverageGraph, + graph: &graph::CoverageGraph, bcb: BasicCoverageBlock, expected_successors: &[BasicCoverageBlock], ) { - let mut successors = basic_coverage_blocks.successors[bcb].clone(); + let mut successors = graph.successors[bcb].clone(); successors.sort_unstable(); assert_eq!(successors, expected_successors); } @@ -315,8 +311,8 @@ fn test_covgraph_goto_switchint() { if false { eprintln!("basic_blocks = {}", debug_basic_blocks(&mir_body)); } - let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); - print_coverage_graphviz("covgraph_goto_switchint ", &mir_body, &basic_coverage_blocks); + let graph = graph::CoverageGraph::from_mir(&mir_body); + print_coverage_graphviz("covgraph_goto_switchint ", &mir_body, &graph); /* ┌──────────────┐ ┌─────────────────┐ │ bcb2: Return │ ◀── │ bcb0: SwitchInt │ @@ -328,16 +324,11 @@ fn test_covgraph_goto_switchint() { │ bcb1: Return │ └─────────────────┘ */ - assert_eq!( - basic_coverage_blocks.num_nodes(), - 3, - "basic_coverage_blocks: {:?}", - basic_coverage_blocks.iter_enumerated().collect::<Vec<_>>() - ); + assert_eq!(graph.num_nodes(), 3, "graph: {:?}", graph.iter_enumerated().collect::<Vec<_>>()); - assert_successors(&basic_coverage_blocks, bcb(0), &[bcb(1), bcb(2)]); - assert_successors(&basic_coverage_blocks, bcb(1), &[]); - assert_successors(&basic_coverage_blocks, bcb(2), &[]); + assert_successors(&graph, bcb(0), &[bcb(1), bcb(2)]); + assert_successors(&graph, bcb(1), &[]); + assert_successors(&graph, bcb(2), &[]); } /// Create a mock `Body` with a loop. @@ -383,12 +374,8 @@ fn switchint_then_loop_else_return<'a>() -> Body<'a> { #[test] fn test_covgraph_switchint_then_loop_else_return() { let mir_body = switchint_then_loop_else_return(); - let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); - print_coverage_graphviz( - "covgraph_switchint_then_loop_else_return", - &mir_body, - &basic_coverage_blocks, - ); + let graph = graph::CoverageGraph::from_mir(&mir_body); + print_coverage_graphviz("covgraph_switchint_then_loop_else_return", &mir_body, &graph); /* ┌─────────────────┐ │ bcb0: Call │ @@ -408,17 +395,12 @@ fn test_covgraph_switchint_then_loop_else_return() { │ │ └─────────────────────────────────────┘ */ - assert_eq!( - basic_coverage_blocks.num_nodes(), - 4, - "basic_coverage_blocks: {:?}", - basic_coverage_blocks.iter_enumerated().collect::<Vec<_>>() - ); + assert_eq!(graph.num_nodes(), 4, "graph: {:?}", graph.iter_enumerated().collect::<Vec<_>>()); - assert_successors(&basic_coverage_blocks, bcb(0), &[bcb(1)]); - assert_successors(&basic_coverage_blocks, bcb(1), &[bcb(2), bcb(3)]); - assert_successors(&basic_coverage_blocks, bcb(2), &[]); - assert_successors(&basic_coverage_blocks, bcb(3), &[bcb(1)]); + assert_successors(&graph, bcb(0), &[bcb(1)]); + assert_successors(&graph, bcb(1), &[bcb(2), bcb(3)]); + assert_successors(&graph, bcb(2), &[]); + assert_successors(&graph, bcb(3), &[bcb(1)]); } /// Create a mock `Body` with nested loops. @@ -494,11 +476,11 @@ fn switchint_loop_then_inner_loop_else_break<'a>() -> Body<'a> { #[test] fn test_covgraph_switchint_loop_then_inner_loop_else_break() { let mir_body = switchint_loop_then_inner_loop_else_break(); - let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); + let graph = graph::CoverageGraph::from_mir(&mir_body); print_coverage_graphviz( "covgraph_switchint_loop_then_inner_loop_else_break", &mir_body, - &basic_coverage_blocks, + &graph, ); /* ┌─────────────────┐ @@ -531,18 +513,13 @@ fn test_covgraph_switchint_loop_then_inner_loop_else_break() { │ │ └────────────────────────────────────────────┘ */ - assert_eq!( - basic_coverage_blocks.num_nodes(), - 7, - "basic_coverage_blocks: {:?}", - basic_coverage_blocks.iter_enumerated().collect::<Vec<_>>() - ); - - assert_successors(&basic_coverage_blocks, bcb(0), &[bcb(1)]); - assert_successors(&basic_coverage_blocks, bcb(1), &[bcb(2), bcb(3)]); - assert_successors(&basic_coverage_blocks, bcb(2), &[]); - assert_successors(&basic_coverage_blocks, bcb(3), &[bcb(4)]); - assert_successors(&basic_coverage_blocks, bcb(4), &[bcb(5), bcb(6)]); - assert_successors(&basic_coverage_blocks, bcb(5), &[bcb(1)]); - assert_successors(&basic_coverage_blocks, bcb(6), &[bcb(4)]); + assert_eq!(graph.num_nodes(), 7, "graph: {:?}", graph.iter_enumerated().collect::<Vec<_>>()); + + assert_successors(&graph, bcb(0), &[bcb(1)]); + assert_successors(&graph, bcb(1), &[bcb(2), bcb(3)]); + assert_successors(&graph, bcb(2), &[]); + assert_successors(&graph, bcb(3), &[bcb(4)]); + assert_successors(&graph, bcb(4), &[bcb(5), bcb(6)]); + assert_successors(&graph, bcb(5), &[bcb(1)]); + assert_successors(&graph, bcb(6), &[bcb(4)]); } diff --git a/compiler/rustc_mir_transform/src/cross_crate_inline.rs b/compiler/rustc_mir_transform/src/cross_crate_inline.rs index 589be81558c..e1f1dd83f0d 100644 --- a/compiler/rustc_mir_transform/src/cross_crate_inline.rs +++ b/compiler/rustc_mir_transform/src/cross_crate_inline.rs @@ -1,4 +1,4 @@ -use rustc_attr::InlineAttr; +use rustc_attr_parsing::InlineAttr; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_middle::mir::visit::Visitor; diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs index 17c8348140a..91e1395e764 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<OptimizationData<'tcx>> { 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/function_item_references.rs b/compiler/rustc_mir_transform/src/function_item_references.rs index 2945fc6f3d5..fb21bf9977f 100644 --- a/compiler/rustc_mir_transform/src/function_item_references.rs +++ b/compiler/rustc_mir_transform/src/function_item_references.rs @@ -5,9 +5,8 @@ use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; use rustc_middle::ty::{self, EarlyBinder, GenericArgsRef, Ty, TyCtxt}; use rustc_session::lint::builtin::FUNCTION_ITEM_REFERENCES; -use rustc_span::Span; use rustc_span::source_map::Spanned; -use rustc_span::symbol::sym; +use rustc_span::{Span, sym}; use crate::errors; diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 79c38b50459..35699acb318 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -4,7 +4,7 @@ use std::iter; use std::ops::{Range, RangeFrom}; use rustc_abi::{ExternAbi, FieldIdx}; -use rustc_attr::InlineAttr; +use rustc_attr_parsing::InlineAttr; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_index::Idx; @@ -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/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index adc3374df2e..1a65affe812 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -7,8 +7,7 @@ use rustc_middle::bug; use rustc_middle::mir::*; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, layout}; -use rustc_span::symbol::Symbol; -use rustc_span::{DUMMY_SP, sym}; +use rustc_span::{DUMMY_SP, Symbol, sym}; use crate::simplify::simplify_duplicate_switch_targets; use crate::take_array; diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index beed007589b..8feb90ff7a0 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -35,7 +35,6 @@ //! Likewise, applying the optimisation can create a lot of new MIR, so we bound the instruction //! cost by `MAX_COST`. -use rustc_abi::{TagEncoding, Variants}; use rustc_arena::DroplessArena; use rustc_const_eval::const_eval::DummyMachine; use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, Projectable}; @@ -565,31 +564,15 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { StatementKind::SetDiscriminant { box place, variant_index } => { let Some(discr_target) = self.map.find_discr(place.as_ref()) else { return }; let enum_ty = place.ty(self.body, self.tcx).ty; - // `SetDiscriminant` may be a no-op if the assigned variant is the untagged variant - // of a niche encoding. If we cannot ensure that we write to the discriminant, do - // nothing. - let Ok(enum_layout) = self.ecx.layout_of(enum_ty) else { + // `SetDiscriminant` guarantees that the discriminant is now `variant_index`. + // Even if the discriminant write does nothing due to niches, it is UB to set the + // discriminant when the data does not encode the desired discriminant. + let Some(discr) = + self.ecx.discriminant_for_variant(enum_ty, *variant_index).discard_err() + else { return; }; - let writes_discriminant = match enum_layout.variants { - Variants::Single { index } => { - assert_eq!(index, *variant_index); - true - } - Variants::Multiple { tag_encoding: TagEncoding::Direct, .. } => true, - Variants::Multiple { - tag_encoding: TagEncoding::Niche { untagged_variant, .. }, - .. - } => *variant_index != untagged_variant, - }; - if writes_discriminant { - let Some(discr) = - self.ecx.discriminant_for_variant(enum_ty, *variant_index).discard_err() - else { - return; - }; - self.process_immediate(bb, discr_target, discr, state); - } + self.process_immediate(bb, discr_target, discr, state); } // If we expect `lhs ?= true`, we have an opportunity if we assume `lhs == true`. StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume( diff --git a/compiler/rustc_mir_transform/src/large_enums.rs b/compiler/rustc_mir_transform/src/large_enums.rs index 8be5a63d008..490e7dd8f7e 100644 --- a/compiler/rustc_mir_transform/src/large_enums.rs +++ b/compiler/rustc_mir_transform/src/large_enums.rs @@ -216,7 +216,7 @@ impl EnumSizeOpt { }; let layout = tcx.layout_of(typing_env.as_query_input(ty)).ok()?; let variants = match &layout.variants { - Variants::Single { .. } => return None, + Variants::Single { .. } | Variants::Empty => return None, Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, .. } => return None, Variants::Multiple { variants, .. } if variants.len() <= 1 => return None, diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index 6d635606687..942c6144ea6 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -3,7 +3,7 @@ use rustc_middle::mir::*; use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::{bug, span_bug}; -use rustc_span::symbol::sym; +use rustc_span::sym; use crate::take_array; 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 139b25be0ab..f01bab75c4a 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_mir_transform/src/unreachable_enum_branching.rs b/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs index 57e255b7c32..55dcad0680a 100644 --- a/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs +++ b/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs @@ -54,6 +54,10 @@ fn variant_discriminants<'tcx>( tcx: TyCtxt<'tcx>, ) -> FxHashSet<u128> { match &layout.variants { + Variants::Empty => { + // Uninhabited, no valid discriminant. + FxHashSet::default() + } Variants::Single { index } => { let mut res = FxHashSet::default(); res.insert( diff --git a/compiler/rustc_monomorphize/Cargo.toml b/compiler/rustc_monomorphize/Cargo.toml index e18441ea7fc..9bdaeb015cd 100644 --- a/compiler/rustc_monomorphize/Cargo.toml +++ b/compiler/rustc_monomorphize/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start rustc_abi = { path = "../rustc_abi" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 480d82c1a38..05d34715314 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -232,8 +232,7 @@ use rustc_middle::{bug, span_bug}; use rustc_session::Limit; use rustc_session::config::EntryFnType; use rustc_span::source_map::{Spanned, dummy_spanned, respan}; -use rustc_span::symbol::sym; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Span, sym}; use tracing::{debug, instrument, trace}; use crate::errors::{self, EncounteredErrorWhileInstantiating, NoOptimizedMir, RecursionLimit}; diff --git a/compiler/rustc_monomorphize/src/mono_checks/move_check.rs b/compiler/rustc_monomorphize/src/mono_checks/move_check.rs index 438d49fd7fb..02b9397f9a9 100644 --- a/compiler/rustc_monomorphize/src/mono_checks/move_check.rs +++ b/compiler/rustc_monomorphize/src/mono_checks/move_check.rs @@ -6,9 +6,8 @@ use rustc_middle::mir::{self, Location, traversal}; use rustc_middle::ty::{self, AssocKind, Instance, Ty, TyCtxt, TypeFoldable}; use rustc_session::Limit; use rustc_session::lint::builtin::LARGE_ASSIGNMENTS; -use rustc_span::Span; use rustc_span::source_map::Spanned; -use rustc_span::symbol::{Ident, sym}; +use rustc_span::{Ident, Span, sym}; use tracing::{debug, trace}; use crate::errors::LargeAssignmentsLint; diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index dabce72650a..7b179663430 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -117,7 +117,7 @@ use rustc_middle::ty::{self, InstanceKind, TyCtxt}; use rustc_middle::util::Providers; use rustc_session::CodegenUnits; use rustc_session::config::{DumpMonoStatsFormat, SwitchWithOptPath}; -use rustc_span::symbol::Symbol; +use rustc_span::Symbol; use rustc_target::spec::SymbolVisibility; use tracing::debug; @@ -833,7 +833,8 @@ fn mono_item_visibility<'tcx>( return if is_generic && (always_export_generics || (can_export_generics - && tcx.codegen_fn_attrs(def_id).inline == rustc_attr::InlineAttr::Never)) + && tcx.codegen_fn_attrs(def_id).inline + == rustc_attr_parsing::InlineAttr::Never)) { // If it is an upstream monomorphization and we export generics, we must make // it available to downstream crates. @@ -847,7 +848,7 @@ fn mono_item_visibility<'tcx>( if is_generic { if always_export_generics || (can_export_generics - && tcx.codegen_fn_attrs(def_id).inline == rustc_attr::InlineAttr::Never) + && tcx.codegen_fn_attrs(def_id).inline == rustc_attr_parsing::InlineAttr::Never) { if tcx.is_unreachable_local_definition(def_id) { // This instance cannot be used from another crate. diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index 63608f9e856..2f7301d8fe5 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -3,6 +3,7 @@ use std::cmp::Ordering; use rustc_type_ir::data_structures::HashMap; use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_type_ir::inherent::*; +use rustc_type_ir::solve::{Goal, QueryInput}; use rustc_type_ir::visit::TypeVisitableExt; use rustc_type_ir::{ self as ty, Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, InferCtxtLike, @@ -17,8 +18,11 @@ use crate::delegate::SolverDelegate; /// while canonicalizing the response happens in the context of the /// query. #[derive(Debug, Clone, Copy)] -pub enum CanonicalizeMode { - Input, +enum CanonicalizeMode { + /// When canonicalizing the `param_env`, we keep `'static` as merging + /// trait candidates relies on it when deciding whether a where-bound + /// is trivial. + Input { keep_static: bool }, /// FIXME: We currently return region constraints referring to /// placeholders and inference variables from a binder instantiated /// inside of the query. @@ -59,15 +63,15 @@ pub struct Canonicalizer<'a, D: SolverDelegate<Interner = I>, I: Interner> { } impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> { - pub fn canonicalize<T: TypeFoldable<I>>( + pub fn canonicalize_response<T: TypeFoldable<I>>( delegate: &'a D, - canonicalize_mode: CanonicalizeMode, + max_input_universe: ty::UniverseIndex, variables: &'a mut Vec<I::GenericArg>, value: T, ) -> ty::Canonical<I, T> { let mut canonicalizer = Canonicalizer { delegate, - canonicalize_mode, + canonicalize_mode: CanonicalizeMode::Response { max_input_universe }, variables, variable_lookup_table: Default::default(), @@ -80,9 +84,67 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> { let value = value.fold_with(&mut canonicalizer); assert!(!value.has_infer(), "unexpected infer in {value:?}"); assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}"); - let (max_universe, variables) = canonicalizer.finalize(); + Canonical { max_universe, variables, value } + } + + /// When canonicalizing query inputs, we keep `'static` in the `param_env` + /// but erase it everywhere else. We generally don't want to depend on region + /// identity, so while it should not matter whether `'static` is kept in the + /// value or opaque type storage as well, this prevents us from accidentally + /// relying on it in the future. + /// + /// We want to keep the option of canonicalizing `'static` to an existential + /// variable in the future by changing the way we detect global where-bounds. + pub fn canonicalize_input<P: TypeFoldable<I>>( + delegate: &'a D, + variables: &'a mut Vec<I::GenericArg>, + input: QueryInput<I, P>, + ) -> ty::Canonical<I, QueryInput<I, P>> { + // First canonicalize the `param_env` while keeping `'static` + let mut env_canonicalizer = Canonicalizer { + delegate, + canonicalize_mode: CanonicalizeMode::Input { keep_static: true }, + + variables, + variable_lookup_table: Default::default(), + primitive_var_infos: Vec::new(), + binder_index: ty::INNERMOST, + + cache: Default::default(), + }; + let param_env = input.goal.param_env.fold_with(&mut env_canonicalizer); + debug_assert_eq!(env_canonicalizer.binder_index, ty::INNERMOST); + // Then canonicalize the rest of the input without keeping `'static` + // while *mostly* reusing the canonicalizer from above. + let mut rest_canonicalizer = Canonicalizer { + delegate, + canonicalize_mode: CanonicalizeMode::Input { keep_static: false }, + + variables: env_canonicalizer.variables, + // We're able to reuse the `variable_lookup_table` as whether or not + // it already contains an entry for `'static` does not matter. + variable_lookup_table: env_canonicalizer.variable_lookup_table, + primitive_var_infos: env_canonicalizer.primitive_var_infos, + binder_index: ty::INNERMOST, + // We do not reuse the cache as it may contain entries whose canonicalized + // value contains `'static`. While we could alternatively handle this by + // checking for `'static` when using cached entries, this does not + // feel worth the effort. I do not expect that a `ParamEnv` will ever + // contain large enough types for caching to be necessary. + cache: Default::default(), + }; + + let predicate = input.goal.predicate.fold_with(&mut rest_canonicalizer); + let goal = Goal { param_env, predicate }; + let predefined_opaques_in_body = + input.predefined_opaques_in_body.fold_with(&mut rest_canonicalizer); + let value = QueryInput { goal, predefined_opaques_in_body }; + + assert!(!value.has_infer(), "unexpected infer in {value:?}"); + assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}"); + let (max_universe, variables) = rest_canonicalizer.finalize(); Canonical { max_universe, variables, value } } @@ -126,7 +188,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> { // all information which should not matter for the solver. // // For this we compress universes as much as possible. - CanonicalizeMode::Input => {} + CanonicalizeMode::Input { .. } => {} // When canonicalizing a response we map a universes already entered // by the caller to the root universe and only return useful universe // information for placeholders and inference variables created inside @@ -290,17 +352,15 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> { } }, ty::Placeholder(placeholder) => match self.canonicalize_mode { - CanonicalizeMode::Input => CanonicalVarKind::PlaceholderTy(PlaceholderLike::new( - placeholder.universe(), - self.variables.len().into(), - )), + CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderTy( + PlaceholderLike::new(placeholder.universe(), self.variables.len().into()), + ), CanonicalizeMode::Response { .. } => CanonicalVarKind::PlaceholderTy(placeholder), }, ty::Param(_) => match self.canonicalize_mode { - CanonicalizeMode::Input => CanonicalVarKind::PlaceholderTy(PlaceholderLike::new( - ty::UniverseIndex::ROOT, - self.variables.len().into(), - )), + CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderTy( + PlaceholderLike::new(ty::UniverseIndex::ROOT, self.variables.len().into()), + ), CanonicalizeMode::Response { .. } => panic!("param ty in response: {t:?}"), }, ty::Bool @@ -357,21 +417,30 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz let kind = match r.kind() { ty::ReBound(..) => return r, - // We may encounter `ReStatic` in item signatures or the hidden type - // of an opaque. `ReErased` should only be encountered in the hidden + // We don't canonicalize `ReStatic` in the `param_env` as we use it + // when checking whether a `ParamEnv` candidate is global. + ty::ReStatic => match self.canonicalize_mode { + CanonicalizeMode::Input { keep_static: false } => { + CanonicalVarKind::Region(ty::UniverseIndex::ROOT) + } + CanonicalizeMode::Input { keep_static: true } + | CanonicalizeMode::Response { .. } => return r, + }, + + // `ReErased` should only be encountered in the hidden // type of an opaque for regions that are ignored for the purposes of // captures. // // FIXME: We should investigate the perf implications of not uniquifying // `ReErased`. We may be able to short-circuit registering region // obligations if we encounter a `ReErased` on one side, for example. - ty::ReStatic | ty::ReErased | ty::ReError(_) => match self.canonicalize_mode { - CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), + ty::ReErased | ty::ReError(_) => match self.canonicalize_mode { + CanonicalizeMode::Input { .. } => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), CanonicalizeMode::Response { .. } => return r, }, ty::ReEarlyParam(_) | ty::ReLateParam(_) => match self.canonicalize_mode { - CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), + CanonicalizeMode::Input { .. } => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), CanonicalizeMode::Response { .. } => { panic!("unexpected region in response: {r:?}") } @@ -379,7 +448,7 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz ty::RePlaceholder(placeholder) => match self.canonicalize_mode { // We canonicalize placeholder regions as existentials in query inputs. - CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), + CanonicalizeMode::Input { .. } => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), CanonicalizeMode::Response { max_input_universe } => { // If we have a placeholder region inside of a query, it must be from // a new universe. @@ -397,7 +466,9 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz "region vid should have been resolved fully before canonicalization" ); match self.canonicalize_mode { - CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), + CanonicalizeMode::Input { keep_static: _ } => { + CanonicalVarKind::Region(ty::UniverseIndex::ROOT) + } CanonicalizeMode::Response { .. } => { CanonicalVarKind::Region(self.delegate.universe_of_lt(vid).unwrap()) } @@ -434,7 +505,7 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz ty::InferConst::Fresh(_) => todo!(), }, ty::ConstKind::Placeholder(placeholder) => match self.canonicalize_mode { - CanonicalizeMode::Input => CanonicalVarKind::PlaceholderConst( + CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderConst( PlaceholderLike::new(placeholder.universe(), self.variables.len().into()), ), CanonicalizeMode::Response { .. } => { @@ -442,7 +513,7 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz } }, ty::ConstKind::Param(_) => match self.canonicalize_mode { - CanonicalizeMode::Input => CanonicalVarKind::PlaceholderConst( + CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderConst( PlaceholderLike::new(ty::UniverseIndex::ROOT, self.variables.len().into()), ), CanonicalizeMode::Response { .. } => panic!("param ty in response: {c:?}"), diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index 198ccb000f3..02f6439b77f 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -11,6 +11,7 @@ use rustc_type_ir::visit::TypeVisitableExt as _; use rustc_type_ir::{self as ty, Interner, TypingMode, Upcast as _, elaborate}; use tracing::{debug, instrument}; +use super::trait_goals::TraitGoalProvenVia; use crate::delegate::SolverDelegate; use crate::solve::inspect::ProbeKind; use crate::solve::{ @@ -337,15 +338,6 @@ where self.assemble_param_env_candidates(goal, &mut candidates); - match self.typing_mode() { - TypingMode::Coherence => {} - TypingMode::Analysis { .. } - | TypingMode::PostBorrowckAnalysis { .. } - | TypingMode::PostAnalysis => { - self.discard_impls_shadowed_by_env(goal, &mut candidates); - } - } - candidates } @@ -500,7 +492,7 @@ where goal: Goal<I, G>, candidates: &mut Vec<Candidate<I>>, ) { - for (i, assumption) in goal.param_env.caller_bounds().into_iter().enumerate() { + for (i, assumption) in goal.param_env.caller_bounds().iter().enumerate() { candidates.extend(G::probe_and_consider_implied_clause( self, CandidateSource::ParamEnv(i), @@ -733,72 +725,64 @@ where }) } - /// If there's a where-bound for the current goal, do not use any impl candidates - /// to prove the current goal. Most importantly, if there is a where-bound which does - /// not specify any associated types, we do not allow normalizing the associated type - /// by using an impl, even if it would apply. + /// We sadly can't simply take all possible candidates for normalization goals + /// and check whether they result in the same constraints. We want to make sure + /// that trying to normalize an alias doesn't result in constraints which aren't + /// otherwise required. + /// + /// Most notably, when proving a trait goal by via a where-bound, we should not + /// normalize via impls which have stricter region constraints than the where-bound: + /// + /// ```rust + /// trait Trait<'a> { + /// type Assoc; + /// } + /// + /// impl<'a, T: 'a> Trait<'a> for T { + /// type Assoc = u32; + /// } + /// + /// fn with_bound<'a, T: Trait<'a>>(_value: T::Assoc) {} + /// ``` /// - /// <https://github.com/rust-lang/trait-system-refactor-initiative/issues/76> - // FIXME(@lcnr): The current structure here makes me unhappy and feels ugly. idk how - // to improve this however. However, this should make it fairly straightforward to refine - // the filtering going forward, so it seems alright-ish for now. - #[instrument(level = "debug", skip(self, goal))] - fn discard_impls_shadowed_by_env<G: GoalKind<D>>( + /// The where-bound of `with_bound` doesn't specify the associated type, so we would + /// only be able to normalize `<T as Trait<'a>>::Assoc` by using the impl. This impl + /// adds a `T: 'a` bound however, which would result in a region error. Given that the + /// user explicitly wrote that `T: Trait<'a>` holds, this is undesirable and we instead + /// treat the alias as rigid. + /// + /// See trait-system-refactor-initiative#124 for more details. + #[instrument(level = "debug", skip(self), ret)] + pub(super) fn merge_candidates( &mut self, - goal: Goal<I, G>, - candidates: &mut Vec<Candidate<I>>, - ) { - let cx = self.cx(); - let trait_goal: Goal<I, ty::TraitPredicate<I>> = - goal.with(cx, goal.predicate.trait_ref(cx)); - - let mut trait_candidates_from_env = vec![]; - self.probe(|_| ProbeKind::ShadowedEnvProbing).enter(|ecx| { - ecx.assemble_param_env_candidates(trait_goal, &mut trait_candidates_from_env); - ecx.assemble_alias_bound_candidates(trait_goal, &mut trait_candidates_from_env); - }); + proven_via: Option<TraitGoalProvenVia>, + candidates: Vec<Candidate<I>>, + ) -> QueryResult<I> { + let Some(proven_via) = proven_via else { + // We don't care about overflow. If proving the trait goal overflowed, then + // it's enough to report an overflow error for that, we don't also have to + // overflow during normalization. + return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Ambiguity)); + }; - if !trait_candidates_from_env.is_empty() { - let trait_env_result = self.merge_candidates(trait_candidates_from_env); - match trait_env_result.unwrap().value.certainty { - // If proving the trait goal succeeds by using the env, - // we freely drop all impl candidates. - // - // FIXME(@lcnr): It feels like this could easily hide - // a forced ambiguity candidate added earlier. - // This feels dangerous. - Certainty::Yes => { - candidates.retain(|c| match c.source { - CandidateSource::Impl(_) | CandidateSource::BuiltinImpl(_) => { - debug!(?c, "discard impl candidate"); - false - } - CandidateSource::ParamEnv(_) | CandidateSource::AliasBound => true, - CandidateSource::CoherenceUnknowable => panic!("uh oh"), - }); - } - // If it is still ambiguous we instead just force the whole goal - // to be ambig and wait for inference constraints. See - // tests/ui/traits/next-solver/env-shadows-impls/ambig-env-no-shadow.rs - Certainty::Maybe(cause) => { - debug!(?cause, "force ambiguity"); - *candidates = self.forced_ambiguity(cause).into_iter().collect(); - } - } - } - } + let responses: Vec<_> = match proven_via { + // Even when a trait bound has been proven using a where-bound, we + // still need to consider alias-bounds for normalization, see + // tests/ui/next-solver/alias-bound-shadowed-by-env.rs. + // + // FIXME(const_trait_impl): should this behavior also be used by + // constness checking. Doing so is *at least theoretically* breaking, + // see github.com/rust-lang/rust/issues/133044#issuecomment-2500709754 + TraitGoalProvenVia::ParamEnv | TraitGoalProvenVia::AliasBound => candidates + .iter() + .filter(|c| { + matches!(c.source, CandidateSource::AliasBound | CandidateSource::ParamEnv(_)) + }) + .map(|c| c.result) + .collect(), + TraitGoalProvenVia::Misc => candidates.iter().map(|c| c.result).collect(), + }; - /// If there are multiple ways to prove a trait or projection goal, we have - /// to somehow try to merge the candidates into one. If that fails, we return - /// ambiguity. - #[instrument(level = "debug", skip(self), ret)] - pub(super) fn merge_candidates(&mut self, candidates: Vec<Candidate<I>>) -> QueryResult<I> { - // First try merging all candidates. This is complete and fully sound. - let responses = candidates.iter().map(|c| c.result).collect::<Vec<_>>(); - if let Some(result) = self.try_merge_responses(&responses) { - return Ok(result); - } else { - self.flounder(&responses) - } + self.try_merge_responses(&responses).map_or_else(|| self.flounder(&responses), Ok) } } diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 81b5199002b..ce7552e30f0 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -4,6 +4,7 @@ use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; +use rustc_type_ir::solve::inspect::ProbeKind; use rustc_type_ir::{self as ty, Interner, elaborate}; use tracing::instrument; @@ -391,6 +392,11 @@ where goal: Goal<I, ty::HostEffectPredicate<I>>, ) -> QueryResult<I> { let candidates = self.assemble_and_evaluate_candidates(goal); - self.merge_candidates(candidates) + let (_, proven_via) = self.probe(|_| ProbeKind::ShadowedEnvProbing).enter(|ecx| { + let trait_goal: Goal<I, ty::TraitPredicate<I>> = + goal.with(ecx.cx(), goal.predicate.trait_ref); + ecx.compute_trait_goal(trait_goal) + })?; + self.merge_candidates(proven_via, candidates) } } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs index a143af13688..e99cd3d2727 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs @@ -18,7 +18,7 @@ use rustc_type_ir::relate::solver_relating::RelateExt; use rustc_type_ir::{self as ty, Canonical, CanonicalVarValues, InferCtxtLike, Interner}; use tracing::{debug, instrument, trace}; -use crate::canonicalizer::{CanonicalizeMode, Canonicalizer}; +use crate::canonicalizer::Canonicalizer; use crate::delegate::SolverDelegate; use crate::resolve::EagerResolver; use crate::solve::eval_ctxt::NestedGoals; @@ -60,17 +60,13 @@ where (goal, opaque_types).fold_with(&mut EagerResolver::new(self.delegate)); let mut orig_values = Default::default(); - let canonical = Canonicalizer::canonicalize( - self.delegate, - CanonicalizeMode::Input, - &mut orig_values, - QueryInput { + let canonical = + Canonicalizer::canonicalize_input(self.delegate, &mut orig_values, QueryInput { goal, predefined_opaques_in_body: self .cx() .mk_predefined_opaques_in_body(PredefinedOpaquesData { opaque_types }), - }, - ); + }); let query_input = ty::CanonicalQueryInput { canonical, typing_mode: self.typing_mode() }; (orig_values, query_input) } @@ -148,9 +144,9 @@ where .region_constraints .retain(|outlives| outlives.0.as_region().map_or(true, |re| re != outlives.1)); - let canonical = Canonicalizer::canonicalize( + let canonical = Canonicalizer::canonicalize_response( self.delegate, - CanonicalizeMode::Response { max_input_universe: self.max_input_universe }, + self.max_input_universe, &mut Default::default(), Response { var_values, @@ -428,12 +424,7 @@ where let var_values = CanonicalVarValues { var_values: delegate.cx().mk_args(var_values) }; let state = inspect::State { var_values, data }; let state = state.fold_with(&mut EagerResolver::new(delegate)); - Canonicalizer::canonicalize( - delegate, - CanonicalizeMode::Response { max_input_universe }, - &mut vec![], - state, - ) + Canonicalizer::canonicalize_response(delegate, max_input_universe, &mut vec![], state) } // FIXME: needs to be pub to be accessed by downstream diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 70ceb22bfea..8c74490e0e0 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -440,7 +440,7 @@ where if let Some(kind) = kind.no_bound_vars() { match kind { ty::PredicateKind::Clause(ty::ClauseKind::Trait(predicate)) => { - self.compute_trait_goal(Goal { param_env, predicate }) + self.compute_trait_goal(Goal { param_env, predicate }).map(|(r, _via)| r) } ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(predicate)) => { self.compute_host_effect_goal(Goal { param_env, predicate }) diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index ebf1013db1e..37678bfd880 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -243,22 +243,27 @@ where .copied() } + fn bail_with_ambiguity(&mut self, responses: &[CanonicalResponse<I>]) -> CanonicalResponse<I> { + debug_assert!(!responses.is_empty()); + if let Certainty::Maybe(maybe_cause) = + responses.iter().fold(Certainty::AMBIGUOUS, |certainty, response| { + certainty.unify_with(response.value.certainty) + }) + { + self.make_ambiguous_response_no_constraints(maybe_cause) + } else { + panic!("expected flounder response to be ambiguous") + } + } + /// If we fail to merge responses we flounder and return overflow or ambiguity. #[instrument(level = "trace", skip(self), ret)] fn flounder(&mut self, responses: &[CanonicalResponse<I>]) -> QueryResult<I> { if responses.is_empty() { return Err(NoSolution); + } else { + Ok(self.bail_with_ambiguity(responses)) } - - let Certainty::Maybe(maybe_cause) = - responses.iter().fold(Certainty::AMBIGUOUS, |certainty, response| { - certainty.unify_with(response.value.certainty) - }) - else { - panic!("expected flounder response to be ambiguous") - }; - - Ok(self.make_ambiguous_response_no_constraints(maybe_cause)) } /// Normalize a type for when it is structurally matched on. diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 63dbee2640b..b8867192225 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -88,10 +88,17 @@ where /// returns `NoSolution`. #[instrument(level = "trace", skip(self), ret)] fn normalize_at_least_one_step(&mut self, goal: Goal<I, NormalizesTo<I>>) -> QueryResult<I> { - match goal.predicate.alias.kind(self.cx()) { + let cx = self.cx(); + match goal.predicate.alias.kind(cx) { ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst => { let candidates = self.assemble_and_evaluate_candidates(goal); - self.merge_candidates(candidates) + let (_, proven_via) = + self.probe(|_| ProbeKind::ShadowedEnvProbing).enter(|ecx| { + let trait_goal: Goal<I, ty::TraitPredicate<I>> = + goal.with(cx, goal.predicate.alias.trait_ref(cx)); + ecx.compute_trait_goal(trait_goal) + })?; + self.merge_candidates(proven_via, candidates) } ty::AliasTermKind::InherentTy => self.normalize_inherent_associated_type(goal), ty::AliasTermKind::OpaqueTy => self.normalize_opaque_type(goal), diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 12df35d30b8..886cdec0345 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -5,6 +5,7 @@ use rustc_type_ir::data_structures::IndexSet; use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; +use rustc_type_ir::solve::CanonicalResponse; use rustc_type_ir::visit::TypeVisitableExt as _; use rustc_type_ir::{self as ty, Interner, TraitPredicate, TypingMode, Upcast as _, elaborate}; use tracing::{instrument, trace}; @@ -1147,13 +1148,101 @@ where ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } +} + +/// How we've proven this trait goal. +/// +/// This is used by `NormalizesTo` goals to only normalize +/// by using the same 'kind of candidate' we've used to prove +/// its corresponding trait goal. Most notably, we do not +/// normalize by using an impl if the trait goal has been +/// proven via a `ParamEnv` candidate. +/// +/// This is necessary to avoid unnecessary region constraints, +/// see trait-system-refactor-initiative#125 for more details. +#[derive(Debug, Clone, Copy)] +pub(super) enum TraitGoalProvenVia { + /// We've proven the trait goal by something which is + /// is not a non-global where-bound or an alias-bound. + /// + /// This means we don't disable any candidates during + /// normalization. + Misc, + ParamEnv, + AliasBound, +} + +impl<D, I> EvalCtxt<'_, D> +where + D: SolverDelegate<Interner = I>, + I: Interner, +{ + pub(super) fn merge_trait_candidates( + &mut self, + goal: Goal<I, TraitPredicate<I>>, + candidates: Vec<Candidate<I>>, + ) -> Result<(CanonicalResponse<I>, Option<TraitGoalProvenVia>), NoSolution> { + if let TypingMode::Coherence = self.typing_mode() { + let all_candidates: Vec<_> = candidates.into_iter().map(|c| c.result).collect(); + return if let Some(response) = self.try_merge_responses(&all_candidates) { + Ok((response, Some(TraitGoalProvenVia::Misc))) + } else { + self.flounder(&all_candidates).map(|r| (r, None)) + }; + } + + // FIXME: prefer trivial builtin impls + + // If there are non-global where-bounds, prefer where-bounds + // (including global ones) over everything else. + let has_non_global_where_bounds = candidates.iter().any(|c| match c.source { + CandidateSource::ParamEnv(idx) => { + let where_bound = goal.param_env.caller_bounds().get(idx); + where_bound.has_bound_vars() || !where_bound.is_global() + } + _ => false, + }); + if has_non_global_where_bounds { + let where_bounds: Vec<_> = candidates + .iter() + .filter(|c| matches!(c.source, CandidateSource::ParamEnv(_))) + .map(|c| c.result) + .collect(); + + return if let Some(response) = self.try_merge_responses(&where_bounds) { + Ok((response, Some(TraitGoalProvenVia::ParamEnv))) + } else { + Ok((self.bail_with_ambiguity(&where_bounds), None)) + }; + } + + if candidates.iter().any(|c| matches!(c.source, CandidateSource::AliasBound)) { + let alias_bounds: Vec<_> = candidates + .iter() + .filter(|c| matches!(c.source, CandidateSource::AliasBound)) + .map(|c| c.result) + .collect(); + return if let Some(response) = self.try_merge_responses(&alias_bounds) { + Ok((response, Some(TraitGoalProvenVia::AliasBound))) + } else { + Ok((self.bail_with_ambiguity(&alias_bounds), None)) + }; + } + + let all_candidates: Vec<_> = candidates.into_iter().map(|c| c.result).collect(); + if let Some(response) = self.try_merge_responses(&all_candidates) { + Ok((response, Some(TraitGoalProvenVia::Misc))) + } else { + self.flounder(&all_candidates).map(|r| (r, None)) + } + } #[instrument(level = "trace", skip(self))] pub(super) fn compute_trait_goal( &mut self, goal: Goal<I, TraitPredicate<I>>, - ) -> QueryResult<I> { + ) -> Result<(CanonicalResponse<I>, Option<TraitGoalProvenVia>), NoSolution> { let candidates = self.assemble_and_evaluate_candidates(goal); - self.merge_candidates(candidates) + self.merge_trait_candidates(goal, candidates) } } diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 4c4e03cdfa3..f78d9dc2bfc 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -13,8 +13,7 @@ use rustc_errors::{ use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::edition::{Edition, LATEST_STABLE_EDITION}; -use rustc_span::symbol::Ident; -use rustc_span::{Span, Symbol}; +use rustc_span::{Ident, Span, Symbol}; use crate::fluent_generated as fluent; use crate::parser::{ForbiddenLetReason, TokenDescription}; diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 443ddfc94ec..792e2cc26ef 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -14,8 +14,7 @@ use rustc_session::lint::builtin::{ TEXT_DIRECTION_CODEPOINT_IN_COMMENT, }; use rustc_session::parse::ParseSess; -use rustc_span::symbol::Symbol; -use rustc_span::{BytePos, Pos, Span}; +use rustc_span::{BytePos, Pos, Span, Symbol}; use tracing::debug; use crate::lexer::diagnostics::TokenTreeDiagInfo; diff --git a/compiler/rustc_parse/src/lexer/unicode_chars.rs b/compiler/rustc_parse/src/lexer/unicode_chars.rs index 42eef27803e..ef8d0f96b61 100644 --- a/compiler/rustc_parse/src/lexer/unicode_chars.rs +++ b/compiler/rustc_parse/src/lexer/unicode_chars.rs @@ -1,8 +1,7 @@ //! Characters and their corresponding confusables were collected from //! <https://www.unicode.org/Public/security/10.0.0/confusables.txt> -use rustc_span::symbol::kw; -use rustc_span::{BytePos, Pos, Span}; +use rustc_span::{BytePos, Pos, Span, kw}; use super::Lexer; use crate::errors::TokenSubstitution; diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index b6fa2099588..2691e6f56d6 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -1,9 +1,6 @@ -use rustc_ast as ast; -use rustc_ast::attr; -use rustc_ast::token::{self, Delimiter}; +use rustc_ast::{self as ast, Attribute, attr, token}; use rustc_errors::codes::*; use rustc_errors::{Diag, PResult}; -use rustc_span::symbol::kw; use rustc_span::{BytePos, Span}; use thin_vec::ThinVec; use tracing::debug; @@ -12,7 +9,7 @@ use super::{ AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, ParserRange, PathStyle, Trailing, UsePreAttrPos, }; -use crate::{errors, fluent_generated as fluent, maybe_whole}; +use crate::{errors, exp, fluent_generated as fluent, maybe_whole}; // Public for rustfmt usage #[derive(Debug)] @@ -47,8 +44,8 @@ impl<'a> Parser<'a> { let mut just_parsed_doc_comment = false; let start_pos = self.num_bump_calls; loop { - let attr = if self.check(&token::Pound) { - let prev_outer_attr_sp = outer_attrs.last().map(|attr| attr.span); + let attr = if self.check(exp!(Pound)) { + let prev_outer_attr_sp = outer_attrs.last().map(|attr: &Attribute| attr.span); let inner_error_reason = if just_parsed_doc_comment { Some(InnerAttrForbiddenReason::AfterOuterDocComment { @@ -128,14 +125,14 @@ impl<'a> Parser<'a> { let lo = self.token.span; // Attributes can't have attributes of their own [Editor's note: not with that attitude] self.collect_tokens_no_attrs(|this| { - assert!(this.eat(&token::Pound), "parse_attribute called in non-attribute position"); + assert!(this.eat(exp!(Pound)), "parse_attribute called in non-attribute position"); let style = - if this.eat(&token::Not) { ast::AttrStyle::Inner } else { ast::AttrStyle::Outer }; + if this.eat(exp!(Not)) { ast::AttrStyle::Inner } else { ast::AttrStyle::Outer }; - this.expect(&token::OpenDelim(Delimiter::Bracket))?; + this.expect(exp!(OpenBracket))?; let item = this.parse_attr_item(ForceCollect::No)?; - this.expect(&token::CloseDelim(Delimiter::Bracket))?; + this.expect(exp!(CloseBracket))?; let attr_sp = lo.to(this.prev_token.span); // Emit error if inner attribute is encountered and forbidden. @@ -276,10 +273,10 @@ impl<'a> Parser<'a> { // Attr items don't have attributes. self.collect_tokens(None, AttrWrapper::empty(), force_collect, |this, _empty_attrs| { - let is_unsafe = this.eat_keyword(kw::Unsafe); + let is_unsafe = this.eat_keyword(exp!(Unsafe)); let unsafety = if is_unsafe { let unsafe_span = this.prev_token.span; - this.expect(&token::OpenDelim(Delimiter::Parenthesis))?; + this.expect(exp!(OpenParen))?; ast::Safety::Unsafe(unsafe_span) } else { ast::Safety::Default @@ -288,7 +285,7 @@ impl<'a> Parser<'a> { let path = this.parse_path(PathStyle::Mod)?; let args = this.parse_attr_args()?; if is_unsafe { - this.expect(&token::CloseDelim(Delimiter::Parenthesis))?; + this.expect(exp!(CloseParen))?; } Ok(( ast::AttrItem { unsafety, path, args, tokens: None }, @@ -308,7 +305,7 @@ impl<'a> Parser<'a> { loop { let start_pos = self.num_bump_calls; // Only try to parse if it is an inner attribute (has `!`). - let attr = if self.check(&token::Pound) && self.look_ahead(1, |t| t == &token::Not) { + let attr = if self.check(exp!(Pound)) && self.look_ahead(1, |t| t == &token::Not) { Some(self.parse_attribute(InnerAttrPolicy::Permitted)?) } else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind { if attr_style == ast::AttrStyle::Inner { @@ -360,7 +357,7 @@ impl<'a> Parser<'a> { &mut self, ) -> PResult<'a, (ast::MetaItemInner, Vec<(ast::AttrItem, Span)>)> { let cfg_predicate = self.parse_meta_item_inner()?; - self.expect(&token::Comma)?; + self.expect(exp!(Comma))?; // Presumably, the majority of the time there will only be one attr. let mut expanded_attrs = Vec::with_capacity(1); @@ -368,7 +365,7 @@ impl<'a> Parser<'a> { let lo = self.token.span; let item = self.parse_attr_item(ForceCollect::Yes)?; expanded_attrs.push((item, lo.to(self.prev_token.span))); - if !self.eat(&token::Comma) { + if !self.eat(exp!(Comma)) { break; } } @@ -382,7 +379,7 @@ impl<'a> Parser<'a> { let mut nmis = ThinVec::with_capacity(1); while self.token != token::Eof { nmis.push(self.parse_meta_item_inner()?); - if !self.eat(&token::Comma) { + if !self.eat(exp!(Comma)) { break; } } @@ -415,13 +412,13 @@ impl<'a> Parser<'a> { let lo = self.token.span; let is_unsafe = if unsafe_allowed == AllowLeadingUnsafe::Yes { - self.eat_keyword(kw::Unsafe) + self.eat_keyword(exp!(Unsafe)) } else { false }; let unsafety = if is_unsafe { let unsafe_span = self.prev_token.span; - self.expect(&token::OpenDelim(Delimiter::Parenthesis))?; + self.expect(exp!(OpenParen))?; ast::Safety::Unsafe(unsafe_span) } else { @@ -431,7 +428,7 @@ impl<'a> Parser<'a> { let path = self.parse_path(PathStyle::Mod)?; let kind = self.parse_meta_item_kind()?; if is_unsafe { - self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; + self.expect(exp!(CloseParen))?; } let span = lo.to(self.prev_token.span); @@ -439,9 +436,9 @@ impl<'a> Parser<'a> { } pub(crate) fn parse_meta_item_kind(&mut self) -> PResult<'a, ast::MetaItemKind> { - Ok(if self.eat(&token::Eq) { + Ok(if self.eat(exp!(Eq)) { ast::MetaItemKind::NameValue(self.parse_unsuffixed_meta_item_lit()?) - } else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) { + } else if self.check(exp!(OpenParen)) { let (list, _) = self.parse_paren_comma_seq(|p| p.parse_meta_item_inner())?; ast::MetaItemKind::List(list) } else { diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index e5edf605d82..aab4e1b1afc 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -16,20 +16,21 @@ use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::Lrc; use rustc_errors::{ - Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, FatalError, PResult, Subdiagnostic, - Suggestions, pluralize, + Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, PResult, Subdiagnostic, Suggestions, + pluralize, }; 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, Ident, kw, sym}; -use rustc_span::{BytePos, DUMMY_SP, Span, SpanSnippetError, Symbol}; +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}; use super::pat::Expected; use super::{ - BlockMode, CommaRecoveryMode, Parser, PathStyle, Restrictions, SemiColonMode, SeqSep, TokenType, + BlockMode, CommaRecoveryMode, ExpTokenPair, Parser, PathStyle, Restrictions, SemiColonMode, + SeqSep, TokenType, }; use crate::errors::{ AddParen, AmbiguousPlus, AsyncMoveBlockIn2015, AttributeOnParamType, AwaitSuggestion, @@ -47,7 +48,7 @@ use crate::errors::{ UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead, WrapType, }; use crate::parser::attr::InnerAttrPolicy; -use crate::{fluent_generated as fluent, parser}; +use crate::{exp, fluent_generated as fluent}; /// Creates a placeholder argument. pub(super) fn dummy_arg(ident: Ident, guar: ErrorGuaranteed) -> Param { @@ -462,8 +463,8 @@ impl<'a> Parser<'a> { pub(super) fn expected_one_of_not_found( &mut self, - edible: &[TokenKind], - inedible: &[TokenKind], + edible: &[ExpTokenPair<'_>], + inedible: &[ExpTokenPair<'_>], ) -> PResult<'a, ErrorGuaranteed> { debug!("expected_one_of_not_found(edible: {:?}, inedible: {:?})", edible, inedible); fn tokens_to_string(tokens: &[TokenType]) -> String { @@ -483,49 +484,17 @@ impl<'a> Parser<'a> { }) } - self.expected_tokens.extend(edible.iter().chain(inedible).cloned().map(TokenType::Token)); - let mut expected = self - .expected_tokens - .iter() - .filter(|token| { - // Filter out suggestions that suggest the same token which was found and deemed incorrect. - fn is_ident_eq_keyword(found: &TokenKind, expected: &TokenType) -> bool { - if let TokenKind::Ident(current_sym, _) = found - && let TokenType::Keyword(suggested_sym) = expected - { - return current_sym == suggested_sym; - } - false - } - - if **token != parser::TokenType::Token(self.token.kind.clone()) { - let eq = is_ident_eq_keyword(&self.token.kind, &token); - // If the suggestion is a keyword and the found token is an ident, - // the content of which are equal to the suggestion's content, - // we can remove that suggestion (see the `return false` below). - - // If this isn't the case however, and the suggestion is a token the - // content of which is the same as the found token's, we remove it as well. - if !eq { - if let TokenType::Token(kind) = token { - if self.token == *kind { - return false; - } - } - return true; - } - } - false - }) - .cloned() - .collect::<Vec<_>>(); + for exp in edible.iter().chain(inedible.iter()) { + self.expected_token_types.insert(exp.token_type); + } + let mut expected: Vec<_> = self.expected_token_types.iter().collect(); expected.sort_by_cached_key(|x| x.to_string()); expected.dedup(); let sm = self.psess.source_map(); // Special-case "expected `;`" errors. - if expected.contains(&TokenType::Token(token::Semi)) { + if expected.contains(&TokenType::Semi) { // If the user is trying to write a ternary expression, recover it and // return an Err to prevent a cascade of irrelevant diagnostics. if self.prev_token == token::Question @@ -577,7 +546,7 @@ impl<'a> Parser<'a> { || (sm.is_multiline( self.prev_token.span.shrink_to_hi().until(self.token.span.shrink_to_lo()), ) && t == &token::Pound) - }) && !expected.contains(&TokenType::Token(token::Comma)) + }) && !expected.contains(&TokenType::Comma) { // Missing semicolon typo. This is triggered if the next token could either start a // new statement or is a block close. For example: @@ -597,7 +566,7 @@ impl<'a> Parser<'a> { if self.token == TokenKind::EqEq && self.prev_token.is_ident() - && expected.iter().any(|tok| matches!(tok, TokenType::Token(TokenKind::Eq))) + && expected.contains(&TokenType::Eq) { // Likely typo: `=` → `==` in let expr or enum item return Err(self.dcx().create_err(UseEqInstead { span: self.token.span })); @@ -636,15 +605,8 @@ impl<'a> Parser<'a> { // Look for usages of '=>' where '>=' was probably intended if self.token == token::FatArrow - && expected - .iter() - .any(|tok| matches!(tok, TokenType::Operator | TokenType::Token(TokenKind::Le))) - && !expected.iter().any(|tok| { - matches!( - tok, - TokenType::Token(TokenKind::FatArrow) | TokenType::Token(TokenKind::Comma) - ) - }) + && expected.iter().any(|tok| matches!(tok, TokenType::Operator | TokenType::Le)) + && !expected.iter().any(|tok| matches!(tok, TokenType::FatArrow | TokenType::Comma)) { err.span_suggestion( self.token.span, @@ -741,7 +703,7 @@ impl<'a> Parser<'a> { }; if self.check_too_many_raw_str_terminators(&mut err) { - if expected.contains(&TokenType::Token(token::Semi)) && self.eat(&token::Semi) { + if expected.contains(&TokenType::Semi) && self.eat(exp!(Semi)) { let guar = err.emit(); return Ok(guar); } else { @@ -785,17 +747,15 @@ impl<'a> Parser<'a> { let Some((curr_ident, _)) = self.token.ident() else { return; }; - let expected_tokens: &[TokenType] = + let expected_token_types: &[TokenType] = expected.len().checked_sub(10).map_or(&expected, |index| &expected[index..]); - let expected_keywords: Vec<Symbol> = expected_tokens - .iter() - .filter_map(|token| if let TokenType::Keyword(kw) = token { Some(*kw) } else { None }) - .collect(); - - // When there are a few keywords in the last ten elements of `self.expected_tokens` and the current - // token is an identifier, it's probably a misspelled keyword. - // This handles code like `async Move {}`, misspelled `if` in match guard, misspelled `else` in `if`-`else` - // and mispelled `where` in a where clause. + let expected_keywords: Vec<Symbol> = + expected_token_types.iter().filter_map(|token| token.is_keyword()).collect(); + + // When there are a few keywords in the last ten elements of `self.expected_token_types` + // and the current token is an identifier, it's probably a misspelled keyword. This handles + // code like `async Move {}`, misspelled `if` in match guard, misspelled `else` in + // `if`-`else` and mispelled `where` in a where clause. if !expected_keywords.is_empty() && !curr_ident.is_used_keyword() && let Some(misspelled_kw) = find_similar_kw(curr_ident, &expected_keywords) @@ -811,12 +771,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 @@ -1052,7 +1012,7 @@ impl<'a> Parser<'a> { (Err(snapshot_err), Err(err)) => { // We don't know what went wrong, emit the normal error. snapshot_err.cancel(); - self.consume_block(Delimiter::Brace, ConsumeClosingDelim::Yes); + self.consume_block(exp!(OpenBrace), exp!(CloseBrace), ConsumeClosingDelim::Yes); Err(err) } (Ok(_), Ok(mut tail)) => { @@ -1089,7 +1049,7 @@ impl<'a> Parser<'a> { Applicability::MaybeIncorrect, ); let guar = err.emit(); - self.eat_to_tokens(&[&token::CloseDelim(Delimiter::Brace)]); + self.eat_to_tokens(&[exp!(CloseBrace)]); guar } token::OpenDelim(Delimiter::Parenthesis) @@ -1097,7 +1057,7 @@ impl<'a> Parser<'a> { { // We are within a function call or tuple, we can emit the error // and recover. - self.eat_to_tokens(&[&token::CloseDelim(Delimiter::Parenthesis), &token::Comma]); + self.eat_to_tokens(&[exp!(CloseParen), exp!(Comma)]); err.multipart_suggestion_verbose( "you might have meant to open the body of the closure", @@ -1124,11 +1084,11 @@ impl<'a> Parser<'a> { Ok(self.mk_expr_err(lo.to(self.token.span), guar)) } - /// Eats and discards tokens until one of `kets` is encountered. Respects token trees, + /// Eats and discards tokens until one of `closes` is encountered. Respects token trees, /// passes through any errors encountered. Used for error recovery. - pub(super) fn eat_to_tokens(&mut self, kets: &[&TokenKind]) { - if let Err(err) = - self.parse_seq_to_before_tokens(kets, &[], SeqSep::none(), |p| Ok(p.parse_token_tree())) + pub(super) fn eat_to_tokens(&mut self, closes: &[ExpTokenPair<'_>]) { + if let Err(err) = self + .parse_seq_to_before_tokens(closes, &[], SeqSep::none(), |p| Ok(p.parse_token_tree())) { err.cancel(); } @@ -1147,7 +1107,7 @@ impl<'a> Parser<'a> { pub(super) fn check_trailing_angle_brackets( &mut self, segment: &PathSegment, - end: &[&TokenKind], + end: &[ExpTokenPair<'_>], ) -> Option<ErrorGuaranteed> { if !self.may_recover() { return None; @@ -1230,7 +1190,7 @@ impl<'a> Parser<'a> { // second case. if self.look_ahead(position, |t| { trace!("check_trailing_angle_brackets: t={:?}", t); - end.contains(&&t.kind) + end.iter().any(|exp| exp.tok == &t.kind) }) { // Eat from where we started until the end token so that parsing can continue // as if we didn't have those extra angle brackets. @@ -1298,11 +1258,11 @@ impl<'a> Parser<'a> { ) -> PResult<'a, ErrorGuaranteed> { if let ExprKind::Binary(binop, _, _) = &expr.kind && let ast::BinOpKind::Lt = binop.node - && self.eat(&token::Comma) + && self.eat(exp!(Comma)) { let x = self.parse_seq_to_before_end( - &token::Gt, - SeqSep::trailing_allowed(token::Comma), + exp!(Gt), + SeqSep::trailing_allowed(exp!(Comma)), |p| match p.parse_generic_arg(None)? { Some(arg) => Ok(arg), // If we didn't eat a generic arg, then we should error. @@ -1311,7 +1271,7 @@ impl<'a> Parser<'a> { ); match x { Ok((_, _, Recovered::No)) => { - if self.eat(&token::Gt) { + if self.eat(exp!(Gt)) { // We made sense of it. Improve the error message. e.span_suggestion_verbose( binop.span.shrink_to_lo(), @@ -1874,7 +1834,7 @@ impl<'a> Parser<'a> { ty_span: Span, ty: P<Ty>, ) -> PResult<'a, P<T>> { - self.expect(&token::PathSep)?; + self.expect(exp!(PathSep))?; let mut path = ast::Path { segments: ThinVec::new(), span: DUMMY_SP, tokens: None }; self.parse_path_segments(&mut path.segments, T::PATH_STYLE, None)?; @@ -1956,10 +1916,10 @@ impl<'a> Parser<'a> { } pub(super) fn expect_semi(&mut self) -> PResult<'a, ()> { - if self.eat(&token::Semi) || self.recover_colon_as_semi() { + if self.eat(exp!(Semi)) || self.recover_colon_as_semi() { return Ok(()); } - self.expect(&token::Semi).map(drop) // Error unconditionally + self.expect(exp!(Semi)).map(drop) // Error unconditionally } pub(super) fn recover_colon_as_semi(&mut self) -> bool { @@ -2004,15 +1964,15 @@ impl<'a> Parser<'a> { } fn recover_await_macro(&mut self) -> PResult<'a, (Span, P<Expr>, bool)> { - self.expect(&token::Not)?; - self.expect(&token::OpenDelim(Delimiter::Parenthesis))?; + self.expect(exp!(Not))?; + self.expect(exp!(OpenParen))?; let expr = self.parse_expr()?; - self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; + self.expect(exp!(CloseParen))?; Ok((self.prev_token.span, expr, false)) } fn recover_await_prefix(&mut self, await_sp: Span) -> PResult<'a, (Span, P<Expr>, bool)> { - let is_question = self.eat(&token::Question); // Handle `await? <expr>`. + let is_question = self.eat(exp!(Question)); // Handle `await? <expr>`. let expr = if self.token == token::OpenDelim(Delimiter::Brace) { // Handle `await { <expr> }`. // This needs to be handled separately from the next arm to avoid @@ -2074,7 +2034,7 @@ impl<'a> Parser<'a> { let try_span = lo.to(self.token.span); //we take the try!( span self.bump(); //remove ( let is_empty = self.token == token::CloseDelim(Delimiter::Parenthesis); //check if the block is empty - self.consume_block(Delimiter::Parenthesis, ConsumeClosingDelim::No); //eat the block + self.consume_block(exp!(OpenParen), exp!(CloseParen), ConsumeClosingDelim::No); //eat the block let hi = self.token.span; self.bump(); //remove ) let mut err = self.dcx().struct_span_err(lo.to(hi), "use of deprecated `try` macro"); @@ -2130,13 +2090,14 @@ impl<'a> Parser<'a> { pub(super) fn recover_seq_parse_error( &mut self, - delim: Delimiter, + open: ExpTokenPair<'_>, + close: ExpTokenPair<'_>, lo: Span, err: Diag<'a>, ) -> P<Expr> { let guar = err.emit(); // Recover from parse error, callers expect the closing delim to be consumed. - self.consume_block(delim, ConsumeClosingDelim::Yes); + self.consume_block(open, close, ConsumeClosingDelim::Yes); self.mk_expr(lo.to(self.prev_token.span), ExprKind::Err(guar)) } @@ -2225,7 +2186,7 @@ impl<'a> Parser<'a> { } pub(super) fn check_for_for_in_in_typo(&mut self, in_span: Span) { - if self.eat_keyword(kw::In) { + if self.eat_keyword(exp!(In)) { // a common typo: `for _ in in bar {}` self.dcx().emit_err(InInTypo { span: self.prev_token.span, @@ -2366,7 +2327,7 @@ impl<'a> Parser<'a> { pub(super) fn recover_arg_parse(&mut self) -> PResult<'a, (P<ast::Pat>, P<ast::Ty>)> { let pat = self.parse_pat_no_top_alt(Some(Expected::ArgumentName), None)?; - self.expect(&token::Colon)?; + self.expect(exp!(Colon))?; let ty = self.parse_ty()?; self.dcx().emit_err(PatternMethodParamWithoutBody { span: pat.span }); @@ -2384,12 +2345,17 @@ impl<'a> Parser<'a> { Ok(param) } - pub(super) fn consume_block(&mut self, delim: Delimiter, consume_close: ConsumeClosingDelim) { + pub(super) fn consume_block( + &mut self, + open: ExpTokenPair<'_>, + close: ExpTokenPair<'_>, + consume_close: ConsumeClosingDelim, + ) { let mut brace_depth = 0; loop { - if self.eat(&token::OpenDelim(delim)) { + if self.eat(open) { brace_depth += 1; - } else if self.check(&token::CloseDelim(delim)) { + } else if self.check(close) { if brace_depth == 0 { if let ConsumeClosingDelim::Yes = consume_close { // Some of the callers of this method expect to be able to parse the @@ -2545,7 +2511,7 @@ impl<'a> Parser<'a> { match self.recover_const_arg(arg.span(), err) { Ok(arg) => { args.push(AngleBracketedArg::Arg(arg)); - if self.eat(&token::Comma) { + if self.eat(exp!(Comma)) { return Ok(true); // Continue } } @@ -3016,7 +2982,7 @@ impl<'a> Parser<'a> { /// Check for exclusive ranges written as `..<` pub(crate) fn maybe_err_dotdotlt_syntax(&self, maybe_lt: Token, mut err: Diag<'a>) -> Diag<'a> { if maybe_lt == token::Lt - && (self.expected_tokens.contains(&TokenType::Token(token::Gt)) + && (self.expected_token_types.contains(TokenType::Gt) || matches!(self.token.kind, token::Literal(..))) { err.span_suggestion( @@ -3057,17 +3023,10 @@ impl<'a> Parser<'a> { } pub(super) fn recover_vcs_conflict_marker(&mut self) { - if let Err(err) = self.err_vcs_conflict_marker() { - err.emit(); - FatalError.raise(); - } - } - - pub(crate) fn err_vcs_conflict_marker(&mut self) -> PResult<'a, ()> { // <<<<<<< let Some(start) = self.conflict_marker(&TokenKind::BinOp(token::Shl), &TokenKind::Lt) else { - return Ok(()); + return; }; let mut spans = Vec::with_capacity(3); spans.push(start); @@ -3097,7 +3056,7 @@ impl<'a> Parser<'a> { self.bump(); } - let mut err = self.dcx().struct_span_err(spans, "encountered diff marker"); + let mut err = self.dcx().struct_span_fatal(spans, "encountered diff marker"); match middlediff3 { // We're using diff3 Some(middlediff3) => { @@ -3140,15 +3099,15 @@ impl<'a> Parser<'a> { visit <https://git-scm.com/book/en/v2/Git-Tools-Advanced-Merging#_checking_out_conflicts>", ); - Err(err) + err.emit(); } /// Parse and throw away a parenthesized comma separated /// sequence of patterns until `)` is reached. fn skip_pat_list(&mut self) -> PResult<'a, ()> { - while !self.check(&token::CloseDelim(Delimiter::Parenthesis)) { + while !self.check(exp!(CloseParen)) { self.parse_pat_no_top_alt(None, None)?; - if !self.eat(&token::Comma) { + if !self.eat(exp!(Comma)) { return Ok(()); } } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index a2136399b0c..7533e75ffe2 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}; @@ -26,8 +27,7 @@ use rustc_session::errors::{ExprParenthesesNeeded, report_lit_error}; use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP; use rustc_span::source_map::{self, Spanned}; -use rustc_span::symbol::{Ident, Symbol, kw, sym}; -use rustc_span::{BytePos, ErrorGuaranteed, Pos, Span}; +use rustc_span::{BytePos, ErrorGuaranteed, Ident, Pos, Span, Symbol, kw, sym}; use thin_vec::{ThinVec, thin_vec}; use tracing::instrument; @@ -35,10 +35,10 @@ use super::diagnostics::SnapshotParser; use super::pat::{CommaRecoveryMode, Expected, RecoverColon, RecoverComma}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{ - AttrWrapper, BlockMode, ClosureSpans, ForceCollect, Parser, PathStyle, Restrictions, - SemiColonMode, SeqSep, TokenType, Trailing, UsePreAttrPos, + AttrWrapper, BlockMode, ClosureSpans, ExpTokenPair, ForceCollect, Parser, PathStyle, + Restrictions, SemiColonMode, SeqSep, TokenType, Trailing, UsePreAttrPos, }; -use crate::{errors, maybe_recover_from_interpolated_ty_qpath}; +use crate::{errors, exp, maybe_recover_from_interpolated_ty_qpath}; #[derive(Debug)] pub(super) enum DestructuredFloat { @@ -153,7 +153,7 @@ impl<'a> Parser<'a> { return Ok((lhs, parsed_something)); } - self.expected_tokens.push(TokenType::Operator); + self.expected_token_types.insert(TokenType::Operator); while let Some(op) = self.check_assoc_op() { let lhs_span = self.interpolated_or_expr_span(&lhs); let cur_op_span = self.token.span; @@ -279,13 +279,9 @@ impl<'a> Parser<'a> { break; } - let fixity = op.fixity(); - let min_prec = match fixity { + let min_prec = match op.fixity() { Fixity::Right => Bound::Included(prec), - Fixity::Left => Bound::Excluded(prec), - // We currently have no non-associative operators that are not handled above by - // the special cases. The code is here only for future convenience. - Fixity::None => Bound::Excluded(prec), + Fixity::Left | Fixity::None => Bound::Excluded(prec), }; let (rhs, _) = self.with_res(restrictions - Restrictions::STMT_EXPR, |this| { let attrs = this.parse_outer_attributes()?; @@ -337,10 +333,6 @@ impl<'a> Parser<'a> { self.dcx().span_bug(span, "AssocOp should have been handled by special case") } }; - - if let Fixity::None = fixity { - break; - } } Ok((lhs, parsed_something)) @@ -873,9 +865,9 @@ impl<'a> Parser<'a> { /// Parse `mut?` or `raw [ const | mut ]`. fn parse_borrow_modifiers(&mut self) -> (ast::BorrowKind, ast::Mutability) { - if self.check_keyword(kw::Raw) && self.look_ahead(1, Token::is_mutability) { + if self.check_keyword(exp!(Raw)) && self.look_ahead(1, Token::is_mutability) { // `raw [ const | mut ]`. - let found_raw = self.eat_keyword(kw::Raw); + let found_raw = self.eat_keyword(exp!(Raw)); assert!(found_raw); let mutability = self.parse_const_or_mut().unwrap(); (ast::BorrowKind::Raw, mutability) @@ -908,7 +900,7 @@ impl<'a> Parser<'a> { // a `return` which could be suggested otherwise. self.eat_noexpect(&token::Question) } else { - self.eat(&token::Question) + self.eat(exp!(Question)) }; if has_question { // `expr?` @@ -926,7 +918,7 @@ impl<'a> Parser<'a> { self.dcx().emit_err(errors::ExprRArrowCall { span }); true } else { - self.eat(&token::Dot) + self.eat(exp!(Dot)) }; if has_dot { // expr.f @@ -1251,7 +1243,7 @@ impl<'a> Parser<'a> { .map(|args| self.mk_expr(lo.to(self.prev_token.span), self.mk_call(fun, args))); match self.maybe_recover_struct_lit_bad_delims(lo, open_paren, seq, snapshot) { Ok(expr) => expr, - Err(err) => self.recover_seq_parse_error(Delimiter::Parenthesis, lo, err), + Err(err) => self.recover_seq_parse_error(exp!(OpenParen), exp!(CloseParen), lo, err), } } @@ -1268,10 +1260,8 @@ impl<'a> Parser<'a> { match (self.may_recover(), seq, snapshot) { (true, Err(err), Some((mut snapshot, ExprKind::Path(None, path)))) => { snapshot.bump(); // `(` - match snapshot.parse_struct_fields(path.clone(), false, Delimiter::Parenthesis) { - Ok((fields, ..)) - if snapshot.eat(&token::CloseDelim(Delimiter::Parenthesis)) => - { + match snapshot.parse_struct_fields(path.clone(), false, exp!(CloseParen)) { + Ok((fields, ..)) if snapshot.eat(exp!(CloseParen)) => { // We are certain we have `Enum::Foo(a: 3, b: 4)`, suggest // `Enum::Foo { a: 3, b: 4 }` or `Enum::Foo(3, 4)`. self.restore_snapshot(snapshot); @@ -1328,7 +1318,7 @@ impl<'a> Parser<'a> { self.bump(); // `[` let index = self.parse_expr()?; self.suggest_missing_semicolon_before_array(prev_span, open_delim_span)?; - self.expect(&token::CloseDelim(Delimiter::Bracket))?; + self.expect(exp!(CloseBracket))?; Ok(self.mk_expr( lo.to(self.prev_token.span), self.mk_index(base, index, open_delim_span.to(self.prev_token.span)), @@ -1337,12 +1327,12 @@ impl<'a> Parser<'a> { /// Assuming we have just parsed `.`, continue parsing into an expression. fn parse_dot_suffix(&mut self, self_arg: P<Expr>, lo: Span) -> PResult<'a, P<Expr>> { - if self.token.uninterpolated_span().at_least_rust_2018() && self.eat_keyword(kw::Await) { + if self.token.uninterpolated_span().at_least_rust_2018() && self.eat_keyword(exp!(Await)) { return Ok(self.mk_await_expr(self_arg, lo)); } // Post-fix match - if self.eat_keyword(kw::Match) { + if self.eat_keyword(exp!(Match)) { let match_span = self.prev_token.span; self.psess.gated_spans.gate(sym::postfix_match, match_span); return self.parse_match_block(lo, match_span, self_arg, MatchKind::Postfix); @@ -1350,10 +1340,10 @@ impl<'a> Parser<'a> { let fn_span_lo = self.token.span; let mut seg = self.parse_path_segment(PathStyle::Expr, None)?; - self.check_trailing_angle_brackets(&seg, &[&token::OpenDelim(Delimiter::Parenthesis)]); + self.check_trailing_angle_brackets(&seg, &[exp!(OpenParen)]); self.check_turbofish_missing_angle_brackets(&mut seg); - if self.check(&token::OpenDelim(Delimiter::Parenthesis)) { + if self.check(exp!(OpenParen)) { // Method call `expr.f()` let args = self.parse_expr_paren_seq()?; let fn_span = fn_span_lo.to(self.prev_token.span); @@ -1415,18 +1405,18 @@ impl<'a> Parser<'a> { let restrictions = self.restrictions; self.with_res(restrictions - Restrictions::ALLOW_LET, |this| { - // Note: when adding new syntax here, don't forget to adjust `TokenKind::can_begin_expr()`. + // Note: adding new syntax here? Don't forget to adjust `TokenKind::can_begin_expr()`. let lo = this.token.span; if let token::Literal(_) = this.token.kind { // This match arm is a special-case of the `_` match arm below and // could be removed without changing functionality, but it's faster // to have it here, especially for programs with large constants. this.parse_expr_lit() - } else if this.check(&token::OpenDelim(Delimiter::Parenthesis)) { + } else if this.check(exp!(OpenParen)) { this.parse_expr_tuple_parens(restrictions) - } else if this.check(&token::OpenDelim(Delimiter::Brace)) { + } else if this.check(exp!(OpenBrace)) { this.parse_expr_block(None, lo, BlockCheckMode::Default) - } else if this.check(&token::BinOp(token::Or)) || this.check(&token::OrOr) { + } else if this.check(exp!(Or)) || this.check(exp!(OrOr)) { this.parse_expr_closure().map_err(|mut err| { // If the input is something like `if a { 1 } else { 2 } | if a { 3 } else { 4 }` // then suggest parens around the lhs. @@ -1435,41 +1425,41 @@ impl<'a> Parser<'a> { } err }) - } else if this.check(&token::OpenDelim(Delimiter::Bracket)) { - this.parse_expr_array_or_repeat(Delimiter::Bracket) + } else if this.check(exp!(OpenBracket)) { + this.parse_expr_array_or_repeat(exp!(CloseBracket)) } else if this.is_builtin() { this.parse_expr_builtin() } else if this.check_path() { this.parse_expr_path_start() - } else if this.check_keyword(kw::Move) - || this.check_keyword(kw::Static) + } else if this.check_keyword(exp!(Move)) + || this.check_keyword(exp!(Static)) || this.check_const_closure() { this.parse_expr_closure() - } else if this.eat_keyword(kw::If) { + } else if this.eat_keyword(exp!(If)) { this.parse_expr_if() - } else if this.check_keyword(kw::For) { + } else if this.check_keyword(exp!(For)) { if this.choose_generics_over_qpath(1) { this.parse_expr_closure() } else { - assert!(this.eat_keyword(kw::For)); + assert!(this.eat_keyword(exp!(For))); this.parse_expr_for(None, lo) } - } else if this.eat_keyword(kw::While) { + } else if this.eat_keyword(exp!(While)) { this.parse_expr_while(None, lo) } else if let Some(label) = this.eat_label() { this.parse_expr_labeled(label, true) - } else if this.eat_keyword(kw::Loop) { + } else if this.eat_keyword(exp!(Loop)) { this.parse_expr_loop(None, lo).map_err(|mut err| { err.span_label(lo, "while parsing this `loop` expression"); err }) - } else if this.eat_keyword(kw::Match) { + } else if this.eat_keyword(exp!(Match)) { this.parse_expr_match().map_err(|mut err| { err.span_label(lo, "while parsing this `match` expression"); err }) - } else if this.eat_keyword(kw::Unsafe) { + } else if this.eat_keyword(exp!(Unsafe)) { this.parse_expr_block(None, lo, BlockCheckMode::Unsafe(ast::UserProvided)).map_err( |mut err| { err.span_label(lo, "while parsing this `unsafe` expression"); @@ -1481,23 +1471,23 @@ impl<'a> Parser<'a> { } else if this.may_recover() && this.is_do_catch_block() { this.recover_do_catch() } else if this.is_try_block() { - this.expect_keyword(kw::Try)?; + this.expect_keyword(exp!(Try))?; this.parse_try_block(lo) - } else if this.eat_keyword(kw::Return) { + } else if this.eat_keyword(exp!(Return)) { this.parse_expr_return() - } else if this.eat_keyword(kw::Continue) { + } else if this.eat_keyword(exp!(Continue)) { this.parse_expr_continue(lo) - } else if this.eat_keyword(kw::Break) { + } else if this.eat_keyword(exp!(Break)) { this.parse_expr_break() - } else if this.eat_keyword(kw::Yield) { + } else if this.eat_keyword(exp!(Yield)) { this.parse_expr_yield() } else if this.is_do_yeet() { this.parse_expr_yeet() - } else if this.eat_keyword(kw::Become) { + } else if this.eat_keyword(exp!(Become)) { this.parse_expr_become() - } else if this.check_keyword(kw::Let) { + } else if this.check_keyword(exp!(Let)) { this.parse_expr_let(restrictions) - } else if this.eat_keyword(kw::Underscore) { + } else if this.eat_keyword(exp!(Underscore)) { Ok(this.mk_expr(this.prev_token.span, ExprKind::Underscore)) } else if this.token.uninterpolated_span().at_least_rust_2018() { // `Span::at_least_rust_2018()` is somewhat expensive; don't get it repeatedly. @@ -1505,11 +1495,11 @@ impl<'a> Parser<'a> { // check for `gen {}` and `gen move {}` // or `async gen {}` and `async gen move {}` && (this.is_gen_block(kw::Gen, 0) - || (this.check_keyword(kw::Async) && this.is_gen_block(kw::Gen, 1))) + || (this.check_keyword(exp!(Async)) && this.is_gen_block(kw::Gen, 1))) { // FIXME: (async) gen closures aren't yet parsed. this.parse_gen_block() - } else if this.check_keyword(kw::Async) { + } else if this.check_keyword(exp!(Async)) { // FIXME(gen_blocks): Parse `gen async` and suggest swap if this.is_gen_block(kw::Async, 0) { // Check for `async {` and `async move {`, @@ -1541,15 +1531,20 @@ impl<'a> Parser<'a> { fn parse_expr_tuple_parens(&mut self, restrictions: Restrictions) -> PResult<'a, P<Expr>> { let lo = self.token.span; - self.expect(&token::OpenDelim(Delimiter::Parenthesis))?; + self.expect(exp!(OpenParen))?; let (es, trailing_comma) = match self.parse_seq_to_end( - &token::CloseDelim(Delimiter::Parenthesis), - SeqSep::trailing_allowed(token::Comma), + exp!(CloseParen), + SeqSep::trailing_allowed(exp!(Comma)), |p| p.parse_expr_catch_underscore(restrictions.intersection(Restrictions::ALLOW_LET)), ) { Ok(x) => x, Err(err) => { - return Ok(self.recover_seq_parse_error(Delimiter::Parenthesis, lo, err)); + return Ok(self.recover_seq_parse_error( + exp!(OpenParen), + exp!(CloseParen), + lo, + err, + )); } }; let kind = if es.len() == 1 && matches!(trailing_comma, Trailing::No) { @@ -1563,25 +1558,24 @@ impl<'a> Parser<'a> { self.maybe_recover_from_bad_qpath(expr) } - fn parse_expr_array_or_repeat(&mut self, close_delim: Delimiter) -> PResult<'a, P<Expr>> { + fn parse_expr_array_or_repeat(&mut self, close: ExpTokenPair<'_>) -> PResult<'a, P<Expr>> { let lo = self.token.span; self.bump(); // `[` or other open delim - let close = &token::CloseDelim(close_delim); let kind = if self.eat(close) { // Empty vector ExprKind::Array(ThinVec::new()) } else { // Non-empty vector let first_expr = self.parse_expr()?; - if self.eat(&token::Semi) { + if self.eat(exp!(Semi)) { // Repeating array syntax: `[ 0; 512 ]` let count = self.parse_expr_anon_const()?; self.expect(close)?; ExprKind::Repeat(first_expr, count) - } else if self.eat(&token::Comma) { + } else if self.eat(exp!(Comma)) { // Vector with two or more elements. - let sep = SeqSep::trailing_allowed(token::Comma); + let sep = SeqSep::trailing_allowed(exp!(Comma)); let (mut exprs, _) = self.parse_seq_to_end(close, sep, |p| p.parse_expr())?; exprs.insert(0, first_expr); ExprKind::Array(exprs) @@ -1615,7 +1609,7 @@ impl<'a> Parser<'a> { }; // `!`, as an operator, is prefix, so we know this isn't that. - let (span, kind) = if self.eat(&token::Not) { + let (span, kind) = if self.eat(exp!(Not)) { // MACRO INVOCATION expression if qself.is_some() { self.dcx().emit_err(errors::MacroInvocationWithQualifiedPath(path.span)); @@ -1623,7 +1617,7 @@ impl<'a> Parser<'a> { let lo = path.span; let mac = P(MacCall { path, args: self.parse_delim_args()? }); (lo.to(self.prev_token.span), ExprKind::MacCall(mac)) - } else if self.check(&token::OpenDelim(Delimiter::Brace)) + } else if self.check(exp!(OpenBrace)) && let Some(expr) = self.maybe_parse_struct_expr(&qself, &path) { if qself.is_some() { @@ -1646,13 +1640,13 @@ impl<'a> Parser<'a> { ) -> PResult<'a, P<Expr>> { let lo = label_.ident.span; let label = Some(label_); - let ate_colon = self.eat(&token::Colon); + let ate_colon = self.eat(exp!(Colon)); let tok_sp = self.token.span; - let expr = if self.eat_keyword(kw::While) { + let expr = if self.eat_keyword(exp!(While)) { self.parse_expr_while(label, lo) - } else if self.eat_keyword(kw::For) { + } else if self.eat_keyword(exp!(For)) { self.parse_expr_for(label, lo) - } else if self.eat_keyword(kw::Loop) { + } else if self.eat_keyword(exp!(Loop)) { self.parse_expr_loop(label, lo) } else if self.check_noexpect(&token::OpenDelim(Delimiter::Brace)) || self.token.is_whole_block() @@ -1958,7 +1952,7 @@ impl<'a> Parser<'a> { self.psess.gated_spans.gate(sym::builtin_syntax, ident.span); self.bump(); - self.expect(&TokenKind::OpenDelim(Delimiter::Parenthesis))?; + self.expect(exp!(OpenParen))?; let ret = if let Some(res) = parse(self, lo, ident)? { Ok(res) } else { @@ -1968,7 +1962,7 @@ impl<'a> Parser<'a> { }); return Err(err); }; - self.expect(&TokenKind::CloseDelim(Delimiter::Parenthesis))?; + self.expect(exp!(CloseParen))?; ret } @@ -1976,14 +1970,12 @@ impl<'a> Parser<'a> { /// Built-in macro for `offset_of!` expressions. pub(crate) fn parse_expr_offset_of(&mut self, lo: Span) -> PResult<'a, P<Expr>> { let container = self.parse_ty()?; - self.expect(&TokenKind::Comma)?; + self.expect(exp!(Comma))?; let fields = self.parse_floating_field_access()?; let trailing_comma = self.eat_noexpect(&TokenKind::Comma); - if let Err(mut e) = - self.expect_one_of(&[], &[TokenKind::CloseDelim(Delimiter::Parenthesis)]) - { + if let Err(mut e) = self.expect_one_of(&[], &[exp!(CloseParen)]) { if trailing_comma { e.note("unexpected third argument to offset_of"); } else { @@ -2006,7 +1998,7 @@ impl<'a> Parser<'a> { /// Built-in macro for type ascription expressions. pub(crate) fn parse_expr_type_ascribe(&mut self, lo: Span) -> PResult<'a, P<Expr>> { let expr = self.parse_expr()?; - self.expect(&token::Comma)?; + self.expect(exp!(Comma))?; let ty = self.parse_ty()?; let span = lo.to(self.token.span); Ok(self.mk_expr(span, ExprKind::Type(expr, ty))) @@ -2018,7 +2010,7 @@ impl<'a> Parser<'a> { kind: UnsafeBinderCastKind, ) -> PResult<'a, P<Expr>> { let expr = self.parse_expr()?; - let ty = if self.eat(&TokenKind::Comma) { Some(self.parse_ty()?) } else { None }; + let ty = if self.eat(exp!(Comma)) { Some(self.parse_ty()?) } else { None }; let span = lo.to(self.token.span); Ok(self.mk_expr(span, ExprKind::UnsafeBinderCast(kind, expr, ty))) } @@ -2214,7 +2206,7 @@ impl<'a> Parser<'a> { } let lo = self.token.span; - let minus_present = self.eat(&token::BinOp(token::Minus)); + let minus_present = self.eat(exp!(Minus)); let (token_lit, span) = self.parse_token_lit()?; let expr = self.mk_expr(span, ExprKind::Lit(token_lit)); @@ -2236,7 +2228,7 @@ impl<'a> Parser<'a> { /// expression. fn maybe_suggest_brackets_instead_of_braces(&mut self, lo: Span) -> Option<P<Expr>> { let mut snapshot = self.create_snapshot_for_diagnostic(); - match snapshot.parse_expr_array_or_repeat(Delimiter::Brace) { + match snapshot.parse_expr_array_or_repeat(exp!(CloseBrace)) { Ok(arr) => { let guar = self.dcx().emit_err(errors::ArrayBracketsInsteadOfSpaces { span: arr.span, @@ -2272,8 +2264,8 @@ impl<'a> Parser<'a> { let mut snapshot = self.create_snapshot_for_diagnostic(); snapshot.bump(); match snapshot.parse_seq_to_before_end( - &token::CloseDelim(Delimiter::Bracket), - SeqSep::trailing_allowed(token::Comma), + exp!(CloseBracket), + SeqSep::trailing_allowed(exp!(Comma)), |p| p.parse_expr(), ) { Ok(_) @@ -2337,7 +2329,7 @@ impl<'a> Parser<'a> { let lo = self.token.span; let before = self.prev_token.clone(); - let binder = if self.check_keyword(kw::For) { + let binder = if self.check_keyword(exp!(For)) { let lo = self.token.span; let (lifetime_defs, _) = self.parse_late_bound_lifetime_defs()?; let span = lo.to(self.prev_token.span); @@ -2352,7 +2344,7 @@ impl<'a> Parser<'a> { let constness = self.parse_closure_constness(); let movability = - if self.eat_keyword(kw::Static) { Movability::Static } else { Movability::Movable }; + if self.eat_keyword(exp!(Static)) { Movability::Static } else { Movability::Movable }; let coroutine_kind = if self.token.uninterpolated_span().at_least_rust_2018() { self.parse_coroutine_kind(Case::Sensitive) @@ -2393,7 +2385,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 @@ -2432,10 +2425,10 @@ impl<'a> Parser<'a> { /// Parses an optional `move` prefix to a closure-like construct. fn parse_capture_clause(&mut self) -> PResult<'a, CaptureBy> { - if self.eat_keyword(kw::Move) { + if self.eat_keyword(exp!(Move)) { let move_kw_span = self.prev_token.span; // Check for `move async` and recover - if self.check_keyword(kw::Async) { + if self.check_keyword(exp!(Async)) { let move_async_span = self.token.span.with_lo(self.prev_token.span.data().lo); Err(self .dcx() @@ -2452,15 +2445,15 @@ impl<'a> Parser<'a> { fn parse_fn_block_decl(&mut self) -> PResult<'a, (P<FnDecl>, Span)> { let arg_start = self.token.span.lo(); - let inputs = if self.eat(&token::OrOr) { + let inputs = if self.eat(exp!(OrOr)) { ThinVec::new() } else { - self.expect(&token::BinOp(token::Or))?; + self.expect(exp!(Or))?; let args = self .parse_seq_to_before_tokens( - &[&token::BinOp(token::Or)], + &[exp!(Or)], &[&token::OrOr], - SeqSep::trailing_allowed(token::Comma), + SeqSep::trailing_allowed(exp!(Comma)), |p| p.parse_fn_block_param(), )? .0; @@ -2480,7 +2473,7 @@ impl<'a> Parser<'a> { let attrs = self.parse_outer_attributes()?; self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| { let pat = this.parse_pat_no_top_alt(Some(Expected::ParameterName), None)?; - let ty = if this.eat(&token::Colon) { + let ty = if this.eat(exp!(Colon)) { this.parse_ty()? } else { this.mk_ty(pat.span, TyKind::Infer) @@ -2565,7 +2558,7 @@ impl<'a> Parser<'a> { } else { let attrs = self.parse_outer_attributes()?; // For recovery. let maybe_fatarrow = self.token.clone(); - let block = if self.check(&token::OpenDelim(Delimiter::Brace)) { + let block = if self.check(exp!(OpenBrace)) { self.parse_block()? } else if let Some(block) = recover_block_from_condition(self) { block @@ -2608,7 +2601,7 @@ impl<'a> Parser<'a> { self.error_on_if_block_attrs(lo, false, block.span, attrs); block }; - let els = if self.eat_keyword(kw::Else) { Some(self.parse_expr_else()?) } else { None }; + let els = if self.eat_keyword(exp!(Else)) { Some(self.parse_expr_else()?) } else { None }; Ok(self.mk_expr(lo.to(self.prev_token.span), ExprKind::If(cond, thn, els))) } @@ -2661,7 +2654,7 @@ impl<'a> Parser<'a> { }); self.bump(); } else { - self.expect(&token::Eq)?; + self.expect(exp!(Eq))?; } let attrs = self.parse_outer_attributes()?; let (expr, _) = @@ -2674,9 +2667,9 @@ impl<'a> Parser<'a> { fn parse_expr_else(&mut self) -> PResult<'a, P<Expr>> { let else_span = self.prev_token.span; // `else` let attrs = self.parse_outer_attributes()?; // For recovery. - let expr = if self.eat_keyword(kw::If) { + let expr = if self.eat_keyword(exp!(If)) { ensure_sufficient_stack(|| self.parse_expr_if())? - } else if self.check(&TokenKind::OpenDelim(Delimiter::Brace)) { + } else if self.check(exp!(OpenBrace)) { self.parse_simple_block()? } else { let snapshot = self.create_snapshot_for_diagnostic(); @@ -2718,7 +2711,7 @@ impl<'a> Parser<'a> { // while true {} // } // ^ - if self.check(&TokenKind::OpenDelim(Delimiter::Brace)) + if self.check(exp!(OpenBrace)) && (classify::expr_requires_semi_to_be_stmt(&cond) || matches!(cond.kind, ExprKind::MacCall(..))) => @@ -2804,7 +2797,7 @@ impl<'a> Parser<'a> { begin_paren, ) { (Ok(pat), _) => pat, // Happy path. - (Err(err), Some((start_span, left))) if self.eat_keyword(kw::In) => { + (Err(err), Some((start_span, left))) if self.eat_keyword(exp!(In)) => { // We know for sure we have seen `for ($SOMETHING in`. In the happy path this would // happen right before the return of this method. let attrs = self.parse_outer_attributes()?; @@ -2838,7 +2831,7 @@ impl<'a> Parser<'a> { } (Err(err), _) => return Err(err), // Some other error, bubble up. }; - if !self.eat_keyword(kw::In) { + if !self.eat_keyword(exp!(In)) { self.error_missing_in_for_loop(); } self.check_for_for_in_in_typo(self.prev_token.span); @@ -2850,7 +2843,7 @@ impl<'a> Parser<'a> { /// Parses `for await? <src_pat> in <src_expr> <src_loop_block>` (`for` token already eaten). fn parse_expr_for(&mut self, opt_label: Option<Label>, lo: Span) -> PResult<'a, P<Expr>> { let is_await = - self.token.uninterpolated_span().at_least_rust_2018() && self.eat_keyword(kw::Await); + self.token.uninterpolated_span().at_least_rust_2018() && self.eat_keyword(exp!(Await)); if is_await { self.psess.gated_spans.gate(sym::async_for_loop, self.prev_token.span); @@ -2980,7 +2973,7 @@ impl<'a> Parser<'a> { scrutinee: P<Expr>, match_kind: MatchKind, ) -> PResult<'a, P<Expr>> { - if let Err(mut e) = self.expect(&token::OpenDelim(Delimiter::Brace)) { + if let Err(mut e) = self.expect(exp!(OpenBrace)) { if self.token == token::Semi { e.span_suggestion_short( match_span, @@ -3120,7 +3113,7 @@ impl<'a> Parser<'a> { let span_before_body = this.prev_token.span; let arm_body; - let is_fat_arrow = this.check(&token::FatArrow); + let is_fat_arrow = this.check(exp!(FatArrow)); let is_almost_fat_arrow = TokenKind::FatArrow .similar_tokens() .is_some_and(|similar_tokens| similar_tokens.contains(&this.token.kind)); @@ -3133,17 +3126,15 @@ impl<'a> Parser<'a> { let mut result = if armless { // A pattern without a body, allowed for never patterns. arm_body = None; - this.expect_one_of(&[token::Comma], &[token::CloseDelim(Delimiter::Brace)]).map( - |x| { - // Don't gate twice - if !pat.contains_never_pattern() { - this.psess.gated_spans.gate(sym::never_patterns, pat.span); - } - x - }, - ) + this.expect_one_of(&[exp!(Comma)], &[exp!(CloseBrace)]).map(|x| { + // Don't gate twice + if !pat.contains_never_pattern() { + this.psess.gated_spans.gate(sym::never_patterns, pat.span); + } + x + }) } else { - if let Err(mut err) = this.expect(&token::FatArrow) { + if let Err(mut err) = this.expect(exp!(FatArrow)) { // We might have a `=>` -> `=` or `->` typo (issue #89396). if is_almost_fat_arrow { err.span_suggestion( @@ -3183,7 +3174,7 @@ impl<'a> Parser<'a> { if !require_comma { arm_body = Some(expr); // Eat a comma if it exists, though. - let _ = this.eat(&token::Comma); + let _ = this.eat(exp!(Comma)); Ok(Recovered::No) } else if let Some((span, guar)) = this.parse_arm_body_missing_braces(&expr, arrow_span) @@ -3194,42 +3185,40 @@ impl<'a> Parser<'a> { } else { let expr_span = expr.span; arm_body = Some(expr); - this.expect_one_of(&[token::Comma], &[token::CloseDelim(Delimiter::Brace)]) - .map_err(|mut err| { - if this.token == token::FatArrow { - let sm = this.psess.source_map(); - if let Ok(expr_lines) = sm.span_to_lines(expr_span) - && let Ok(arm_start_lines) = sm.span_to_lines(arm_start_span) - && arm_start_lines.lines[0].end_col - == expr_lines.lines[0].end_col - && expr_lines.lines.len() == 2 - { - // We check whether there's any trailing code in the parse span, - // if there isn't, we very likely have the following: - // - // X | &Y => "y" - // | -- - missing comma - // | | - // | arrow_span - // X | &X => "x" - // | - ^^ self.token.span - // | | - // | parsed until here as `"y" & X` - err.span_suggestion_short( - arm_start_span.shrink_to_hi(), - "missing a comma here to end this `match` arm", - ",", - Applicability::MachineApplicable, - ); - } - } else { - err.span_label( - arrow_span, - "while parsing the `match` arm starting here", + this.expect_one_of(&[exp!(Comma)], &[exp!(CloseBrace)]).map_err(|mut err| { + if this.token == token::FatArrow { + let sm = this.psess.source_map(); + if let Ok(expr_lines) = sm.span_to_lines(expr_span) + && let Ok(arm_start_lines) = sm.span_to_lines(arm_start_span) + && arm_start_lines.lines[0].end_col == expr_lines.lines[0].end_col + && expr_lines.lines.len() == 2 + { + // We check whether there's any trailing code in the parse span, + // if there isn't, we very likely have the following: + // + // X | &Y => "y" + // | -- - missing comma + // | | + // | arrow_span + // X | &X => "x" + // | - ^^ self.token.span + // | | + // | parsed until here as `"y" & X` + err.span_suggestion_short( + arm_start_span.shrink_to_hi(), + "missing a comma here to end this `match` arm", + ",", + Applicability::MachineApplicable, ); } - err - }) + } else { + err.span_label( + arrow_span, + "while parsing the `match` arm starting here", + ); + } + err + }) } }; @@ -3266,7 +3255,7 @@ impl<'a> Parser<'a> { ) .map_err(|err| err.cancel()) .is_ok(); - if pattern_follows && snapshot.check(&TokenKind::FatArrow) { + if pattern_follows && snapshot.check(exp!(FatArrow)) { err.cancel(); let guar = this.dcx().emit_err(errors::MissingCommaAfterMatchArm { span: arm_span.shrink_to_hi(), @@ -3308,7 +3297,7 @@ impl<'a> Parser<'a> { _ => (false, true), } } - if !self.eat_keyword(kw::If) { + if !self.eat_keyword(exp!(If)) { // No match arm guard present. return Ok(None); } @@ -3383,7 +3372,7 @@ impl<'a> Parser<'a> { // errors. self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore); let msg = "you might have meant to start a match arm after the match guard"; - if self.eat(&token::CloseDelim(Delimiter::Brace)) { + if self.eat(exp!(CloseBrace)) { let applicability = if self.token != token::FatArrow { // We have high confidence that we indeed didn't have a struct // literal in the match guard, but rather we had some operation @@ -3408,7 +3397,7 @@ impl<'a> Parser<'a> { /// Parses a `try {...}` expression (`try` token already eaten). fn parse_try_block(&mut self, span_lo: Span) -> PResult<'a, P<Expr>> { let (attrs, body) = self.parse_inner_attrs_and_block()?; - if self.eat_keyword(kw::Catch) { + if self.eat_keyword(exp!(Catch)) { Err(self.dcx().create_err(errors::CatchAfterTry { span: self.prev_token.span })) } else { let span = span_lo.to(body.span); @@ -3439,10 +3428,10 @@ impl<'a> Parser<'a> { /// Parses an `async move? {...}` or `gen move? {...}` expression. fn parse_gen_block(&mut self) -> PResult<'a, P<Expr>> { let lo = self.token.span; - let kind = if self.eat_keyword(kw::Async) { - if self.eat_keyword(kw::Gen) { GenBlockKind::AsyncGen } else { GenBlockKind::Async } + let kind = if self.eat_keyword(exp!(Async)) { + if self.eat_keyword(exp!(Gen)) { GenBlockKind::AsyncGen } else { GenBlockKind::Async } } else { - assert!(self.eat_keyword(kw::Gen)); + assert!(self.eat_keyword(exp!(Gen))); GenBlockKind::Gen }; match kind { @@ -3503,7 +3492,7 @@ impl<'a> Parser<'a> { ) -> Option<PResult<'a, P<Expr>>> { let struct_allowed = !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL); if struct_allowed || self.is_certainly_not_a_block() { - if let Err(err) = self.expect(&token::OpenDelim(Delimiter::Brace)) { + if let Err(err) = self.expect(exp!(OpenBrace)) { return Some(Err(err)); } let expr = self.parse_expr_struct(qself.clone(), path.clone(), true); @@ -3526,7 +3515,7 @@ impl<'a> Parser<'a> { &mut self, pth: ast::Path, recover: bool, - close_delim: Delimiter, + close: ExpTokenPair<'_>, ) -> PResult< 'a, ( @@ -3545,11 +3534,11 @@ impl<'a> Parser<'a> { errors::HelpUseLatestEdition::new().add_to_diag(e); }; - while self.token != token::CloseDelim(close_delim) { - if self.eat(&token::DotDot) || self.recover_struct_field_dots(close_delim) { + while self.token != *close.tok { + if self.eat(exp!(DotDot)) || self.recover_struct_field_dots(close.tok) { let exp_span = self.prev_token.span; // We permit `.. }` on the left-hand side of a destructuring assignment. - if self.check(&token::CloseDelim(close_delim)) { + if self.check(close) { base = ast::StructRest::Rest(self.prev_token.span); break; } @@ -3624,7 +3613,7 @@ impl<'a> Parser<'a> { Applicability::MaybeIncorrect, ); } - if in_if_guard && close_delim == Delimiter::Brace { + if in_if_guard && close.token_type == TokenType::CloseBrace { return Err(e); } @@ -3654,9 +3643,9 @@ impl<'a> Parser<'a> { let is_shorthand = parsed_field.as_ref().is_ok_and(|f| f.is_shorthand); // A shorthand field can be turned into a full field with `:`. // We should point this out. - self.check_or_expected(!is_shorthand, TokenType::Token(token::Colon)); + self.check_or_expected(!is_shorthand, TokenType::Colon); - match self.expect_one_of(&[token::Comma], &[token::CloseDelim(close_delim)]) { + match self.expect_one_of(&[exp!(Comma)], &[close]) { Ok(_) => { if let Ok(f) = parsed_field.or_else(|guar| field_ident(self, guar).ok_or(guar)) { @@ -3688,7 +3677,7 @@ impl<'a> Parser<'a> { fields.push(f); } self.recover_stmt_(SemiColonMode::Comma, BlockMode::Ignore); - let _ = self.eat(&token::Comma); + let _ = self.eat(exp!(Comma)); } } } @@ -3704,9 +3693,9 @@ impl<'a> Parser<'a> { ) -> PResult<'a, P<Expr>> { let lo = pth.span; let (fields, base, recovered_async) = - self.parse_struct_fields(pth.clone(), recover, Delimiter::Brace)?; + self.parse_struct_fields(pth.clone(), recover, exp!(CloseBrace))?; let span = lo.to(self.token.span); - self.expect(&token::CloseDelim(Delimiter::Brace))?; + self.expect(exp!(CloseBrace))?; let expr = if let Some(guar) = recovered_async { ExprKind::Err(guar) } else { @@ -3726,10 +3715,8 @@ impl<'a> Parser<'a> { self.recover_stmt(); } - fn recover_struct_field_dots(&mut self, close_delim: Delimiter) -> bool { - if !self.look_ahead(1, |t| *t == token::CloseDelim(close_delim)) - && self.eat(&token::DotDotDot) - { + fn recover_struct_field_dots(&mut self, close: &TokenKind) -> bool { + if !self.look_ahead(1, |t| t == close) && self.eat(exp!(DotDotDot)) { // recover from typo of `...`, suggest `..` let span = self.prev_token.span; self.dcx().emit_err(errors::MissingDotDot { token_span: span, sugg_span: span }); diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index 76ecb77d750..b1b84b0b701 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -4,8 +4,7 @@ use rustc_ast::{ WhereClause, token, }; use rustc_errors::{Applicability, PResult}; -use rustc_span::Span; -use rustc_span::symbol::{Ident, kw}; +use rustc_span::{Ident, Span, kw}; use thin_vec::ThinVec; use super::{ForceCollect, Parser, Trailing, UsePreAttrPos}; @@ -14,6 +13,7 @@ use crate::errors::{ UnexpectedSelfInGenericParameters, WhereClauseBeforeTupleStructBody, WhereClauseBeforeTupleStructBodySugg, }; +use crate::exp; enum PredicateKindOrStructBody { PredicateKind(ast::WherePredicateKind), @@ -53,7 +53,7 @@ impl<'a> Parser<'a> { // Parse optional colon and param bounds. let mut colon_span = None; - let bounds = if self.eat(&token::Colon) { + let bounds = if self.eat(exp!(Colon)) { colon_span = Some(self.prev_token.span); // recover from `impl Trait` in type param bound if self.token.is_keyword(kw::Impl) { @@ -90,7 +90,7 @@ impl<'a> Parser<'a> { Vec::new() }; - let default = if self.eat(&token::Eq) { Some(self.parse_ty()?) } else { None }; + let default = if self.eat(exp!(Eq)) { Some(self.parse_ty()?) } else { None }; Ok(GenericParam { ident, id: ast::DUMMY_NODE_ID, @@ -108,13 +108,13 @@ impl<'a> Parser<'a> { ) -> PResult<'a, GenericParam> { let const_span = self.token.span; - self.expect_keyword(kw::Const)?; + self.expect_keyword(exp!(Const))?; let ident = self.parse_ident()?; - self.expect(&token::Colon)?; + self.expect(exp!(Colon))?; let ty = self.parse_ty()?; // Parse optional const generics default value. - let default = if self.eat(&token::Eq) { Some(self.parse_const_arg()?) } else { None }; + let default = if self.eat(exp!(Eq)) { Some(self.parse_const_arg()?) } else { None }; Ok(GenericParam { ident, @@ -133,11 +133,11 @@ impl<'a> Parser<'a> { mistyped_const_ident: Ident, ) -> PResult<'a, GenericParam> { let ident = self.parse_ident()?; - self.expect(&token::Colon)?; + self.expect(exp!(Colon))?; let ty = self.parse_ty()?; // Parse optional const generics default value. - let default = if self.eat(&token::Eq) { Some(self.parse_const_arg()?) } else { None }; + let default = if self.eat(exp!(Eq)) { Some(self.parse_const_arg()?) } else { None }; self.dcx() .struct_span_err( @@ -178,13 +178,13 @@ impl<'a> Parser<'a> { .emit_err(UnexpectedSelfInGenericParameters { span: this.prev_token.span }); // Eat a trailing comma, if it exists. - let _ = this.eat(&token::Comma); + let _ = this.eat(exp!(Comma)); } let param = if this.check_lifetime() { let lifetime = this.expect_lifetime(); // Parse lifetime parameter. - let (colon_span, bounds) = if this.eat(&token::Colon) { + let (colon_span, bounds) = if this.eat(exp!(Colon)) { (Some(this.prev_token.span), this.parse_lt_param_bounds()) } else { (None, Vec::new()) @@ -210,7 +210,7 @@ impl<'a> Parser<'a> { is_placeholder: false, colon_span, }) - } else if this.check_keyword(kw::Const) { + } else if this.check_keyword(exp!(Const)) { // Parse const parameter. Some(this.parse_const_param(attrs)?) } else if this.check_ident() { @@ -247,7 +247,7 @@ impl<'a> Parser<'a> { return Ok((None, Trailing::No, UsePreAttrPos::No)); }; - if !this.eat(&token::Comma) { + if !this.eat(exp!(Comma)) { done = true; } // We just ate the comma, so no need to capture the trailing token. @@ -325,7 +325,7 @@ impl<'a> Parser<'a> { }; let mut tuple_struct_body = None; - if !self.eat_keyword(kw::Where) { + if !self.eat_keyword(exp!(Where)) { return Ok((where_clause, None)); } where_clause.has_where_token = true; @@ -345,7 +345,7 @@ impl<'a> Parser<'a> { let kind = if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) { let lifetime = self.expect_lifetime(); // Bounds starting with a colon are mandatory, but possibly empty. - self.expect(&token::Colon)?; + self.expect(exp!(Colon))?; let bounds = self.parse_lt_param_bounds(); ast::WherePredicateKind::RegionPredicate(ast::WhereRegionPredicate { lifetime, @@ -371,7 +371,7 @@ impl<'a> Parser<'a> { }); let prev_token = self.prev_token.span; - let ate_comma = self.eat(&token::Comma); + let ate_comma = self.eat(exp!(Comma)); if self.eat_keyword_noexpect(kw::Where) { self.dcx().emit_err(MultipleWhereClauses { @@ -465,7 +465,7 @@ impl<'a> Parser<'a> { // Parse type with mandatory colon and (possibly empty) bounds, // or with mandatory equality sign and the second type. let ty = self.parse_ty_for_where_clause()?; - if self.eat(&token::Colon) { + if self.eat(exp!(Colon)) { let bounds = self.parse_generic_bounds()?; Ok(ast::WherePredicateKind::BoundPredicate(ast::WhereBoundPredicate { bound_generic_params: lifetime_defs, @@ -474,7 +474,7 @@ impl<'a> Parser<'a> { })) // FIXME: Decide what should be used here, `=` or `==`. // FIXME: We are just dropping the binders in lifetime_defs on the floor here. - } else if self.eat(&token::Eq) || self.eat(&token::EqEq) { + } else if self.eat(exp!(Eq)) || self.eat(exp!(EqEq)) { let rhs_ty = self.parse_ty()?; Ok(ast::WherePredicateKind::EqPredicate(ast::WhereEqPredicate { lhs_ty: ty, rhs_ty })) } else { diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index e27fc963eb9..f3e56be9f6e 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -13,37 +13,36 @@ use rustc_errors::codes::*; use rustc_errors::{Applicability, PResult, StashKey, struct_span_code_err}; use rustc_span::edit_distance::edit_distance; use rustc_span::edition::Edition; -use rustc_span::symbol::{Ident, Symbol, kw, sym}; -use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, source_map}; +use rustc_span::{DUMMY_SP, ErrorGuaranteed, Ident, Span, Symbol, kw, source_map, sym}; use thin_vec::{ThinVec, thin_vec}; use tracing::debug; use super::diagnostics::{ConsumeClosingDelim, dummy_arg}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{ - AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle, Trailing, UsePreAttrPos, + AttrWrapper, ExpKeywordPair, ExpTokenPair, FollowedByType, ForceCollect, Parser, PathStyle, + Trailing, UsePreAttrPos, }; use crate::errors::{self, MacroExpandsToAdtField}; -use crate::{fluent_generated as fluent, maybe_whole}; +use crate::{exp, fluent_generated as fluent, maybe_whole}; impl<'a> Parser<'a> { /// Parses a source module as a crate. This is the main entry point for the parser. pub fn parse_crate_mod(&mut self) -> PResult<'a, ast::Crate> { - let (attrs, items, spans) = self.parse_mod(&token::Eof)?; + let (attrs, items, spans) = self.parse_mod(exp!(Eof))?; Ok(ast::Crate { attrs, items, spans, id: DUMMY_NODE_ID, is_placeholder: false }) } /// Parses a `mod <foo> { ... }` or `mod <foo>;` item. fn parse_item_mod(&mut self, attrs: &mut AttrVec) -> PResult<'a, ItemInfo> { let safety = self.parse_safety(Case::Sensitive); - self.expect_keyword(kw::Mod)?; + self.expect_keyword(exp!(Mod))?; let id = self.parse_ident()?; - let mod_kind = if self.eat(&token::Semi) { + let mod_kind = if self.eat(exp!(Semi)) { ModKind::Unloaded } else { - self.expect(&token::OpenDelim(Delimiter::Brace))?; - let (inner_attrs, items, inner_span) = - self.parse_mod(&token::CloseDelim(Delimiter::Brace))?; + self.expect(exp!(OpenBrace))?; + let (inner_attrs, items, inner_span) = self.parse_mod(exp!(CloseBrace))?; attrs.extend(inner_attrs); ModKind::Loaded(items, Inline::Yes, inner_span, Ok(())) }; @@ -56,7 +55,7 @@ impl<'a> Parser<'a> { /// - `}` for mod items pub fn parse_mod( &mut self, - term: &TokenKind, + term: ExpTokenPair<'_>, ) -> PResult<'a, (AttrVec, ThinVec<P<Item>>, ModSpans)> { let lo = self.token.span; let attrs = self.parse_inner_attributes()?; @@ -210,15 +209,15 @@ impl<'a> Parser<'a> { let check_pub = def == &Defaultness::Final; let mut def_ = || mem::replace(def, Defaultness::Final); - let info = if self.eat_keyword_case(kw::Use, case) { + let info = if self.eat_keyword_case(exp!(Use), case) { self.parse_use_item()? } else if self.check_fn_front_matter(check_pub, case) { // FUNCTION ITEM let (ident, sig, generics, body) = self.parse_fn(attrs, fn_parse_mode, lo, vis, case)?; (ident, ItemKind::Fn(Box::new(Fn { defaultness: def_(), sig, generics, body }))) - } else if self.eat_keyword(kw::Extern) { - if self.eat_keyword(kw::Crate) { + } else if self.eat_keyword(exp!(Extern)) { + if self.eat_keyword(exp!(Crate)) { // EXTERN CRATE self.parse_item_extern_crate()? } else { @@ -228,7 +227,7 @@ impl<'a> Parser<'a> { } else if self.is_unsafe_foreign_mod() { // EXTERN BLOCK let safety = self.parse_safety(Case::Sensitive); - self.expect_keyword(kw::Extern)?; + self.expect_keyword(exp!(Extern))?; self.parse_item_foreign_mod(attrs, safety)? } else if self.is_static_global() { let safety = self.parse_safety(Case::Sensitive); @@ -256,28 +255,28 @@ impl<'a> Parser<'a> { })), ) } - } else if self.check_keyword(kw::Trait) || self.check_auto_or_unsafe_trait_item() { + } else if self.check_keyword(exp!(Trait)) || self.check_auto_or_unsafe_trait_item() { // TRAIT ITEM self.parse_item_trait(attrs, lo)? - } else if self.check_keyword(kw::Impl) - || self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Impl]) + } else if self.check_keyword(exp!(Impl)) + || self.check_keyword(exp!(Unsafe)) && self.is_keyword_ahead(1, &[kw::Impl]) { // IMPL ITEM self.parse_item_impl(attrs, def_())? } else if self.is_reuse_path_item() { self.parse_item_delegation()? - } else if self.check_keyword(kw::Mod) - || self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Mod]) + } else if self.check_keyword(exp!(Mod)) + || self.check_keyword(exp!(Unsafe)) && self.is_keyword_ahead(1, &[kw::Mod]) { // MODULE ITEM self.parse_item_mod(attrs)? - } else if self.eat_keyword(kw::Type) { + } else if self.eat_keyword(exp!(Type)) { // TYPE ITEM self.parse_type_alias(def_())? - } else if self.eat_keyword(kw::Enum) { + } else if self.eat_keyword(exp!(Enum)) { // ENUM ITEM self.parse_item_enum()? - } else if self.eat_keyword(kw::Struct) { + } else if self.eat_keyword(exp!(Struct)) { // STRUCT ITEM self.parse_item_struct()? } else if self.is_kw_followed_by_ident(kw::Union) { @@ -287,7 +286,7 @@ impl<'a> Parser<'a> { } else if self.is_builtin() { // BUILTIN# ITEM return self.parse_item_builtin(); - } else if self.eat_keyword(kw::Macro) { + } else if self.eat_keyword(exp!(Macro)) { // MACROS 2.0 ITEM self.parse_item_decl_macro(lo)? } else if let IsMacroRulesItem::Yes { has_bang } = self.is_macro_rules_item() { @@ -408,13 +407,13 @@ impl<'a> Parser<'a> { }; let mut found_generics = false; - if self.check(&token::Lt) { + if self.check(exp!(Lt)) { found_generics = true; - self.eat_to_tokens(&[&token::Gt]); + self.eat_to_tokens(&[exp!(Gt)]); self.bump(); // `>` } - let err = if self.check(&token::OpenDelim(Delimiter::Brace)) { + let err = if self.check(exp!(OpenBrace)) { // possible struct or enum definition where `struct` or `enum` was forgotten if self.look_ahead(1, |t| *t == token::CloseDelim(Delimiter::Brace)) { // `S {}` could be unit enum or struct @@ -427,25 +426,23 @@ impl<'a> Parser<'a> { } else { Some(errors::MissingKeywordForItemDefinition::Enum { span, insert_span, ident }) } - } else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) { + } else if self.check(exp!(OpenParen)) { // possible function or tuple struct definition where `fn` or `struct` was forgotten self.bump(); // `(` let is_method = self.recover_self_param(); - self.consume_block(Delimiter::Parenthesis, ConsumeClosingDelim::Yes); + self.consume_block(exp!(OpenParen), exp!(CloseParen), ConsumeClosingDelim::Yes); - let err = if self.check(&token::RArrow) - || self.check(&token::OpenDelim(Delimiter::Brace)) - { - self.eat_to_tokens(&[&token::OpenDelim(Delimiter::Brace)]); + let err = if self.check(exp!(RArrow)) || self.check(exp!(OpenBrace)) { + self.eat_to_tokens(&[exp!(OpenBrace)]); self.bump(); // `{` - self.consume_block(Delimiter::Brace, ConsumeClosingDelim::Yes); + self.consume_block(exp!(OpenBrace), exp!(CloseBrace), ConsumeClosingDelim::Yes); if is_method { errors::MissingKeywordForItemDefinition::Method { span, insert_span, ident } } else { errors::MissingKeywordForItemDefinition::Function { span, insert_span, ident } } - } else if is_pub && self.check(&token::Semi) { + } else if is_pub && self.check(exp!(Semi)) { errors::MissingKeywordForItemDefinition::Struct { span, insert_span, ident } } else { errors::MissingKeywordForItemDefinition::Ambiguous { @@ -480,7 +477,7 @@ impl<'a> Parser<'a> { /// Parses an item macro, e.g., `item!();`. fn parse_item_macro(&mut self, vis: &Visibility) -> PResult<'a, MacCall> { let path = self.parse_path(PathStyle::Mod)?; // `foo::bar` - self.expect(&token::Not)?; // `!` + self.expect(exp!(Not))?; // `!` match self.parse_delim_args() { // `( .. )` or `[ .. ]` (followed by `;`), or `{ .. }`. Ok(args) => { @@ -540,7 +537,7 @@ impl<'a> Parser<'a> { fn parse_polarity(&mut self) -> ast::ImplPolarity { // Disambiguate `impl !Trait for Type { ... }` and `impl ! { ... }` for the never type. - if self.check(&token::Not) && self.look_ahead(1, |t| t.can_begin_type()) { + if self.check(exp!(Not)) && self.look_ahead(1, |t| t.can_begin_type()) { self.bump(); // `!` ast::ImplPolarity::Negative(self.prev_token.span) } else { @@ -568,7 +565,7 @@ impl<'a> Parser<'a> { defaultness: Defaultness, ) -> PResult<'a, ItemInfo> { let safety = self.parse_safety(Case::Sensitive); - self.expect_keyword(kw::Impl)?; + self.expect_keyword(exp!(Impl))?; // First, parse generic parameters if necessary. let mut generics = if self.choose_generics_over_qpath(0) { @@ -618,7 +615,7 @@ impl<'a> Parser<'a> { }; // If `for` is missing we try to recover. - let has_for = self.eat_keyword(kw::For); + let has_for = self.eat_keyword(exp!(For)); let missing_for_span = self.prev_token.span.between(self.token.span); let ty_second = if self.token == token::DotDot { @@ -703,7 +700,7 @@ impl<'a> Parser<'a> { fn parse_item_delegation(&mut self) -> PResult<'a, ItemInfo> { let span = self.token.span; - self.expect_keyword(kw::Reuse)?; + self.expect_keyword(exp!(Reuse))?; let (qself, path) = if self.eat_lt() { let (qself, path) = self.parse_qpath(PathStyle::Expr)?; @@ -713,23 +710,23 @@ impl<'a> Parser<'a> { }; let rename = |this: &mut Self| { - Ok(if this.eat_keyword(kw::As) { Some(this.parse_ident()?) } else { None }) + Ok(if this.eat_keyword(exp!(As)) { Some(this.parse_ident()?) } else { None }) }; let body = |this: &mut Self| { - Ok(if this.check(&token::OpenDelim(Delimiter::Brace)) { + Ok(if this.check(exp!(OpenBrace)) { Some(this.parse_block()?) } else { - this.expect(&token::Semi)?; + this.expect(exp!(Semi))?; None }) }; let (ident, item_kind) = if self.eat_path_sep() { - let suffixes = if self.eat(&token::BinOp(token::Star)) { + let suffixes = if self.eat(exp!(Star)) { None } else { let parse_suffix = |p: &mut Self| Ok((p.parse_path_segment_ident()?, rename(p)?)); - Some(self.parse_delim_comma_seq(Delimiter::Brace, parse_suffix)?.0) + Some(self.parse_delim_comma_seq(exp!(OpenBrace), exp!(CloseBrace), parse_suffix)?.0) }; let deleg = DelegationMac { qself, prefix: path, suffixes, body: body(self)? }; (Ident::empty(), ItemKind::DelegationMac(Box::new(deleg))) @@ -767,11 +764,11 @@ impl<'a> Parser<'a> { return Ok(ThinVec::new()); } - self.expect(&token::OpenDelim(Delimiter::Brace))?; + self.expect(exp!(OpenBrace))?; attrs.extend(self.parse_inner_attributes()?); let mut items = ThinVec::new(); - while !self.eat(&token::CloseDelim(Delimiter::Brace)) { + while !self.eat(exp!(CloseBrace)) { if self.recover_doc_comment_before_brace() { continue; } @@ -812,7 +809,7 @@ impl<'a> Parser<'a> { let mut err = self.dcx().struct_span_err(non_item_span, "non-item in item list"); - self.consume_block(Delimiter::Brace, ConsumeClosingDelim::Yes); + self.consume_block(exp!(OpenBrace), exp!(CloseBrace), ConsumeClosingDelim::Yes); if is_let { err.span_suggestion_verbose( non_item_span, @@ -838,7 +835,7 @@ impl<'a> Parser<'a> { } Ok(Some(item)) => items.extend(item), Err(err) => { - self.consume_block(Delimiter::Brace, ConsumeClosingDelim::Yes); + self.consume_block(exp!(OpenBrace), exp!(CloseBrace), ConsumeClosingDelim::Yes); err.with_span_label( open_brace_span, "while parsing this item list starting here", @@ -881,7 +878,7 @@ impl<'a> Parser<'a> { // We are interested in `default` followed by another identifier. // However, we must avoid keywords that occur as binary operators. // Currently, the only applicable keyword is `as` (`default as Ty`). - if self.check_keyword(kw::Default) + if self.check_keyword(exp!(Default)) && self.look_ahead(1, |t| t.is_non_raw_ident_where(|i| i.name != kw::As)) { self.bump(); // `default` @@ -894,33 +891,33 @@ impl<'a> Parser<'a> { /// Is this an `(unsafe auto? | auto) trait` item? fn check_auto_or_unsafe_trait_item(&mut self) -> bool { // auto trait - self.check_keyword(kw::Auto) && self.is_keyword_ahead(1, &[kw::Trait]) + self.check_keyword(exp!(Auto)) && self.is_keyword_ahead(1, &[kw::Trait]) // unsafe auto trait - || self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Trait, kw::Auto]) + || self.check_keyword(exp!(Unsafe)) && self.is_keyword_ahead(1, &[kw::Trait, kw::Auto]) } /// Parses `unsafe? auto? trait Foo { ... }` or `trait Foo = Bar;`. fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, ItemInfo> { let safety = self.parse_safety(Case::Sensitive); // Parse optional `auto` prefix. - let is_auto = if self.eat_keyword(kw::Auto) { + let is_auto = if self.eat_keyword(exp!(Auto)) { self.psess.gated_spans.gate(sym::auto_traits, self.prev_token.span); IsAuto::Yes } else { IsAuto::No }; - self.expect_keyword(kw::Trait)?; + self.expect_keyword(exp!(Trait))?; let ident = self.parse_ident()?; let mut generics = self.parse_generics()?; // Parse optional colon and supertrait bounds. - let had_colon = self.eat(&token::Colon); + let had_colon = self.eat(exp!(Colon)); let span_at_colon = self.prev_token.span; let bounds = if had_colon { self.parse_generic_bounds()? } else { Vec::new() }; let span_before_eq = self.prev_token.span; - if self.eat(&token::Eq) { + if self.eat(exp!(Eq)) { // It's a trait alias. if had_colon { let span = span_at_colon.to(span_before_eq); @@ -1008,11 +1005,10 @@ impl<'a> Parser<'a> { let mut generics = self.parse_generics()?; // Parse optional colon and param bounds. - let bounds = - if self.eat(&token::Colon) { self.parse_generic_bounds()? } else { Vec::new() }; + let bounds = if self.eat(exp!(Colon)) { self.parse_generic_bounds()? } else { Vec::new() }; let before_where_clause = self.parse_where_clause()?; - let ty = if self.eat(&token::Eq) { Some(self.parse_ty()?) } else { None }; + let ty = if self.eat(exp!(Eq)) { Some(self.parse_ty()?) } else { None }; let after_where_clause = self.parse_where_clause()?; @@ -1065,46 +1061,44 @@ impl<'a> Parser<'a> { let mut prefix = ast::Path { segments: ThinVec::new(), span: lo.shrink_to_lo(), tokens: None }; - let kind = if self.check(&token::OpenDelim(Delimiter::Brace)) - || self.check(&token::BinOp(token::Star)) - || self.is_import_coupler() - { - // `use *;` or `use ::*;` or `use {...};` or `use ::{...};` - let mod_sep_ctxt = self.token.span.ctxt(); - if self.eat_path_sep() { - prefix - .segments - .push(PathSegment::path_root(lo.shrink_to_lo().with_ctxt(mod_sep_ctxt))); - } - - self.parse_use_tree_glob_or_nested()? - } else { - // `use path::*;` or `use path::{...};` or `use path;` or `use path as bar;` - prefix = self.parse_path(PathStyle::Mod)?; + let kind = + if self.check(exp!(OpenBrace)) || self.check(exp!(Star)) || self.is_import_coupler() { + // `use *;` or `use ::*;` or `use {...};` or `use ::{...};` + let mod_sep_ctxt = self.token.span.ctxt(); + if self.eat_path_sep() { + prefix + .segments + .push(PathSegment::path_root(lo.shrink_to_lo().with_ctxt(mod_sep_ctxt))); + } - if self.eat_path_sep() { self.parse_use_tree_glob_or_nested()? } else { - // Recover from using a colon as path separator. - while self.eat_noexpect(&token::Colon) { - self.dcx() - .emit_err(errors::SingleColonImportPath { span: self.prev_token.span }); - - // We parse the rest of the path and append it to the original prefix. - self.parse_path_segments(&mut prefix.segments, PathStyle::Mod, None)?; - prefix.span = lo.to(self.prev_token.span); - } + // `use path::*;` or `use path::{...};` or `use path;` or `use path as bar;` + prefix = self.parse_path(PathStyle::Mod)?; - UseTreeKind::Simple(self.parse_rename()?) - } - }; + if self.eat_path_sep() { + self.parse_use_tree_glob_or_nested()? + } else { + // Recover from using a colon as path separator. + while self.eat_noexpect(&token::Colon) { + self.dcx() + .emit_err(errors::SingleColonImportPath { span: self.prev_token.span }); + + // We parse the rest of the path and append it to the original prefix. + self.parse_path_segments(&mut prefix.segments, PathStyle::Mod, None)?; + prefix.span = lo.to(self.prev_token.span); + } + + UseTreeKind::Simple(self.parse_rename()?) + } + }; Ok(UseTree { prefix, kind, span: lo.to(self.prev_token.span) }) } /// Parses `*` or `{...}`. fn parse_use_tree_glob_or_nested(&mut self) -> PResult<'a, UseTreeKind> { - Ok(if self.eat(&token::BinOp(token::Star)) { + Ok(if self.eat(exp!(Star)) { UseTreeKind::Glob } else { let lo = self.token.span; @@ -1121,7 +1115,7 @@ impl<'a> Parser<'a> { /// USE_TREE_LIST = ∅ | (USE_TREE `,`)* USE_TREE [`,`] /// ``` fn parse_use_tree_list(&mut self) -> PResult<'a, ThinVec<(UseTree, ast::NodeId)>> { - self.parse_delim_comma_seq(Delimiter::Brace, |p| { + self.parse_delim_comma_seq(exp!(OpenBrace), exp!(CloseBrace), |p| { p.recover_vcs_conflict_marker(); Ok((p.parse_use_tree()?, DUMMY_NODE_ID)) }) @@ -1129,7 +1123,11 @@ impl<'a> Parser<'a> { } fn parse_rename(&mut self) -> PResult<'a, Option<Ident>> { - if self.eat_keyword(kw::As) { self.parse_ident_or_underscore().map(Some) } else { Ok(None) } + if self.eat_keyword(exp!(As)) { + self.parse_ident_or_underscore().map(Some) + } else { + Ok(None) + } } fn parse_ident_or_underscore(&mut self) -> PResult<'a, Ident> { @@ -1169,15 +1167,15 @@ impl<'a> Parser<'a> { self.parse_ident() }?; - let dash = token::BinOp(token::BinOpToken::Minus); - if self.token != dash { + let dash = exp!(Minus); + if self.token != *dash.tok { return Ok(ident); } // Accept `extern crate name-like-this` for better diagnostics. let mut dashes = vec![]; let mut idents = vec![]; - while self.eat(&dash) { + while self.eat(dash) { dashes.push(self.prev_token.span); idents.push(self.parse_ident()?); } @@ -1218,9 +1216,9 @@ impl<'a> Parser<'a> { && self.token.is_keyword(kw::Unsafe) && self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace)) { - self.expect(&token::OpenDelim(Delimiter::Brace)).unwrap_err().emit(); + self.expect(exp!(OpenBrace)).unwrap_err().emit(); safety = Safety::Unsafe(self.token.span); - let _ = self.eat_keyword(kw::Unsafe); + let _ = self.eat_keyword(exp!(Unsafe)); } let module = ast::ForeignMod { extern_span, @@ -1286,7 +1284,7 @@ impl<'a> Parser<'a> { } fn is_static_global(&mut self) -> bool { - if self.check_keyword(kw::Static) { + if self.check_keyword(exp!(Static)) { // Check if this could be a closure. !self.look_ahead(1, |token| { if token.is_keyword(kw::Move) { @@ -1295,20 +1293,19 @@ impl<'a> Parser<'a> { matches!(token.kind, token::BinOp(token::Or) | token::OrOr) }) } else { - let quals: &[Symbol] = &[kw::Unsafe, kw::Safe]; // `$qual static` - quals.iter().any(|&kw| self.check_keyword(kw)) + (self.check_keyword(exp!(Unsafe)) || self.check_keyword(exp!(Safe))) && self.look_ahead(1, |t| t.is_keyword(kw::Static)) } } /// Recover on `const mut` with `const` already eaten. fn recover_const_mut(&mut self, const_span: Span) { - if self.eat_keyword(kw::Mut) { + if self.eat_keyword(exp!(Mut)) { let span = self.prev_token.span; self.dcx() .emit_err(errors::ConstGlobalCannotBeMutable { ident_span: span, const_span }); - } else if self.eat_keyword(kw::Let) { + } else if self.eat_keyword(exp!(Let)) { let span = self.prev_token.span; self.dcx().emit_err(errors::ConstLetMutuallyExclusive { span: const_span.to(span) }); } @@ -1373,14 +1370,13 @@ impl<'a> Parser<'a> { // Parse the type of a static item. That is, the `":" $ty` fragment. // FIXME: This could maybe benefit from `.may_recover()`? - let ty = match (self.eat(&token::Colon), self.check(&token::Eq) | self.check(&token::Semi)) - { + let ty = match (self.eat(exp!(Colon)), self.check(exp!(Eq)) | self.check(exp!(Semi))) { (true, false) => self.parse_ty()?, // If there wasn't a `:` or the colon was followed by a `=` or `;`, recover a missing type. (colon, _) => self.recover_missing_global_item_type(colon, Some(mutability)), }; - let expr = if self.eat(&token::Eq) { Some(self.parse_expr()?) } else { None }; + let expr = if self.eat(exp!(Eq)) { Some(self.parse_expr()?) } else { None }; self.expect_semi()?; @@ -1406,8 +1402,8 @@ impl<'a> Parser<'a> { // Parse the type of a constant item. That is, the `":" $ty` fragment. // FIXME: This could maybe benefit from `.may_recover()`? let ty = match ( - self.eat(&token::Colon), - self.check(&token::Eq) | self.check(&token::Semi) | self.check_keyword(kw::Where), + self.eat(exp!(Colon)), + self.check(exp!(Eq)) | self.check(exp!(Semi)) | self.check_keyword(exp!(Where)), ) { (true, false) => self.parse_ty()?, // If there wasn't a `:` or the colon was followed by a `=`, `;` or `where`, recover a missing type. @@ -1419,7 +1415,7 @@ impl<'a> Parser<'a> { let before_where_clause = if self.may_recover() { self.parse_where_clause()? } else { WhereClause::default() }; - let expr = if self.eat(&token::Eq) { Some(self.parse_expr()?) } else { None }; + let expr = if self.eat(exp!(Eq)) { Some(self.parse_expr()?) } else { None }; let after_where_clause = self.parse_where_clause()?; @@ -1532,31 +1528,33 @@ impl<'a> Parser<'a> { self.bump(); (thin_vec![], Trailing::No) } else { - self.parse_delim_comma_seq(Delimiter::Brace, |p| p.parse_enum_variant(id.span)) - .map_err(|mut err| { - err.span_label(id.span, "while parsing this enum"); - if self.token == token::Colon { - let snapshot = self.create_snapshot_for_diagnostic(); - self.bump(); - match self.parse_ty() { - Ok(_) => { - err.span_suggestion_verbose( - prev_span, - "perhaps you meant to use `struct` here", - "struct", - Applicability::MaybeIncorrect, - ); - } - Err(e) => { - e.cancel(); - } + self.parse_delim_comma_seq(exp!(OpenBrace), exp!(CloseBrace), |p| { + p.parse_enum_variant(id.span) + }) + .map_err(|mut err| { + err.span_label(id.span, "while parsing this enum"); + if self.token == token::Colon { + let snapshot = self.create_snapshot_for_diagnostic(); + self.bump(); + match self.parse_ty() { + Ok(_) => { + err.span_suggestion_verbose( + prev_span, + "perhaps you meant to use `struct` here", + "struct", + Applicability::MaybeIncorrect, + ); + } + Err(e) => { + e.cancel(); } - self.restore_snapshot(snapshot); } - self.eat_to_tokens(&[&token::CloseDelim(Delimiter::Brace)]); - self.bump(); // } - err - })? + self.restore_snapshot(snapshot); + } + self.eat_to_tokens(&[exp!(CloseBrace)]); + self.bump(); // } + err + })? }; let enum_definition = EnumDef { variants: variants.into_iter().flatten().collect() }; @@ -1589,7 +1587,7 @@ impl<'a> Parser<'a> { return Ok((None, Trailing::from(this.token == token::Comma), UsePreAttrPos::No)); } - let struct_def = if this.check(&token::OpenDelim(Delimiter::Brace)) { + let struct_def = if this.check(exp!(OpenBrace)) { // Parse a struct variant. let (fields, recovered) = match this.parse_record_struct_body("struct", ident.span, false) { @@ -1599,7 +1597,7 @@ impl<'a> Parser<'a> { // We handle `enum` to `struct` suggestion in the caller. return Err(err); } - this.eat_to_tokens(&[&token::CloseDelim(Delimiter::Brace)]); + this.eat_to_tokens(&[exp!(CloseBrace)]); this.bump(); // } err.span_label(span, "while parsing this enum"); err.help(help); @@ -1608,7 +1606,7 @@ impl<'a> Parser<'a> { } }; VariantData::Struct { fields, recovered } - } else if this.check(&token::OpenDelim(Delimiter::Parenthesis)) { + } else if this.check(exp!(OpenParen)) { let body = match this.parse_tuple_struct_body() { Ok(body) => body, Err(mut err) => { @@ -1616,7 +1614,7 @@ impl<'a> Parser<'a> { // We handle `enum` to `struct` suggestion in the caller. return Err(err); } - this.eat_to_tokens(&[&token::CloseDelim(Delimiter::Parenthesis)]); + this.eat_to_tokens(&[exp!(CloseParen)]); this.bump(); // ) err.span_label(span, "while parsing this enum"); err.help(help); @@ -1630,7 +1628,7 @@ impl<'a> Parser<'a> { }; let disr_expr = - if this.eat(&token::Eq) { Some(this.parse_expr_anon_const()?) } else { None }; + if this.eat(exp!(Eq)) { Some(this.parse_expr_anon_const()?) } else { None }; let vr = ast::Variant { ident, @@ -1681,7 +1679,7 @@ impl<'a> Parser<'a> { let body = VariantData::Tuple(body, DUMMY_NODE_ID); self.expect_semi()?; body - } else if self.eat(&token::Semi) { + } else if self.eat(exp!(Semi)) { // If we see a: `struct Foo<T> where T: Copy;` style decl. VariantData::Unit(DUMMY_NODE_ID) } else { @@ -1694,7 +1692,7 @@ impl<'a> Parser<'a> { VariantData::Struct { fields, recovered } } // No `where` so: `struct Foo<T>;` - } else if self.eat(&token::Semi) { + } else if self.eat(exp!(Semi)) { VariantData::Unit(DUMMY_NODE_ID) // Record-style struct definition } else if self.token == token::OpenDelim(Delimiter::Brace) { @@ -1763,14 +1761,18 @@ impl<'a> Parser<'a> { ) -> PResult<'a, (ThinVec<FieldDef>, Recovered)> { let mut fields = ThinVec::new(); let mut recovered = Recovered::No; - if self.eat(&token::OpenDelim(Delimiter::Brace)) { + if self.eat(exp!(OpenBrace)) { while self.token != token::CloseDelim(Delimiter::Brace) { match self.parse_field_def(adt_ty) { Ok(field) => { fields.push(field); } Err(mut err) => { - self.consume_block(Delimiter::Brace, ConsumeClosingDelim::No); + self.consume_block( + exp!(OpenBrace), + exp!(CloseBrace), + ConsumeClosingDelim::No, + ); err.span_label(ident_span, format!("while parsing this {adt_ty}")); let guar = err.emit(); recovered = Recovered::Yes(guar); @@ -1778,7 +1780,7 @@ impl<'a> Parser<'a> { } } } - self.expect(&token::CloseDelim(Delimiter::Brace))?; + self.expect(exp!(CloseBrace))?; } else { let token_str = super::token_descr(&self.token); let where_str = if parsed_where { "" } else { "`where`, or " }; @@ -1793,7 +1795,7 @@ impl<'a> Parser<'a> { fn parse_unsafe_field(&mut self) -> Safety { // not using parse_safety as that also accepts `safe`. - if self.eat_keyword(kw::Unsafe) { + if self.eat_keyword(exp!(Unsafe)) { let span = self.prev_token.span; self.psess.gated_spans.gate(sym::unsafe_fields, span); Safety::Unsafe(span) @@ -1901,7 +1903,7 @@ impl<'a> Parser<'a> { if self.token == token::Comma { seen_comma = true; } - if self.eat(&token::Semi) { + if self.eat(exp!(Semi)) { let sp = self.prev_token.span; let mut err = self.dcx().struct_span_err(sp, format!("{adt_ty} fields are separated by `,`")); @@ -1925,7 +1927,7 @@ impl<'a> Parser<'a> { missing_comma: None, }; self.bump(); // consume the doc comment - let comma_after_doc_seen = self.eat(&token::Comma); + let comma_after_doc_seen = self.eat(exp!(Comma)); // `seen_comma` is always false, because we are inside doc block // condition is here to make code more readable if !seen_comma && comma_after_doc_seen { @@ -1950,13 +1952,13 @@ impl<'a> Parser<'a> { if let TyKind::Path(_, Path { segments, .. }) = &a_var.ty.kind { if let Some(last_segment) = segments.last() { let guar = self.check_trailing_angle_brackets(last_segment, &[ - &token::Comma, - &token::CloseDelim(Delimiter::Brace), + exp!(Comma), + exp!(CloseBrace), ]); if let Some(_guar) = guar { // Handle a case like `Vec<u8>>,` where we can continue parsing fields // after the comma - let _ = self.eat(&token::Comma); + let _ = self.eat(exp!(Comma)); // `check_trailing_angle_brackets` already emitted a nicer error, as // proven by the presence of `_guar`. We can continue parsing. @@ -1989,7 +1991,7 @@ impl<'a> Parser<'a> { } fn expect_field_ty_separator(&mut self) -> PResult<'a, ()> { - if let Err(err) = self.expect(&token::Colon) { + if let Err(err) = self.expect(exp!(Colon)) { let sm = self.psess.source_map(); let eq_typo = self.token == token::Eq && self.look_ahead(1, |t| t.is_path_start()); let semi_typo = self.token == token::Semi @@ -2097,7 +2099,7 @@ impl<'a> Parser<'a> { self.expected_ident_found_err() } } - } else if self.eat_keyword(kw::Struct) { + } else if self.eat_keyword(exp!(Struct)) { match self.parse_item_struct() { Ok((ident, _)) => self .dcx() @@ -2154,12 +2156,12 @@ impl<'a> Parser<'a> { /// ``` fn parse_item_decl_macro(&mut self, lo: Span) -> PResult<'a, ItemInfo> { let ident = self.parse_ident()?; - let body = if self.check(&token::OpenDelim(Delimiter::Brace)) { + let body = if self.check(exp!(OpenBrace)) { self.parse_delim_args()? // `MacBody` - } else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) { + } else if self.check(exp!(OpenParen)) { let params = self.parse_token_tree(); // `MacParams` let pspan = params.span(); - if !self.check(&token::OpenDelim(Delimiter::Brace)) { + if !self.check(exp!(OpenBrace)) { self.unexpected()?; } let body = self.parse_token_tree(); // `MacBody` @@ -2179,7 +2181,7 @@ impl<'a> Parser<'a> { /// Is this a possibly malformed start of a `macro_rules! foo` item definition? fn is_macro_rules_item(&mut self) -> IsMacroRulesItem { - if self.check_keyword(kw::MacroRules) { + if self.check_keyword(exp!(MacroRules)) { let macro_rules_span = self.token.span; if self.look_ahead(1, |t| *t == token::Not) && self.look_ahead(2, |t| t.is_ident()) { @@ -2204,14 +2206,14 @@ impl<'a> Parser<'a> { vis: &Visibility, has_bang: bool, ) -> PResult<'a, ItemInfo> { - self.expect_keyword(kw::MacroRules)?; // `macro_rules` + self.expect_keyword(exp!(MacroRules))?; // `macro_rules` if has_bang { - self.expect(&token::Not)?; // `!` + self.expect(exp!(Not))?; // `!` } let ident = self.parse_ident()?; - if self.eat(&token::Not) { + if self.eat(exp!(Not)) { // Handle macro_rules! foo! let span = self.prev_token.span; self.dcx().emit_err(errors::MacroNameRemoveBang { span }); @@ -2241,7 +2243,7 @@ impl<'a> Parser<'a> { } fn eat_semi_for_macro_if_needed(&mut self, args: &DelimArgs) { - if args.need_semicolon() && !self.eat(&token::Semi) { + if args.need_semicolon() && !self.eat(exp!(Semi)) { self.report_invalid_macro_expansion_item(args); } } @@ -2417,11 +2419,8 @@ impl<'a> Parser<'a> { req_body: bool, fn_params_end: Option<Span>, ) -> PResult<'a, ErrorGuaranteed> { - let expected = if req_body { - &[token::OpenDelim(Delimiter::Brace)][..] - } else { - &[token::Semi, token::OpenDelim(Delimiter::Brace)] - }; + let expected: &[_] = + if req_body { &[exp!(OpenBrace)] } else { &[exp!(Semi), exp!(OpenBrace)] }; match self.expected_one_of_not_found(&[], expected) { Ok(error_guaranteed) => Ok(error_guaranteed), Err(mut err) => { @@ -2506,14 +2505,14 @@ impl<'a> Parser<'a> { self.token == TokenKind::Semi } else { // Only include `;` in list of expected tokens if body is not required - self.check(&TokenKind::Semi) + self.check(exp!(Semi)) }; let (inner_attrs, body) = if has_semi { // Include the trailing semicolon in the span of the signature self.expect_semi()?; *sig_hi = self.prev_token.span; (AttrVec::new(), None) - } else if self.check(&token::OpenDelim(Delimiter::Brace)) || self.token.is_whole_block() { + } else if self.check(exp!(OpenBrace)) || self.token.is_whole_block() { self.parse_block_common(self.token.span, BlockCheckMode::Default, false) .map(|(attrs, body)| (attrs, Some(body)))? } else if self.token == token::Eq { @@ -2541,21 +2540,28 @@ impl<'a> Parser<'a> { /// `check_pub` adds additional `pub` to the checks in case users place it /// wrongly, can be used to ensure `pub` never comes after `default`. pub(super) fn check_fn_front_matter(&mut self, check_pub: bool, case: Case) -> bool { - const ALL_QUALS: &[Symbol] = - &[kw::Pub, kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Safe, kw::Extern]; + const ALL_QUALS: &[ExpKeywordPair] = &[ + exp!(Pub), + exp!(Gen), + exp!(Const), + exp!(Async), + exp!(Unsafe), + exp!(Safe), + exp!(Extern), + ]; // We use an over-approximation here. // `const const`, `fn const` won't parse, but we're not stepping over other syntax either. // `pub` is added in case users got confused with the ordering like `async pub fn`, // only if it wasn't preceded by `default` as `default pub` is invalid. - let quals: &[Symbol] = if check_pub { + let quals: &[_] = if check_pub { ALL_QUALS } else { - &[kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Safe, kw::Extern] + &[exp!(Gen), exp!(Const), exp!(Async), exp!(Unsafe), exp!(Safe), exp!(Extern)] }; - self.check_keyword_case(kw::Fn, case) // Definitely an `fn`. + self.check_keyword_case(exp!(Fn), case) // Definitely an `fn`. // `$qual fn` or `$qual $qual`: - || quals.iter().any(|&kw| self.check_keyword_case(kw, case)) + || quals.iter().any(|&exp| self.check_keyword_case(exp, case)) && self.look_ahead(1, |t| { // `$qual fn`, e.g. `const fn` or `async fn`. t.is_keyword_case(kw::Fn, case) @@ -2563,12 +2569,14 @@ impl<'a> Parser<'a> { || ( ( t.is_non_raw_ident_where(|i| - quals.contains(&i.name) + quals.iter().any(|exp| exp.kw == i.name) // Rule out 2015 `const async: T = val`. && i.is_reserved() ) || case == Case::Insensitive - && t.is_non_raw_ident_where(|i| quals.iter().any(|qual| qual.as_str() == i.name.as_str().to_lowercase())) + && t.is_non_raw_ident_where(|i| quals.iter().any(|exp| { + exp.kw.as_str() == i.name.as_str().to_lowercase() + })) ) // Rule out `unsafe extern {`. && !self.is_unsafe_foreign_mod() @@ -2576,12 +2584,13 @@ impl<'a> Parser<'a> { && !self.is_async_gen_block()) }) // `extern ABI fn` - || self.check_keyword_case(kw::Extern, case) + || self.check_keyword_case(exp!(Extern), case) && self.look_ahead(1, |t| t.can_begin_string_literal()) && (self.look_ahead(2, |t| t.is_keyword_case(kw::Fn, case)) || - // this branch is only for better diagnostics; `pub`, `unsafe`, etc. are not allowed here + // This branch is only for better diagnostics; `pub`, `unsafe`, etc. are not + // allowed here. (self.may_recover() - && self.look_ahead(2, |t| ALL_QUALS.iter().any(|&kw| t.is_keyword(kw))) + && self.look_ahead(2, |t| ALL_QUALS.iter().any(|exp| t.is_keyword(exp.kw))) && self.look_ahead(3, |t| t.is_keyword_case(kw::Fn, case)))) } @@ -2629,9 +2638,9 @@ impl<'a> Parser<'a> { Some(CoroutineKind::Async { .. }) | None => {} } - if !self.eat_keyword_case(kw::Fn, case) { + if !self.eat_keyword_case(exp!(Fn), case) { // It is possible for `expect_one_of` to recover given the contents of - // `self.expected_tokens`, therefore, do not use `self.unexpected()` which doesn't + // `self.expected_token_types`, therefore, do not use `self.unexpected()` which doesn't // account for this. match self.expect_one_of(&[], &[]) { Ok(Recovered::Yes(_)) => {} @@ -2649,7 +2658,7 @@ impl<'a> Parser<'a> { let mut recover_safety = safety; // This will allow the machine fix to directly place the keyword in the correct place or to indicate // that the keyword is already present and the second instance should be removed. - let wrong_kw = if self.check_keyword(kw::Const) { + let wrong_kw = if self.check_keyword(exp!(Const)) { match constness { Const::Yes(sp) => Some(WrongKw::Duplicated(sp)), Const::No => { @@ -2657,7 +2666,7 @@ impl<'a> Parser<'a> { Some(WrongKw::Misplaced(async_start_sp)) } } - } else if self.check_keyword(kw::Async) { + } else if self.check_keyword(exp!(Async)) { match coroutine_kind { Some(CoroutineKind::Async { span, .. }) => { Some(WrongKw::Duplicated(span)) @@ -2683,7 +2692,7 @@ impl<'a> Parser<'a> { Some(WrongKw::Misplaced(unsafe_start_sp)) } } - } else if self.check_keyword(kw::Unsafe) { + } else if self.check_keyword(exp!(Unsafe)) { match safety { Safety::Unsafe(sp) => Some(WrongKw::Duplicated(sp)), Safety::Safe(sp) => { @@ -2695,7 +2704,7 @@ impl<'a> Parser<'a> { Some(WrongKw::Misplaced(ext_start_sp)) } } - } else if self.check_keyword(kw::Safe) { + } else if self.check_keyword(exp!(Safe)) { match safety { Safety::Safe(sp) => Some(WrongKw::Duplicated(sp)), Safety::Unsafe(sp) => { @@ -2741,7 +2750,7 @@ impl<'a> Parser<'a> { } } // Recover incorrect visibility order such as `async pub` - else if self.check_keyword(kw::Pub) { + else if self.check_keyword(exp!(Pub)) { let sp = sp_start.to(self.prev_token.span); if let Ok(snippet) = self.span_to_snippet(sp) { let current_vis = match self.parse_visibility(FollowedByType::No) { @@ -2844,7 +2853,7 @@ impl<'a> Parser<'a> { }; p.restore_snapshot(snapshot); // Skip every token until next possible arg or end. - p.eat_to_tokens(&[&token::Comma, &token::CloseDelim(Delimiter::Parenthesis)]); + p.eat_to_tokens(&[exp!(Comma), exp!(CloseParen)]); // Create a placeholder argument for proper arg count (issue #34264). Ok(dummy_arg(Ident::new(kw::Empty, lo.to(p.prev_token.span)), guar)) }); @@ -2955,7 +2964,7 @@ impl<'a> Parser<'a> { let parse_self_possibly_typed = |this: &mut Self, m| { let eself_ident = expect_self_ident(this); let eself_hi = this.prev_token.span; - let eself = if this.eat(&token::Colon) { + let eself = if this.eat(exp!(Colon)) { SelfKind::Explicit(this.parse_ty()?, m) } else { SelfKind::Value(m) diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 37556c064d8..2637ea268c8 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -8,6 +8,7 @@ mod nonterminal; mod pat; mod path; mod stmt; +pub mod token_type; mod ty; use std::assert_matches::debug_assert_matches; @@ -24,14 +25,12 @@ 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, AttrArgsEq, AttrId, ByRef, Const, CoroutineKind, - DUMMY_NODE_ID, DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, Mutability, Recovered, - Safety, StrLit, Visibility, VisibilityKind, + self as ast, AnonConst, AttrArgs, AttrId, ByRef, Const, CoroutineKind, DUMMY_NODE_ID, + DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, Mutability, Recovered, Safety, StrLit, + Visibility, VisibilityKind, }; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; @@ -39,14 +38,16 @@ use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, Diag, FatalError, MultiSpan, PResult}; use rustc_index::interval::IntervalSet; use rustc_session::parse::ParseSess; -use rustc_span::symbol::{Ident, Symbol, kw, sym}; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use thin_vec::ThinVec; +use token_type::TokenTypeSet; +pub use token_type::{ExpKeywordPair, ExpTokenPair, TokenType}; use tracing::debug; use crate::errors::{ self, IncorrectVisibilityRestriction, MismatchedClosingDelimiter, NonStringAbiLiteral, }; +use crate::exp; use crate::lexer::UnmatchedDelim; #[cfg(test)] @@ -144,7 +145,7 @@ pub struct Parser<'a> { pub prev_token: Token, pub capture_cfg: bool, restrictions: Restrictions, - expected_tokens: Vec<TokenType>, + expected_token_types: TokenTypeSet, token_cursor: TokenCursor, // The number of calls to `bump`, i.e. the position in the token stream. num_bump_calls: u32, @@ -187,9 +188,9 @@ pub struct Parser<'a> { recovery: Recovery, } -// This type is used a lot, e.g. it's cloned when matching many declarative macro rules with nonterminals. Make sure -// it doesn't unintentionally get bigger. -#[cfg(target_pointer_width = "64")] +// This type is used a lot, e.g. it's cloned when matching many declarative macro rules with +// nonterminals. Make sure it doesn't unintentionally get bigger. +#[cfg(all(target_pointer_width = "64", not(target_arch = "s390x")))] rustc_data_structures::static_assert_size!(Parser<'_>, 288); /// Stores span information about a closure. @@ -273,21 +274,48 @@ struct CaptureState { seen_attrs: IntervalSet<AttrId>, } -/// 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<TokenTreeCursor>, } impl TokenCursor { @@ -302,32 +330,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); } @@ -342,48 +371,21 @@ impl TokenCursor { } } -#[derive(Debug, Clone, PartialEq)] -enum TokenType { - Token(TokenKind), - Keyword(Symbol), - Operator, - Lifetime, - Ident, - Path, - Type, - Const, -} - -impl TokenType { - fn to_string(&self) -> String { - match self { - TokenType::Token(t) => format!("`{}`", pprust::token_kind_to_string(t)), - TokenType::Keyword(kw) => format!("`{kw}`"), - TokenType::Operator => "an operator".to_string(), - TokenType::Lifetime => "lifetime".to_string(), - TokenType::Ident => "identifier".to_string(), - TokenType::Path => "path".to_string(), - TokenType::Type => "type".to_string(), - TokenType::Const => "a const expression".to_string(), - } - } -} - /// A sequence separator. #[derive(Debug)] -struct SeqSep { +struct SeqSep<'a> { /// The separator token. - sep: Option<TokenKind>, + sep: Option<ExpTokenPair<'a>>, /// `true` if a trailing separator is allowed. trailing_sep_allowed: bool, } -impl SeqSep { - fn trailing_allowed(t: TokenKind) -> SeqSep { - SeqSep { sep: Some(t), trailing_sep_allowed: true } +impl<'a> SeqSep<'a> { + fn trailing_allowed(sep: ExpTokenPair<'a>) -> SeqSep<'a> { + SeqSep { sep: Some(sep), trailing_sep_allowed: true } } - fn none() -> SeqSep { + fn none() -> SeqSep<'a> { SeqSep { sep: None, trailing_sep_allowed: false } } } @@ -465,8 +467,8 @@ impl<'a> Parser<'a> { prev_token: Token::dummy(), capture_cfg: false, restrictions: Restrictions::empty(), - expected_tokens: Vec::new(), - token_cursor: TokenCursor { tree_cursor: stream.into_trees(), stack: Vec::new() }, + expected_token_types: TokenTypeSet::new(), + token_cursor: TokenCursor { curr: TokenTreeCursor::new(stream), stack: Vec::new() }, num_bump_calls: 0, break_last_token: 0, unmatched_angle_bracket_count: 0, @@ -528,16 +530,16 @@ impl<'a> Parser<'a> { } /// Expects and consumes the token `t`. Signals an error if the next token is not `t`. - pub fn expect(&mut self, t: &TokenKind) -> PResult<'a, Recovered> { - if self.expected_tokens.is_empty() { - if self.token == *t { + pub fn expect(&mut self, exp: ExpTokenPair<'_>) -> PResult<'a, Recovered> { + if self.expected_token_types.is_empty() { + if self.token == *exp.tok { self.bump(); Ok(Recovered::No) } else { - self.unexpected_try_recover(t) + self.unexpected_try_recover(exp.tok) } } else { - self.expect_one_of(slice::from_ref(t), &[]) + self.expect_one_of(slice::from_ref(&exp), &[]) } } @@ -546,13 +548,13 @@ impl<'a> Parser<'a> { /// anything. Signal a fatal error if next token is unexpected. fn expect_one_of( &mut self, - edible: &[TokenKind], - inedible: &[TokenKind], + edible: &[ExpTokenPair<'_>], + inedible: &[ExpTokenPair<'_>], ) -> PResult<'a, Recovered> { - if edible.contains(&self.token.kind) { + if edible.iter().any(|exp| exp.tok == &self.token.kind) { self.bump(); Ok(Recovered::No) - } else if inedible.contains(&self.token.kind) { + } else if inedible.iter().any(|exp| exp.tok == &self.token.kind) { // leave it in the input Ok(Recovered::No) } else if self.token != token::Eof @@ -594,13 +596,13 @@ impl<'a> Parser<'a> { /// Checks if the next token is `tok`, and returns `true` if so. /// - /// This method will automatically add `tok` to `expected_tokens` if `tok` is not + /// This method will automatically add `tok` to `expected_token_types` if `tok` is not /// encountered. #[inline] - fn check(&mut self, tok: &TokenKind) -> bool { - let is_present = self.token == *tok; + fn check(&mut self, exp: ExpTokenPair<'_>) -> bool { + let is_present = self.token == *exp.tok; if !is_present { - self.expected_tokens.push(TokenType::Token(tok.clone())); + self.expected_token_types.insert(exp.token_type); } is_present } @@ -628,8 +630,8 @@ impl<'a> Parser<'a> { /// Consumes a token 'tok' if it exists. Returns whether the given token was present. #[inline] #[must_use] - pub fn eat(&mut self, tok: &TokenKind) -> bool { - let is_present = self.check(tok); + pub fn eat(&mut self, exp: ExpTokenPair<'_>) -> bool { + let is_present = self.check(exp); if is_present { self.bump() } @@ -640,22 +642,23 @@ impl<'a> Parser<'a> { /// An expectation is also added for diagnostics purposes. #[inline] #[must_use] - fn check_keyword(&mut self, kw: Symbol) -> bool { - self.expected_tokens.push(TokenType::Keyword(kw)); - self.token.is_keyword(kw) + fn check_keyword(&mut self, exp: ExpKeywordPair) -> bool { + let is_keyword = self.token.is_keyword(exp.kw); + if !is_keyword { + self.expected_token_types.insert(exp.token_type); + } + is_keyword } #[inline] #[must_use] - fn check_keyword_case(&mut self, kw: Symbol, case: Case) -> bool { - if self.check_keyword(kw) { - return true; - } - + fn check_keyword_case(&mut self, exp: ExpKeywordPair, case: Case) -> bool { + if self.check_keyword(exp) { + true // Do an ASCII case-insensitive match, because all keywords are ASCII. - if case == Case::Insensitive + } else if case == Case::Insensitive && let Some((ident, IdentIsRaw::No)) = self.token.ident() - && ident.as_str().eq_ignore_ascii_case(kw.as_str()) + && ident.as_str().eq_ignore_ascii_case(exp.kw.as_str()) { true } else { @@ -668,13 +671,12 @@ impl<'a> Parser<'a> { // Public for rustc_builtin_macros and rustfmt usage. #[inline] #[must_use] - pub fn eat_keyword(&mut self, kw: Symbol) -> bool { - if self.check_keyword(kw) { + pub fn eat_keyword(&mut self, exp: ExpKeywordPair) -> bool { + let is_keyword = self.check_keyword(exp); + if is_keyword { self.bump(); - true - } else { - false } + is_keyword } /// Eats a keyword, optionally ignoring the case. @@ -682,21 +684,19 @@ impl<'a> Parser<'a> { /// This is useful for recovery. #[inline] #[must_use] - fn eat_keyword_case(&mut self, kw: Symbol, case: Case) -> bool { - if self.eat_keyword(kw) { - return true; - } - - if case == Case::Insensitive + fn eat_keyword_case(&mut self, exp: ExpKeywordPair, case: Case) -> bool { + if self.eat_keyword(exp) { + true + } else if case == Case::Insensitive && let Some((ident, IdentIsRaw::No)) = self.token.ident() - && ident.as_str().to_lowercase() == kw.as_str().to_lowercase() + && ident.as_str().to_lowercase() == exp.kw.as_str().to_lowercase() { - self.dcx().emit_err(errors::KwBadCase { span: ident.span, kw: kw.as_str() }); + self.dcx().emit_err(errors::KwBadCase { span: ident.span, kw: exp.kw.as_str() }); self.bump(); - return true; + true + } else { + false } - - false } /// If the next token is the given keyword, eats it and returns `true`. @@ -705,19 +705,18 @@ impl<'a> Parser<'a> { #[inline] #[must_use] pub fn eat_keyword_noexpect(&mut self, kw: Symbol) -> bool { - if self.token.is_keyword(kw) { + let is_keyword = self.token.is_keyword(kw); + if is_keyword { self.bump(); - true - } else { - false } + is_keyword } /// If the given word is not a keyword, signals an error. /// If the next token is not the given word, signals an error. /// Otherwise, eats it. - pub fn expect_keyword(&mut self, kw: Symbol) -> PResult<'a, ()> { - if !self.eat_keyword(kw) { self.unexpected() } else { Ok(()) } + pub fn expect_keyword(&mut self, exp: ExpKeywordPair) -> PResult<'a, ()> { + if !self.eat_keyword(exp) { self.unexpected() } else { Ok(()) } } /// Is the given keyword `kw` followed by a non-reserved identifier? @@ -726,13 +725,11 @@ impl<'a> Parser<'a> { } #[inline] - fn check_or_expected(&mut self, ok: bool, typ: TokenType) -> bool { - if ok { - true - } else { - self.expected_tokens.push(typ); - false + fn check_or_expected(&mut self, ok: bool, token_type: TokenType) -> bool { + if !ok { + self.expected_token_types.insert(token_type); } + ok } fn check_ident(&mut self) -> bool { @@ -775,22 +772,19 @@ impl<'a> Parser<'a> { /// Otherwise returns `false`. #[inline] fn check_plus(&mut self) -> bool { - self.check_or_expected( - self.token.is_like_plus(), - TokenType::Token(token::BinOp(token::Plus)), - ) + self.check_or_expected(self.token.is_like_plus(), TokenType::Plus) } /// Eats the expected token if it's present possibly breaking /// compound tokens like multi-character operators in process. /// Returns `true` if the token was eaten. - fn break_and_eat(&mut self, expected: TokenKind) -> bool { - if self.token == expected { + fn break_and_eat(&mut self, exp: ExpTokenPair<'_>) -> bool { + if self.token == *exp.tok { self.bump(); return true; } match self.token.kind.break_two_token_op(1) { - Some((first, second)) if first == expected => { + Some((first, second)) if first == *exp.tok => { let first_span = self.psess.source_map().start_point(self.token.span); let second_span = self.token.span.with_lo(first_span.hi()); self.token = Token::new(first, first_span); @@ -807,7 +801,7 @@ impl<'a> Parser<'a> { true } _ => { - self.expected_tokens.push(TokenType::Token(expected)); + self.expected_token_types.insert(exp.token_type); false } } @@ -815,24 +809,24 @@ impl<'a> Parser<'a> { /// Eats `+` possibly breaking tokens like `+=` in process. fn eat_plus(&mut self) -> bool { - self.break_and_eat(token::BinOp(token::Plus)) + self.break_and_eat(exp!(Plus)) } /// Eats `&` possibly breaking tokens like `&&` in process. /// Signals an error if `&` is not eaten. fn expect_and(&mut self) -> PResult<'a, ()> { - if self.break_and_eat(token::BinOp(token::And)) { Ok(()) } else { self.unexpected() } + if self.break_and_eat(exp!(And)) { Ok(()) } else { self.unexpected() } } /// Eats `|` possibly breaking tokens like `||` in process. /// Signals an error if `|` was not eaten. fn expect_or(&mut self) -> PResult<'a, ()> { - if self.break_and_eat(token::BinOp(token::Or)) { Ok(()) } else { self.unexpected() } + if self.break_and_eat(exp!(Or)) { Ok(()) } else { self.unexpected() } } /// Eats `<` possibly breaking tokens like `<<` in process. fn eat_lt(&mut self) -> bool { - let ate = self.break_and_eat(token::Lt); + let ate = self.break_and_eat(exp!(Lt)); if ate { // See doc comment for `unmatched_angle_bracket_count`. self.unmatched_angle_bracket_count += 1; @@ -850,7 +844,7 @@ impl<'a> Parser<'a> { /// Eats `>` possibly breaking tokens like `>>` in process. /// Signals an error if `>` was not eaten. fn expect_gt(&mut self) -> PResult<'a, ()> { - if self.break_and_eat(token::Gt) { + if self.break_and_eat(exp!(Gt)) { // See doc comment for `unmatched_angle_bracket_count`. if self.unmatched_angle_bracket_count > 0 { self.unmatched_angle_bracket_count -= 1; @@ -862,14 +856,14 @@ impl<'a> Parser<'a> { } } - /// Checks if the next token is contained within `kets`, and returns `true` if so. + /// Checks if the next token is contained within `closes`, and returns `true` if so. fn expect_any_with_type( &mut self, - kets_expected: &[&TokenKind], - kets_not_expected: &[&TokenKind], + closes_expected: &[ExpTokenPair<'_>], + closes_not_expected: &[&TokenKind], ) -> bool { - kets_expected.iter().any(|k| self.check(k)) - || kets_not_expected.iter().any(|k| self.check_noexpect(k)) + closes_expected.iter().any(|&close| self.check(close)) + || closes_not_expected.iter().any(|k| self.check_noexpect(k)) } /// Parses a sequence until the specified delimiters. The function @@ -877,9 +871,9 @@ impl<'a> Parser<'a> { /// closing bracket. fn parse_seq_to_before_tokens<T>( &mut self, - kets_expected: &[&TokenKind], - kets_not_expected: &[&TokenKind], - sep: SeqSep, + closes_expected: &[ExpTokenPair<'_>], + closes_not_expected: &[&TokenKind], + sep: SeqSep<'_>, mut f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, ) -> PResult<'a, (ThinVec<T>, Trailing, Recovered)> { let mut first = true; @@ -887,17 +881,17 @@ impl<'a> Parser<'a> { let mut trailing = Trailing::No; let mut v = ThinVec::new(); - while !self.expect_any_with_type(kets_expected, kets_not_expected) { + while !self.expect_any_with_type(closes_expected, closes_not_expected) { if let token::CloseDelim(..) | token::Eof = self.token.kind { break; } - if let Some(t) = &sep.sep { + if let Some(exp) = sep.sep { if first { // no separator for the first element first = false; } else { // check for separator - match self.expect(t) { + match self.expect(exp) { Ok(Recovered::No) => { self.current_closure.take(); } @@ -908,7 +902,7 @@ impl<'a> Parser<'a> { } Err(mut expect_err) => { let sp = self.prev_token.span.shrink_to_hi(); - let token_str = pprust::token_kind_to_string(t); + let token_str = pprust::token_kind_to_string(exp.tok); match self.current_closure.take() { Some(closure_spans) if self.token == TokenKind::Semi => { @@ -928,7 +922,7 @@ impl<'a> Parser<'a> { _ => { // Attempt to keep parsing if it was a similar separator. - if let Some(tokens) = t.similar_tokens() { + if let Some(tokens) = exp.tok.similar_tokens() { if tokens.contains(&self.token.kind) { self.bump(); } @@ -978,15 +972,17 @@ impl<'a> Parser<'a> { // Parsing failed, therefore it must be something more serious // than just a missing separator. for xx in &e.children { - // propagate the help message from sub error 'e' to main error 'expect_err; + // Propagate the help message from sub error `e` to main + // error `expect_err`. expect_err.children.push(xx.clone()); } e.cancel(); if self.token == token::Colon { - // we will try to recover in `maybe_recover_struct_lit_bad_delims` + // We will try to recover in + // `maybe_recover_struct_lit_bad_delims`. return Err(expect_err); - } else if let [token::CloseDelim(Delimiter::Parenthesis)] = - kets_expected + } else if let [exp] = closes_expected + && exp.token_type == TokenType::CloseParen { return Err(expect_err); } else { @@ -1000,7 +996,7 @@ impl<'a> Parser<'a> { } } if sep.trailing_sep_allowed - && self.expect_any_with_type(kets_expected, kets_not_expected) + && self.expect_any_with_type(closes_expected, closes_not_expected) { trailing = Trailing::Yes; break; @@ -1020,7 +1016,7 @@ impl<'a> Parser<'a> { ) -> PResult<'a, ()> { let initial_semicolon = self.token.span; - while self.eat(&TokenKind::Semi) { + while self.eat(exp!(Semi)) { let _ = self.parse_stmt_without_recovery(false, ForceCollect::No).unwrap_or_else(|e| { e.cancel(); None @@ -1076,11 +1072,11 @@ impl<'a> Parser<'a> { /// closing bracket. fn parse_seq_to_before_end<T>( &mut self, - ket: &TokenKind, - sep: SeqSep, + close: ExpTokenPair<'_>, + sep: SeqSep<'_>, f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, ) -> PResult<'a, (ThinVec<T>, Trailing, Recovered)> { - self.parse_seq_to_before_tokens(&[ket], &[], sep, f) + self.parse_seq_to_before_tokens(&[close], &[], sep, f) } /// Parses a sequence, including only the closing delimiter. The function @@ -1088,15 +1084,15 @@ impl<'a> Parser<'a> { /// closing bracket. fn parse_seq_to_end<T>( &mut self, - ket: &TokenKind, - sep: SeqSep, + close: ExpTokenPair<'_>, + sep: SeqSep<'_>, f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, ) -> PResult<'a, (ThinVec<T>, Trailing)> { - let (val, trailing, recovered) = self.parse_seq_to_before_end(ket, sep, f)?; - if matches!(recovered, Recovered::No) && !self.eat(ket) { + let (val, trailing, recovered) = self.parse_seq_to_before_end(close, sep, f)?; + if matches!(recovered, Recovered::No) && !self.eat(close) { self.dcx().span_delayed_bug( self.token.span, - "recovered but `parse_seq_to_before_end` did not give us the ket token", + "recovered but `parse_seq_to_before_end` did not give us the close token", ); } Ok((val, trailing)) @@ -1107,13 +1103,13 @@ impl<'a> Parser<'a> { /// closing bracket. fn parse_unspanned_seq<T>( &mut self, - bra: &TokenKind, - ket: &TokenKind, - sep: SeqSep, + open: ExpTokenPair<'_>, + close: ExpTokenPair<'_>, + sep: SeqSep<'_>, f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, ) -> PResult<'a, (ThinVec<T>, Trailing)> { - self.expect(bra)?; - self.parse_seq_to_end(ket, sep, f) + self.expect(open)?; + self.parse_seq_to_end(close, sep, f) } /// Parses a comma-separated sequence, including both delimiters. @@ -1121,15 +1117,11 @@ impl<'a> Parser<'a> { /// closing bracket. fn parse_delim_comma_seq<T>( &mut self, - delim: Delimiter, + open: ExpTokenPair<'_>, + close: ExpTokenPair<'_>, f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, ) -> PResult<'a, (ThinVec<T>, Trailing)> { - self.parse_unspanned_seq( - &token::OpenDelim(delim), - &token::CloseDelim(delim), - SeqSep::trailing_allowed(token::Comma), - f, - ) + self.parse_unspanned_seq(open, close, SeqSep::trailing_allowed(exp!(Comma)), f) } /// Parses a comma-separated sequence delimited by parentheses (e.g. `(x, y)`). @@ -1139,7 +1131,7 @@ impl<'a> Parser<'a> { &mut self, f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, ) -> PResult<'a, (ThinVec<T>, Trailing)> { - self.parse_delim_comma_seq(Delimiter::Parenthesis, f) + self.parse_delim_comma_seq(exp!(OpenParen), exp!(CloseParen), f) } /// Advance the parser by one token using provided token as the next one. @@ -1155,7 +1147,7 @@ impl<'a> Parser<'a> { self.token_spacing = next_spacing; // Diagnostics. - self.expected_tokens.clear(); + self.expected_token_types.clear(); } /// Advance the parser by one token. @@ -1192,7 +1184,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 { @@ -1202,12 +1194,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 @@ -1244,11 +1237,11 @@ impl<'a> Parser<'a> { /// Parses asyncness: `async` or nothing. fn parse_coroutine_kind(&mut self, case: Case) -> Option<CoroutineKind> { let span = self.token.uninterpolated_span(); - if self.eat_keyword_case(kw::Async, case) { + if self.eat_keyword_case(exp!(Async), case) { // FIXME(gen_blocks): Do we want to unconditionally parse `gen` and then // error if edition <= 2024, like we do with async and edition <= 2018? if self.token.uninterpolated_span().at_least_rust_2024() - && self.eat_keyword_case(kw::Gen, case) + && self.eat_keyword_case(exp!(Gen), case) { let gen_span = self.prev_token.uninterpolated_span(); Some(CoroutineKind::AsyncGen { @@ -1264,7 +1257,7 @@ impl<'a> Parser<'a> { }) } } else if self.token.uninterpolated_span().at_least_rust_2024() - && self.eat_keyword_case(kw::Gen, case) + && self.eat_keyword_case(exp!(Gen), case) { Some(CoroutineKind::Gen { span, @@ -1278,9 +1271,9 @@ impl<'a> Parser<'a> { /// Parses fn unsafety: `unsafe`, `safe` or nothing. fn parse_safety(&mut self, case: Case) -> Safety { - if self.eat_keyword_case(kw::Unsafe, case) { + if self.eat_keyword_case(exp!(Unsafe), case) { Safety::Unsafe(self.prev_token.uninterpolated_span()) - } else if self.eat_keyword_case(kw::Safe, case) { + } else if self.eat_keyword_case(exp!(Safe), case) { Safety::Safe(self.prev_token.uninterpolated_span()) } else { Safety::Default @@ -1306,7 +1299,7 @@ impl<'a> Parser<'a> { if (self.check_const_closure() == is_closure) && !self .look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace) || t.is_whole_block()) - && self.eat_keyword_case(kw::Const, case) + && self.eat_keyword_case(exp!(Const), case) { Const::Yes(self.prev_token.uninterpolated_span()) } else { @@ -1319,7 +1312,7 @@ impl<'a> Parser<'a> { if pat { self.psess.gated_spans.gate(sym::inline_const_pat, span); } - self.expect_keyword(kw::Const)?; + self.expect_keyword(exp!(Const))?; let (attrs, blk) = self.parse_inner_attrs_and_block()?; let anon_const = AnonConst { id: DUMMY_NODE_ID, @@ -1331,19 +1324,19 @@ impl<'a> Parser<'a> { /// Parses mutability (`mut` or nothing). fn parse_mutability(&mut self) -> Mutability { - if self.eat_keyword(kw::Mut) { Mutability::Mut } else { Mutability::Not } + if self.eat_keyword(exp!(Mut)) { Mutability::Mut } else { Mutability::Not } } /// Parses reference binding mode (`ref`, `ref mut`, or nothing). fn parse_byref(&mut self) -> ByRef { - if self.eat_keyword(kw::Ref) { ByRef::Yes(self.parse_mutability()) } else { ByRef::No } + if self.eat_keyword(exp!(Ref)) { ByRef::Yes(self.parse_mutability()) } else { ByRef::No } } /// Possibly parses mutability (`const` or `mut`). fn parse_const_or_mut(&mut self) -> Option<Mutability> { - if self.eat_keyword(kw::Mut) { + if self.eat_keyword(exp!(Mut)) { Some(Mutability::Mut) - } else if self.eat_keyword(kw::Const) { + } else if self.eat_keyword(exp!(Const)) { Some(Mutability::Not) } else { None @@ -1374,18 +1367,18 @@ impl<'a> Parser<'a> { fn parse_attr_args(&mut self) -> PResult<'a, AttrArgs> { Ok(if let Some(args) = self.parse_delim_args_inner() { AttrArgs::Delimited(args) - } else if self.eat(&token::Eq) { + } else if self.eat(exp!(Eq)) { let eq_span = self.prev_token.span; - AttrArgs::Eq { eq_span, value: AttrArgsEq::Ast(self.parse_expr_force_collect()?) } + AttrArgs::Eq { eq_span, expr: self.parse_expr_force_collect()? } } else { AttrArgs::Empty }) } fn parse_delim_args_inner(&mut self) -> Option<DelimArgs> { - let delimited = self.check(&token::OpenDelim(Delimiter::Parenthesis)) - || self.check(&token::OpenDelim(Delimiter::Bracket)) - || self.check(&token::OpenDelim(Delimiter::Brace)); + let delimited = self.check(exp!(OpenParen)) + || self.check(exp!(OpenBracket)) + || self.check(exp!(OpenBrace)); delimited.then(|| { let TokenTree::Delimited(dspan, _, delim, tokens) = self.parse_token_tree() else { @@ -1399,9 +1392,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 @@ -1421,7 +1415,7 @@ impl<'a> Parser<'a> { // Consume close delimiter self.bump(); - TokenTree::Delimited(span, spacing, delim, stream) + tree } token::CloseDelim(_) | token::Eof => unreachable!(), _ => { @@ -1463,7 +1457,7 @@ impl<'a> Parser<'a> { pub fn parse_visibility(&mut self, fbt: FollowedByType) -> PResult<'a, Visibility> { maybe_whole!(self, NtVis, |vis| vis.into_inner()); - if !self.eat_keyword(kw::Pub) { + if !self.eat_keyword(exp!(Pub)) { // We need a span for our `Spanned<VisibilityKind>`, but there's inherently no // keyword to grab a span from for inherited visibility; an empty span at the // beginning of the current token would seem to be the "Schelling span". @@ -1475,7 +1469,7 @@ impl<'a> Parser<'a> { } let lo = self.prev_token.span; - if self.check(&token::OpenDelim(Delimiter::Parenthesis)) { + if self.check(exp!(OpenParen)) { // We don't `self.bump()` the `(` yet because this might be a struct definition where // `()` or a tuple might be allowed. For example, `struct Struct(pub (), pub (usize));`. // Because of this, we only `bump` the `(` if we're assured it is appropriate to do so @@ -1485,7 +1479,7 @@ impl<'a> Parser<'a> { self.bump(); // `(` self.bump(); // `in` let path = self.parse_path(PathStyle::Mod)?; // `path` - self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; // `)` + self.expect(exp!(CloseParen))?; // `)` let vis = VisibilityKind::Restricted { path: P(path), id: ast::DUMMY_NODE_ID, @@ -1502,7 +1496,7 @@ impl<'a> Parser<'a> { // Parse `pub(crate)`, `pub(self)`, or `pub(super)`. self.bump(); // `(` let path = self.parse_path(PathStyle::Mod)?; // `crate`/`super`/`self` - self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; // `)` + self.expect(exp!(CloseParen))?; // `)` let vis = VisibilityKind::Restricted { path: P(path), id: ast::DUMMY_NODE_ID, @@ -1528,7 +1522,7 @@ impl<'a> Parser<'a> { fn recover_incorrect_vis_restriction(&mut self) -> PResult<'a, ()> { self.bump(); // `(` let path = self.parse_path(PathStyle::Mod)?; - self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; // `)` + self.expect(exp!(CloseParen))?; // `)` let path_str = pprust::path_to_string(&path); self.dcx() @@ -1539,7 +1533,7 @@ impl<'a> Parser<'a> { /// Parses `extern string_literal?`. fn parse_extern(&mut self, case: Case) -> Extern { - if self.eat_keyword_case(kw::Extern, case) { + if self.eat_keyword_case(exp!(Extern), case) { let mut extern_span = self.prev_token.span; let abi = self.parse_abi(); if let Some(abi) = abi { @@ -1579,7 +1573,7 @@ impl<'a> Parser<'a> { /// Checks for `::` or, potentially, `:::` and then look ahead after it. fn check_path_sep_and_look_ahead(&mut self, looker: impl Fn(&Token) -> bool) -> bool { - if self.check(&token::PathSep) { + if self.check(exp!(PathSep)) { if self.may_recover() && self.look_ahead(1, |t| t.kind == token::Colon) { debug_assert!(!self.look_ahead(1, &looker), "Looker must not match on colon"); self.look_ahead(2, looker) @@ -1643,8 +1637,8 @@ impl<'a> Parser<'a> { DebugParser { parser: self, lookahead } } - pub fn clear_expected_tokens(&mut self) { - self.expected_tokens.clear(); + pub fn clear_expected_token_types(&mut self) { + self.expected_token_types.clear(); } pub fn approx_token_stream_pos(&self) -> u32 { diff --git a/compiler/rustc_parse/src/parser/mut_visit/tests.rs b/compiler/rustc_parse/src/parser/mut_visit/tests.rs index b82c295732d..46c678c3902 100644 --- a/compiler/rustc_parse/src/parser/mut_visit/tests.rs +++ b/compiler/rustc_parse/src/parser/mut_visit/tests.rs @@ -1,8 +1,7 @@ use rustc_ast as ast; use rustc_ast::mut_visit::MutVisitor; use rustc_ast_pretty::pprust; -use rustc_span::create_default_session_globals_then; -use rustc_span::symbol::Ident; +use rustc_span::{Ident, create_default_session_globals_then}; use crate::parser::tests::{matches_codepattern, string_to_crate}; diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 752a52b382b..67cabb757e9 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -9,7 +9,7 @@ use rustc_ast::token::{ use rustc_ast_pretty::pprust; use rustc_data_structures::sync::Lrc; use rustc_errors::PResult; -use rustc_span::symbol::{Ident, kw}; +use rustc_span::{Ident, kw}; use crate::errors::UnexpectedNonterminal; use crate::parser::pat::{CommaRecoveryMode, RecoverColon, RecoverComma}; diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 4cda887a02b..5ad3da2196f 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -13,8 +13,7 @@ use rustc_ast_pretty::pprust; use rustc_errors::{Applicability, Diag, DiagArgValue, PResult, StashKey}; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::source_map::{Spanned, respan}; -use rustc_span::symbol::{Ident, kw, sym}; -use rustc_span::{BytePos, ErrorGuaranteed, Span}; +use rustc_span::{BytePos, ErrorGuaranteed, Ident, Span, kw, sym}; use thin_vec::{ThinVec, thin_vec}; use super::{ForceCollect, Parser, PathStyle, Restrictions, Trailing, UsePreAttrPos}; @@ -31,7 +30,7 @@ use crate::errors::{ UnexpectedVertVertInPattern, WrapInParens, }; use crate::parser::expr::{DestructuredFloat, could_be_unclosed_char_literal}; -use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; +use crate::{exp, maybe_recover_from_interpolated_ty_qpath, maybe_whole}; #[derive(PartialEq, Copy, Clone)] pub enum Expected { @@ -111,7 +110,7 @@ impl<'a> Parser<'a> { ) -> PResult<'a, P<Pat>> { let pat = self.parse_pat_no_top_guard(expected, rc, ra, rt)?; - if self.eat_keyword(kw::If) { + if self.eat_keyword(exp!(If)) { let cond = self.parse_expr()?; // Feature-gate guard patterns self.psess.gated_spans.gate(sym::guard_patterns, cond.span); @@ -194,7 +193,7 @@ impl<'a> Parser<'a> { // If the next token is not a `|`, // this is not an or-pattern and we should exit here. - if !self.check(&token::BinOp(token::Or)) && self.token != token::OrOr { + if !self.check(exp!(Or)) && self.token != token::OrOr { // If we parsed a leading `|` which should be gated, // then we should really gate the leading `|`. // This complicated procedure is done purely for diagnostics UX. @@ -264,7 +263,7 @@ impl<'a> Parser<'a> { CommaRecoveryMode::LikelyTuple, Some(syntax_loc), )?; - let colon = self.eat(&token::Colon); + let colon = self.eat(exp!(Colon)); if let PatKind::Or(pats) = &pat.kind { let span = pat.span; @@ -328,7 +327,7 @@ impl<'a> Parser<'a> { self.dcx().emit_err(UnexpectedVertVertInPattern { span: self.token.span, start: lo }); self.bump(); EatOrResult::AteOr - } else if self.eat(&token::BinOp(token::Or)) { + } else if self.eat(exp!(Or)) { EatOrResult::AteOr } else { EatOrResult::None @@ -715,40 +714,41 @@ impl<'a> Parser<'a> { lo = self.token.span; } - let pat = if self.check(&token::BinOp(token::And)) || self.token == token::AndAnd { + let pat = if self.check(exp!(And)) || self.token == token::AndAnd { self.parse_pat_deref(expected)? - } else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) { + } else if self.check(exp!(OpenParen)) { self.parse_pat_tuple_or_parens()? - } else if self.check(&token::OpenDelim(Delimiter::Bracket)) { + } else if self.check(exp!(OpenBracket)) { // Parse `[pat, pat,...]` as a slice pattern. - let (pats, _) = self.parse_delim_comma_seq(Delimiter::Bracket, |p| { - p.parse_pat_allow_top_guard( - None, - RecoverComma::No, - RecoverColon::No, - CommaRecoveryMode::EitherTupleOrPipe, - ) - })?; + let (pats, _) = + self.parse_delim_comma_seq(exp!(OpenBracket), exp!(CloseBracket), |p| { + p.parse_pat_allow_top_guard( + None, + RecoverComma::No, + RecoverColon::No, + CommaRecoveryMode::EitherTupleOrPipe, + ) + })?; PatKind::Slice(pats) - } else if self.check(&token::DotDot) && !self.is_pat_range_end_start(1) { + } else if self.check(exp!(DotDot)) && !self.is_pat_range_end_start(1) { // A rest pattern `..`. self.bump(); // `..` PatKind::Rest - } else if self.check(&token::DotDotDot) && !self.is_pat_range_end_start(1) { + } else if self.check(exp!(DotDotDot)) && !self.is_pat_range_end_start(1) { self.recover_dotdotdot_rest_pat(lo) } else if let Some(form) = self.parse_range_end() { self.parse_pat_range_to(form)? // `..=X`, `...X`, or `..X`. - } else if self.eat(&token::Not) { + } else if self.eat(exp!(Not)) { // Parse `!` self.psess.gated_spans.gate(sym::never_patterns, self.prev_token.span); PatKind::Never - } else if self.eat_keyword(kw::Underscore) { + } else if self.eat_keyword(exp!(Underscore)) { // Parse `_` PatKind::Wild - } else if self.eat_keyword(kw::Mut) { + } else if self.eat_keyword(exp!(Mut)) { self.parse_pat_ident_mut()? - } else if self.eat_keyword(kw::Ref) { - if self.check_keyword(kw::Box) { + } else if self.eat_keyword(exp!(Ref)) { + if self.check_keyword(exp!(Box)) { // Suggest `box ref`. let span = self.prev_token.span.to(self.token.span); self.bump(); @@ -757,7 +757,7 @@ impl<'a> Parser<'a> { // Parse ref ident @ pat / ref mut ident @ pat let mutbl = self.parse_mutability(); self.parse_pat_ident(BindingMode(ByRef::Yes(mutbl), Mutability::Not), syntax_loc)? - } else if self.eat_keyword(kw::Box) { + } else if self.eat_keyword(exp!(Box)) { self.parse_pat_box()? } else if self.check_inline_const(0) { // Parse `const pat` @@ -794,14 +794,14 @@ impl<'a> Parser<'a> { }; let span = lo.to(self.prev_token.span); - if qself.is_none() && self.check(&token::Not) { + if qself.is_none() && self.check(exp!(Not)) { self.parse_pat_mac_invoc(path)? } else if let Some(form) = self.parse_range_end() { let begin = self.mk_expr(span, ExprKind::Path(qself, path)); self.parse_pat_range_begin_with(begin, form)? - } else if self.check(&token::OpenDelim(Delimiter::Brace)) { + } else if self.check(exp!(OpenBrace)) { self.parse_pat_struct(qself, path)? - } else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) { + } else if self.check(exp!(OpenParen)) { self.parse_pat_tuple_struct(qself, path)? } else { match self.maybe_recover_trailing_expr(span, false) { @@ -1107,7 +1107,7 @@ impl<'a> Parser<'a> { /// Eat any extraneous `mut`s and error + recover if we ate any. fn recover_additional_muts(&mut self) { let lo = self.token.span; - while self.eat_keyword(kw::Mut) {} + while self.eat_keyword(exp!(Mut)) {} if lo == self.token.span { return; } @@ -1148,11 +1148,11 @@ impl<'a> Parser<'a> { /// Parses the range pattern end form `".." | "..." | "..=" ;`. fn parse_range_end(&mut self) -> Option<Spanned<RangeEnd>> { - let re = if self.eat(&token::DotDotDot) { + let re = if self.eat(exp!(DotDotDot)) { RangeEnd::Included(RangeSyntax::DotDotDot) - } else if self.eat(&token::DotDotEq) { + } else if self.eat(exp!(DotDotEq)) { RangeEnd::Included(RangeSyntax::DotDotEq) - } else if self.eat(&token::DotDot) { + } else if self.eat(exp!(DotDot)) { RangeEnd::Excluded } else { return None; @@ -1272,7 +1272,7 @@ impl<'a> Parser<'a> { // recover trailing `)` if let Some(open_paren) = open_paren { - self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; + self.expect(exp!(CloseParen))?; self.dcx().emit_err(UnexpectedParenInRangePat { span: vec![open_paren, self.prev_token.span], @@ -1332,7 +1332,7 @@ impl<'a> Parser<'a> { })); } - let sub = if self.eat(&token::At) { + let sub = if self.eat(exp!(At)) { Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern), None)?) } else { None @@ -1371,10 +1371,10 @@ impl<'a> Parser<'a> { self.bump(); let (fields, etc) = self.parse_pat_fields().unwrap_or_else(|mut e| { e.span_label(path.span, "while parsing the fields for this pattern"); - e.emit(); + let guar = e.emit(); self.recover_stmt(); // When recovering, pretend we had `Foo { .. }`, to avoid cascading errors. - (ThinVec::new(), PatFieldsRest::Rest) + (ThinVec::new(), PatFieldsRest::Recovered(guar)) }); self.bump(); Ok(PatKind::Struct(qself, path, fields, etc)) @@ -1448,7 +1448,7 @@ impl<'a> Parser<'a> { // We cannot use `parse_pat_ident()` since it will complain `box` // is not an identifier. - let sub = if self.eat(&token::At) { + let sub = if self.eat(exp!(At)) { Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern), None)?) } else { None @@ -1505,9 +1505,9 @@ impl<'a> Parser<'a> { } ate_comma = false; - if self.check(&token::DotDot) + if self.check(exp!(DotDot)) || self.check_noexpect(&token::DotDotDot) - || self.check_keyword(kw::Underscore) + || self.check_keyword(exp!(Underscore)) { etc = PatFieldsRest::Rest; let mut etc_sp = self.token.span; @@ -1595,7 +1595,7 @@ impl<'a> Parser<'a> { return Err(err); } }?; - ate_comma = this.eat(&token::Comma); + ate_comma = this.eat(exp!(Comma)); last_non_comma_dotdot_span = Some(this.prev_token.span); @@ -1648,7 +1648,7 @@ impl<'a> Parser<'a> { ident: prev_field, }) } else { - self.dcx().create_err(AtInStructPattern { span: span }) + self.dcx().create_err(AtInStructPattern { span }) } } @@ -1707,7 +1707,7 @@ impl<'a> Parser<'a> { (pat, fieldname, false) } else { // Parsing a pattern of the form `(box) (ref) (mut) fieldname`. - let is_box = self.eat_keyword(kw::Box); + let is_box = self.eat_keyword(exp!(Box)); let boxed_span = self.token.span; let mutability = self.parse_mutability(); let by_ref = self.parse_byref(); diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 6a7029a8f1c..39737b9e137 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -9,8 +9,7 @@ use rustc_ast::{ Path, PathSegment, QSelf, }; use rustc_errors::{Applicability, Diag, PResult}; -use rustc_span::symbol::{Ident, kw, sym}; -use rustc_span::{BytePos, Span}; +use rustc_span::{BytePos, Ident, Span, kw, sym}; use thin_vec::ThinVec; use tracing::debug; @@ -18,7 +17,7 @@ use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{Parser, Restrictions, TokenType}; use crate::errors::{PathSingleColon, PathTripleColon}; use crate::parser::{CommaRecoveryMode, RecoverColon, RecoverComma}; -use crate::{errors, maybe_whole}; +use crate::{errors, exp, maybe_whole}; /// Specifies how to parse a path. #[derive(Copy, Clone, PartialEq)] @@ -81,7 +80,7 @@ impl<'a> Parser<'a> { // above). `path_span` has the span of that path, or an empty // span in the case of something like `<T>::Bar`. let (mut path, path_span); - if self.eat_keyword(kw::As) { + if self.eat_keyword(exp!(As)) { let path_lo = self.token.span; path = self.parse_path(PathStyle::Type)?; path_span = path_lo.to(self.prev_token.span); @@ -91,7 +90,7 @@ impl<'a> Parser<'a> { } // See doc comment for `unmatched_angle_bracket_count`. - self.expect(&token::Gt)?; + self.expect(exp!(Gt))?; if self.unmatched_angle_bracket_count > 0 { self.unmatched_angle_bracket_count -= 1; debug!("parse_qpath: (decrement) count={:?}", self.unmatched_angle_bracket_count); @@ -99,7 +98,7 @@ impl<'a> Parser<'a> { let is_import_coupler = self.is_import_coupler(); if !is_import_coupler && !self.recover_colon_before_qpath_proj() { - self.expect(&token::PathSep)?; + self.expect(exp!(PathSep))?; } let qself = P(QSelf { ty, path_span, position: path.segments.len() }); @@ -243,7 +242,7 @@ impl<'a> Parser<'a> { // `PathStyle::Expr` is only provided at the root invocation and never in // `parse_path_segment` to recurse and therefore can be checked to maintain // this invariant. - self.check_trailing_angle_brackets(&segment, &[&token::PathSep]); + self.check_trailing_angle_brackets(&segment, &[exp!(PathSep)]); } segments.push(segment); @@ -276,7 +275,7 @@ impl<'a> Parser<'a> { /// Eat `::` or, potentially, `:::`. #[must_use] pub(super) fn eat_path_sep(&mut self) -> bool { - let result = self.eat(&token::PathSep); + let result = self.eat(exp!(PathSep)); if result && self.may_recover() { if self.eat_noexpect(&token::Colon) { self.dcx().emit_err(PathTripleColon { span: self.prev_token.span }); @@ -301,10 +300,8 @@ impl<'a> Parser<'a> { ) }; let check_args_start = |this: &mut Self| { - this.expected_tokens.extend_from_slice(&[ - TokenType::Token(token::Lt), - TokenType::Token(token::OpenDelim(Delimiter::Parenthesis)), - ]); + this.expected_token_types.insert(TokenType::Lt); + this.expected_token_types.insert(TokenType::OpenParen); is_args_start(&this.token) }; @@ -368,7 +365,7 @@ impl<'a> Parser<'a> { { self.bump(); // ( self.bump(); // .. - self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; + self.expect(exp!(CloseParen))?; let span = lo.to(self.prev_token.span); self.psess.gated_spans.gate(sym::return_type_notation, span); @@ -600,8 +597,7 @@ impl<'a> Parser<'a> { // When encountering severely malformed code where there are several levels of // nested unclosed angle args (`f::<f::<f::<f::<...`), we avoid severe O(n^2) // behavior by bailing out earlier (#117080). - e.emit(); - rustc_errors::FatalError.raise(); + e.emit().raise_fatal(); } Err(e) if is_first_invocation && self.unmatched_angle_bracket_count > 0 => { self.angle_bracket_nesting -= 1; @@ -662,12 +658,12 @@ impl<'a> Parser<'a> { let mut args = ThinVec::new(); while let Some(arg) = self.parse_angle_arg(ty_generics)? { args.push(arg); - if !self.eat(&token::Comma) { + if !self.eat(exp!(Comma)) { if self.check_noexpect(&TokenKind::Semi) && self.look_ahead(1, |t| t.is_ident() || t.is_lifetime()) { // Add `>` to the list of expected tokens. - self.check(&token::Gt); + self.check(exp!(Gt)); // Handle `,` to `;` substitution let mut err = self.unexpected().unwrap_err(); self.bump(); @@ -706,7 +702,7 @@ impl<'a> Parser<'a> { // is present and then use that info to push the other token onto the tokens list let separated = self.check_noexpect(&token::Colon) || self.check_noexpect(&token::Eq); - if separated && (self.check(&token::Colon) | self.check(&token::Eq)) { + if separated && (self.check(exp!(Colon)) | self.check(exp!(Eq))) { let arg_span = arg.span(); let (binder, ident, gen_args) = match self.get_ident_from_generic_arg(&arg) { Ok(ident_gen_args) => ident_gen_args, @@ -721,9 +717,9 @@ impl<'a> Parser<'a> { "`for<...>` is not allowed on associated type bounds", )); } - let kind = if self.eat(&token::Colon) { + let kind = if self.eat(exp!(Colon)) { AssocItemConstraintKind::Bound { bounds: self.parse_generic_bounds()? } - } else if self.eat(&token::Eq) { + } else if self.eat(exp!(Eq)) { self.parse_assoc_equality_term( ident, gen_args.as_ref(), @@ -744,8 +740,8 @@ impl<'a> Parser<'a> { if self.prev_token.is_ident() && (self.token.is_ident() || self.look_ahead(1, |token| token.is_ident())) { - self.check(&token::Colon); - self.check(&token::Eq); + self.check(exp!(Colon)); + self.check(exp!(Eq)); } Ok(Some(AngleBracketedArg::Arg(arg))) } diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 5fa2e01fc86..1ddb5fc0a11 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -12,8 +12,7 @@ use rustc_ast::{ LocalKind, MacCall, MacCallStmt, MacStmtStyle, Recovered, Stmt, StmtKind, }; use rustc_errors::{Applicability, Diag, PResult}; -use rustc_span::symbol::{Ident, kw, sym}; -use rustc_span::{BytePos, ErrorGuaranteed, Span}; +use rustc_span::{BytePos, ErrorGuaranteed, Ident, Span, kw, sym}; use thin_vec::{ThinVec, thin_vec}; use super::attr::InnerAttrForbiddenReason; @@ -25,7 +24,7 @@ use super::{ Trailing, UsePreAttrPos, }; use crate::errors::MalformedLoopLabel; -use crate::{errors, maybe_whole}; +use crate::{errors, exp, maybe_whole}; impl<'a> Parser<'a> { /// Parses a statement. This stops just before trailing semicolons on everything but items. @@ -72,7 +71,7 @@ impl<'a> Parser<'a> { let stmt = if self.token.is_keyword(kw::Let) { self.collect_tokens(None, attrs, force_collect, |this, attrs| { - this.expect_keyword(kw::Let)?; + this.expect_keyword(exp!(Let))?; let local = this.parse_local(attrs)?; let trailing = Trailing::from(capture_semi && this.token == token::Semi); Ok(( @@ -141,7 +140,7 @@ impl<'a> Parser<'a> { force_collect, )? { self.mk_stmt(lo.to(item.span), StmtKind::Item(P(item))) - } else if self.eat(&token::Semi) { + } else if self.eat(exp!(Semi)) { // Do not attempt to parse an expression if we're done here. self.error_outer_attrs(attrs); self.mk_stmt(lo, StmtKind::Empty) @@ -157,7 +156,7 @@ impl<'a> Parser<'a> { Ok((expr, Trailing::No, UsePreAttrPos::Yes)) }, )?; - if matches!(e.kind, ExprKind::Assign(..)) && self.eat_keyword(kw::Else) { + if matches!(e.kind, ExprKind::Assign(..)) && self.eat_keyword(exp!(Else)) { let bl = self.parse_block()?; // Destructuring assignment ... else. // This is not allowed, but point it out in a nice way. @@ -177,7 +176,7 @@ impl<'a> Parser<'a> { let stmt = self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| { let path = this.parse_path(PathStyle::Expr)?; - if this.eat(&token::Not) { + if this.eat(exp!(Not)) { let stmt_mac = this.parse_stmt_mac(lo, attrs, path)?; return Ok(( stmt_mac, @@ -186,7 +185,7 @@ impl<'a> Parser<'a> { )); } - let expr = if this.eat(&token::OpenDelim(Delimiter::Brace)) { + let expr = if this.eat(exp!(OpenBrace)) { this.parse_expr_struct(None, path, true)? } else { let hi = this.prev_token.span; @@ -371,7 +370,7 @@ impl<'a> Parser<'a> { let kind = match init { None => LocalKind::Decl, Some(init) => { - if self.eat_keyword(kw::Else) { + if self.eat_keyword(exp!(Else)) { if self.token.is_keyword(kw::If) { // `let...else if`. Emit the same error that `parse_block()` would, // but explicitly point out that this pattern is not allowed. @@ -450,7 +449,7 @@ impl<'a> Parser<'a> { self.bump(); true } - _ => self.eat(&token::Eq), + _ => self.eat(exp!(Eq)), }; Ok(if eq_consumed || eq_optional { Some(self.parse_expr()?) } else { None }) @@ -510,7 +509,7 @@ impl<'a> Parser<'a> { Ok(Some(Stmt { kind: StmtKind::Empty, .. })) => {} Ok(Some(stmt)) => { let stmt_own_line = self.psess.source_map().is_line_before_span_empty(sp); - let stmt_span = if stmt_own_line && self.eat(&token::Semi) { + let stmt_span = if stmt_own_line && self.eat(exp!(Semi)) { // Expand the span to include the semicolon. stmt.span.with_hi(self.prev_token.span.hi()) } else { @@ -652,7 +651,7 @@ impl<'a> Parser<'a> { let maybe_ident = self.prev_token.clone(); self.maybe_recover_unexpected_block_label(); - if !self.eat(&token::OpenDelim(Delimiter::Brace)) { + if !self.eat(exp!(OpenBrace)) { return self.error_block_no_opening_brace(); } @@ -679,7 +678,7 @@ impl<'a> Parser<'a> { ) -> PResult<'a, P<Block>> { let mut stmts = ThinVec::new(); let mut snapshot = None; - while !self.eat(&token::CloseDelim(Delimiter::Brace)) { + while !self.eat(exp!(CloseBrace)) { if self.token == token::Eof { break; } @@ -746,6 +745,51 @@ impl<'a> Parser<'a> { Ok(self.mk_block(stmts, s, lo.to(self.prev_token.span))) } + fn recover_missing_dot(&mut self, err: &mut Diag<'_>) { + let Some((ident, _)) = self.token.ident() else { + return; + }; + if let Some(c) = ident.name.as_str().chars().next() + && c.is_uppercase() + { + return; + } + if self.token.is_reserved_ident() && !self.token.is_ident_named(kw::Await) { + return; + } + if self.prev_token.is_reserved_ident() && self.prev_token.is_ident_named(kw::Await) { + // Likely `foo.await bar` + } else if !self.prev_token.is_reserved_ident() && self.prev_token.is_ident() { + // Likely `foo bar` + } else if self.prev_token.kind == token::Question { + // `foo? bar` + } else if self.prev_token.kind == token::CloseDelim(Delimiter::Parenthesis) { + // `foo() bar` + } else { + return; + } + if self.token.span == self.prev_token.span { + // Account for syntax errors in proc-macros. + return; + } + if self.look_ahead(1, |t| [token::Semi, token::Question, token::Dot].contains(&t.kind)) { + err.span_suggestion_verbose( + self.prev_token.span.between(self.token.span), + "you might have meant to write a field access", + ".".to_string(), + Applicability::MaybeIncorrect, + ); + } + if self.look_ahead(1, |t| t.kind == token::OpenDelim(Delimiter::Parenthesis)) { + err.span_suggestion_verbose( + self.prev_token.span.between(self.token.span), + "you might have meant to write a method call", + ".".to_string(), + Applicability::MaybeIncorrect, + ); + } + } + /// Parses a statement, including the trailing semicolon. pub fn parse_full_stmt( &mut self, @@ -782,8 +826,7 @@ impl<'a> Parser<'a> { { // Just check for errors and recover; do not eat semicolon yet. - let expect_result = - self.expect_one_of(&[], &[token::Semi, token::CloseDelim(Delimiter::Brace)]); + let expect_result = self.expect_one_of(&[], &[exp!(Semi), exp!(CloseBrace)]); // Try to both emit a better diagnostic, and avoid further errors by replacing // the `expr` with `ExprKind::Err`. @@ -853,7 +896,8 @@ impl<'a> Parser<'a> { Some(if recover.no() { res? } else { - res.unwrap_or_else(|e| { + res.unwrap_or_else(|mut e| { + self.recover_missing_dot(&mut e); let guar = e.emit(); self.recover_stmt(); guar @@ -874,7 +918,12 @@ impl<'a> Parser<'a> { // We might be at the `,` in `let x = foo<bar, baz>;`. Try to recover. match &mut local.kind { LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => { - self.check_mistyped_turbofish_with_multiple_type_params(e, expr)?; + self.check_mistyped_turbofish_with_multiple_type_params(e, expr).map_err( + |mut e| { + self.recover_missing_dot(&mut e); + e + }, + )?; // We found `foo<bar, baz>`, have we fully recovered? self.expect_semi()?; } @@ -931,7 +980,7 @@ impl<'a> Parser<'a> { } } - if add_semi_to_stmt || (eat_semi && self.eat(&token::Semi)) { + if add_semi_to_stmt || (eat_semi && self.eat(exp!(Semi))) { stmt = stmt.add_trailing_semicolon(); } diff --git a/compiler/rustc_parse/src/parser/tests.rs b/compiler/rustc_parse/src/parser/tests.rs index 1813960dad0..3f8d66c2c95 100644 --- a/compiler/rustc_parse/src/parser/tests.rs +++ b/compiler/rustc_parse/src/parser/tests.rs @@ -18,8 +18,9 @@ use rustc_errors::emitter::{HumanEmitter, OutputTheme}; use rustc_errors::{DiagCtxt, MultiSpan, PResult}; use rustc_session::parse::ParseSess; use rustc_span::source_map::{FilePathMapping, SourceMap}; -use rustc_span::symbol::{Symbol, kw, sym}; -use rustc_span::{BytePos, FileName, Pos, Span, create_default_session_globals_then}; +use rustc_span::{ + BytePos, FileName, Pos, Span, Symbol, create_default_session_globals_then, kw, sym, +}; use termcolor::WriteColor; use crate::parser::{ForceCollect, Parser}; @@ -2285,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::<Vec<_>>()[..]; + let tts = &stream.iter().collect::<Vec<_>>()[..]; match tts { [ @@ -2297,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::<Vec<_>>(); + let tts = ¯o_tts.iter().collect::<Vec<_>>(); 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::<Vec<_>>(); + let tts = &first_tts.iter().collect::<Vec<_>>(); match &tts[..] { [ TokenTree::Token(Token { kind: token::Dollar, .. }, _), @@ -2316,7 +2317,7 @@ fn string_to_tts_macro() { } _ => panic!("value 3: {:?} {:?}", first_delim, first_tts), } - let tts = &second_tts.trees().collect::<Vec<_>>(); + let tts = &second_tts.iter().collect::<Vec<_>>(); match &tts[..] { [ TokenTree::Token(Token { kind: token::Dollar, .. }, _), @@ -2544,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/token_type.rs b/compiler/rustc_parse/src/parser/token_type.rs new file mode 100644 index 00000000000..73f3ac001c8 --- /dev/null +++ b/compiler/rustc_parse/src/parser/token_type.rs @@ -0,0 +1,631 @@ +use rustc_ast::token::TokenKind; +use rustc_span::symbol::{Symbol, kw, sym}; + +/// Used in "expected"/"expected one of" error messages. Tokens are added here +/// as necessary. Tokens with values (e.g. literals, identifiers) are +/// represented by a single variant (e.g. `Literal`, `Ident`). +/// +/// It's an awkward representation, but it's important for performance. It's a +/// C-style parameterless enum so that `TokenTypeSet` can be a bitset. This is +/// important because `Parser::expected_token_types` is very hot. `TokenType` +/// used to have variants with parameters (e.g. all the keywords were in a +/// single `Keyword` variant with a `Symbol` parameter) and +/// `Parser::expected_token_types` was a `Vec<TokenType>` which was much slower +/// to manipulate. +/// +/// We really want to keep the number of variants to 128 or fewer, so that +/// `TokenTypeSet` can be implemented with a `u128`. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum TokenType { + // Expression-operator symbols + Eq, + Lt, + Le, + EqEq, + Gt, + AndAnd, + OrOr, + Not, + Tilde, + + // BinOps + Plus, + Minus, + Star, + And, + Or, + + // Structural symbols + At, + Dot, + DotDot, + DotDotDot, + DotDotEq, + Comma, + Semi, + Colon, + PathSep, + RArrow, + FatArrow, + Pound, + Question, + OpenParen, + CloseParen, + OpenBrace, + CloseBrace, + OpenBracket, + CloseBracket, + Eof, + + // Token types with some details elided. + /// Any operator. + Operator, + /// Any identifier token. + Ident, + /// Any lifetime token. + Lifetime, + /// Any token that can start a path. + Path, + /// Any token that can start a type. + Type, + /// Any token that can start a const expression. + Const, + + // Keywords + // tidy-alphabetical-start + KwAs, + KwAsync, + KwAuto, + KwAwait, + KwBecome, + KwBox, + KwBreak, + KwCatch, + KwConst, + KwContinue, + KwCrate, + KwDefault, + KwDyn, + KwElse, + KwEnum, + KwExtern, + KwFn, + KwFor, + KwGen, + KwIf, + KwImpl, + KwIn, + KwLet, + KwLoop, + KwMacro, + KwMacroRules, + KwMatch, + KwMod, + KwMove, + KwMut, + KwPub, + KwRaw, + KwRef, + KwReturn, + KwReuse, + KwSafe, + KwSelfUpper, + KwStatic, + KwStruct, + KwTrait, + KwTry, + KwType, + KwUnderscore, + KwUnsafe, + KwUse, + KwWhere, + KwWhile, + KwYield, + // tidy-alphabetical-end + + // Keyword-like symbols. + // tidy-alphabetical-start + SymAttSyntax, + SymClobberAbi, + SymInlateout, + SymInout, + SymIs, + SymLabel, + SymLateout, + SymMayUnwind, + SymNomem, + SymNoreturn, + SymNostack, + SymOptions, + SymOut, + SymPreservesFlags, + SymPure, + SymReadonly, + SymSym, + // tidy-alphabetical-end +} + +// Macro to avoid repetitive boilerplate code. +macro_rules! from_u32_match { + ($val:ident; $($tok:ident,)+) => { + // A more obvious formulation would be `0 => TokenType::Eq`. But + // this formulation with the guard lets us avoid specifying a + // specific integer for each variant. + match $val { + $( + t if t == TokenType::$tok as u32 => TokenType::$tok, + )+ + _ => panic!("unhandled value: {}", $val), + } + }; +} + +impl TokenType { + fn from_u32(val: u32) -> TokenType { + let token_type = from_u32_match! { val; + Eq, + Lt, + Le, + EqEq, + Gt, + AndAnd, + OrOr, + Not, + Tilde, + + Plus, + Minus, + Star, + And, + Or, + + At, + Dot, + DotDot, + DotDotDot, + DotDotEq, + Comma, + Semi, + Colon, + PathSep, + RArrow, + FatArrow, + Pound, + Question, + OpenParen, + CloseParen, + OpenBrace, + CloseBrace, + OpenBracket, + CloseBracket, + Eof, + + Operator, + Ident, + Lifetime, + Path, + Type, + Const, + + KwAs, + KwAsync, + KwAuto, + KwAwait, + KwBecome, + KwBox, + KwBreak, + KwCatch, + KwConst, + KwContinue, + KwCrate, + KwDefault, + KwDyn, + KwElse, + KwEnum, + KwExtern, + KwFn, + KwFor, + KwGen, + KwIf, + KwImpl, + KwIn, + KwLet, + KwLoop, + KwMacro, + KwMacroRules, + KwMatch, + KwMod, + KwMove, + KwMut, + KwPub, + KwRaw, + KwRef, + KwReturn, + KwReuse, + KwSafe, + KwSelfUpper, + KwStatic, + KwStruct, + KwTrait, + KwTry, + KwType, + KwUnderscore, + KwUnsafe, + KwUse, + KwWhere, + KwWhile, + KwYield, + + SymAttSyntax, + SymClobberAbi, + SymInlateout, + SymInout, + SymIs, + SymLabel, + SymLateout, + SymMayUnwind, + SymNomem, + SymNoreturn, + SymNostack, + SymOptions, + SymOut, + SymPreservesFlags, + SymPure, + SymReadonly, + SymSym, + }; + token_type + } + + pub(super) fn is_keyword(&self) -> Option<Symbol> { + match self { + TokenType::KwAs => Some(kw::As), + TokenType::KwAsync => Some(kw::Async), + TokenType::KwAuto => Some(kw::Auto), + TokenType::KwAwait => Some(kw::Await), + TokenType::KwBecome => Some(kw::Become), + TokenType::KwBox => Some(kw::Box), + TokenType::KwBreak => Some(kw::Break), + TokenType::KwCatch => Some(kw::Catch), + TokenType::KwConst => Some(kw::Const), + TokenType::KwContinue => Some(kw::Continue), + TokenType::KwCrate => Some(kw::Crate), + TokenType::KwDefault => Some(kw::Default), + TokenType::KwDyn => Some(kw::Dyn), + TokenType::KwElse => Some(kw::Else), + TokenType::KwEnum => Some(kw::Enum), + TokenType::KwExtern => Some(kw::Extern), + TokenType::KwFn => Some(kw::Fn), + TokenType::KwFor => Some(kw::For), + TokenType::KwGen => Some(kw::Gen), + TokenType::KwIf => Some(kw::If), + TokenType::KwImpl => Some(kw::Impl), + TokenType::KwIn => Some(kw::In), + TokenType::KwLet => Some(kw::Let), + TokenType::KwLoop => Some(kw::Loop), + TokenType::KwMacroRules => Some(kw::MacroRules), + TokenType::KwMacro => Some(kw::Macro), + TokenType::KwMatch => Some(kw::Match), + TokenType::KwMod => Some(kw::Mod), + TokenType::KwMove => Some(kw::Move), + TokenType::KwMut => Some(kw::Mut), + TokenType::KwPub => Some(kw::Pub), + TokenType::KwRaw => Some(kw::Raw), + TokenType::KwRef => Some(kw::Ref), + TokenType::KwReturn => Some(kw::Return), + TokenType::KwReuse => Some(kw::Reuse), + TokenType::KwSafe => Some(kw::Safe), + TokenType::KwSelfUpper => Some(kw::SelfUpper), + TokenType::KwStatic => Some(kw::Static), + TokenType::KwStruct => Some(kw::Struct), + TokenType::KwTrait => Some(kw::Trait), + TokenType::KwTry => Some(kw::Try), + TokenType::KwType => Some(kw::Type), + TokenType::KwUnderscore => Some(kw::Underscore), + TokenType::KwUnsafe => Some(kw::Unsafe), + TokenType::KwUse => Some(kw::Use), + TokenType::KwWhere => Some(kw::Where), + TokenType::KwWhile => Some(kw::While), + TokenType::KwYield => Some(kw::Yield), + + TokenType::SymAttSyntax => Some(sym::att_syntax), + TokenType::SymClobberAbi => Some(sym::clobber_abi), + TokenType::SymInlateout => Some(sym::inlateout), + TokenType::SymInout => Some(sym::inout), + TokenType::SymIs => Some(sym::is), + TokenType::SymLabel => Some(sym::label), + TokenType::SymLateout => Some(sym::lateout), + TokenType::SymMayUnwind => Some(sym::may_unwind), + TokenType::SymNomem => Some(sym::nomem), + TokenType::SymNoreturn => Some(sym::noreturn), + TokenType::SymNostack => Some(sym::nostack), + TokenType::SymOptions => Some(sym::options), + TokenType::SymOut => Some(sym::out), + TokenType::SymPreservesFlags => Some(sym::preserves_flags), + TokenType::SymPure => Some(sym::pure), + TokenType::SymReadonly => Some(sym::readonly), + TokenType::SymSym => Some(sym::sym), + _ => None, + } + } + + // The output should be the same as that produced by + // `rustc_ast_pretty::pprust::token_to_string`. + pub(super) fn to_string(&self) -> String { + match self { + TokenType::Eq => "`=`", + TokenType::Lt => "`<`", + TokenType::Le => "`<=`", + TokenType::EqEq => "`==`", + TokenType::Gt => "`>`", + TokenType::AndAnd => "`&&`", + TokenType::OrOr => "`||`", + TokenType::Not => "`!`", + TokenType::Tilde => "`~`", + + TokenType::Plus => "`+`", + TokenType::Minus => "`-`", + TokenType::Star => "`*`", + TokenType::And => "`&`", + TokenType::Or => "`|`", + + TokenType::At => "`@`", + TokenType::Dot => "`.`", + TokenType::DotDot => "`..`", + TokenType::DotDotDot => "`...`", + TokenType::DotDotEq => "`..=`", + TokenType::Comma => "`,`", + TokenType::Semi => "`;`", + TokenType::Colon => "`:`", + TokenType::PathSep => "`::`", + TokenType::RArrow => "`->`", + TokenType::FatArrow => "`=>`", + TokenType::Pound => "`#`", + TokenType::Question => "`?`", + TokenType::OpenParen => "`(`", + TokenType::CloseParen => "`)`", + TokenType::OpenBrace => "`{`", + TokenType::CloseBrace => "`}`", + TokenType::OpenBracket => "`[`", + TokenType::CloseBracket => "`]`", + TokenType::Eof => "<eof>", + + TokenType::Operator => "an operator", + TokenType::Ident => "identifier", + TokenType::Lifetime => "lifetime", + TokenType::Path => "path", + TokenType::Type => "type", + TokenType::Const => "a const expression", + + _ => return format!("`{}`", self.is_keyword().unwrap()), + } + .to_string() + } +} + +/// Used by various `Parser` methods such as `check` and `eat`. The first field +/// is always by used those methods. The second field is only used when the +/// first field doesn't match. +#[derive(Clone, Copy, Debug)] +pub struct ExpTokenPair<'a> { + pub tok: &'a TokenKind, + pub token_type: TokenType, +} + +/// Used by various `Parser` methods such as `check_keyword` and `eat_keyword`. +/// The first field is always used by those methods. The second field is only +/// used when the first field doesn't match. +#[derive(Clone, Copy)] +pub struct ExpKeywordPair { + pub kw: Symbol, + pub token_type: TokenType, +} + +// Gets a statically-known `ExpTokenPair` pair (for non-keywords) or +// `ExpKeywordPair` (for keywords), as used with various `check`/`expect` +// methods in `Parser`. +// +// The name is short because it's used a lot. +#[macro_export] +// We don't use the normal `#[rustfmt::skip]` here because that triggers a +// bogus "macro-expanded `macro_export` macros from the current crate cannot be +// referred to by absolute paths" error, ugh. See #52234. +#[cfg_attr(rustfmt, rustfmt::skip)] +macro_rules! exp { + // `ExpTokenPair` helper rules. + (@tok, $tok:ident) => { + $crate::parser::token_type::ExpTokenPair { + tok: &rustc_ast::token::$tok, + token_type: $crate::parser::token_type::TokenType::$tok + } + }; + (@binop, $op:ident) => { + $crate::parser::token_type::ExpTokenPair { + tok: &rustc_ast::token::BinOp(rustc_ast::token::BinOpToken::$op), + token_type: $crate::parser::token_type::TokenType::$op, + } + }; + (@open, $delim:ident, $token_type:ident) => { + $crate::parser::token_type::ExpTokenPair { + tok: &rustc_ast::token::OpenDelim(rustc_ast::token::Delimiter::$delim), + token_type: $crate::parser::token_type::TokenType::$token_type, + } + }; + (@close, $delim:ident, $token_type:ident) => { + $crate::parser::token_type::ExpTokenPair { + tok: &rustc_ast::token::CloseDelim(rustc_ast::token::Delimiter::$delim), + token_type: $crate::parser::token_type::TokenType::$token_type, + } + }; + + // `ExpKeywordPair` helper rules. + (@kw, $kw:ident, $token_type:ident) => { + $crate::parser::token_type::ExpKeywordPair { + kw: rustc_span::symbol::kw::$kw, + token_type: $crate::parser::token_type::TokenType::$token_type, + } + }; + (@sym, $kw:ident, $token_type:ident) => { + $crate::parser::token_type::ExpKeywordPair { + kw: rustc_span::symbol::sym::$kw, + token_type: $crate::parser::token_type::TokenType::$token_type, + } + }; + + (Eq) => { exp!(@tok, Eq) }; + (Lt) => { exp!(@tok, Lt) }; + (Le) => { exp!(@tok, Le) }; + (EqEq) => { exp!(@tok, EqEq) }; + (Gt) => { exp!(@tok, Gt) }; + (AndAnd) => { exp!(@tok, AndAnd) }; + (OrOr) => { exp!(@tok, OrOr) }; + (Not) => { exp!(@tok, Not) }; + (Tilde) => { exp!(@tok, Tilde) }; + (At) => { exp!(@tok, At) }; + (Dot) => { exp!(@tok, Dot) }; + (DotDot) => { exp!(@tok, DotDot) }; + (DotDotDot) => { exp!(@tok, DotDotDot) }; + (DotDotEq) => { exp!(@tok, DotDotEq) }; + (Comma) => { exp!(@tok, Comma) }; + (Semi) => { exp!(@tok, Semi) }; + (Colon) => { exp!(@tok, Colon) }; + (PathSep) => { exp!(@tok, PathSep) }; + (RArrow) => { exp!(@tok, RArrow) }; + (FatArrow) => { exp!(@tok, FatArrow) }; + (Pound) => { exp!(@tok, Pound) }; + (Question) => { exp!(@tok, Question) }; + (Eof) => { exp!(@tok, Eof) }; + + (Plus) => { exp!(@binop, Plus) }; + (Minus) => { exp!(@binop, Minus) }; + (Star) => { exp!(@binop, Star) }; + (And) => { exp!(@binop, And) }; + (Or) => { exp!(@binop, Or) }; + + (OpenParen) => { exp!(@open, Parenthesis, OpenParen) }; + (OpenBrace) => { exp!(@open, Brace, OpenBrace) }; + (OpenBracket) => { exp!(@open, Bracket, OpenBracket) }; + (CloseParen) => { exp!(@close, Parenthesis, CloseParen) }; + (CloseBrace) => { exp!(@close, Brace, CloseBrace) }; + (CloseBracket) => { exp!(@close, Bracket, CloseBracket) }; + + (As) => { exp!(@kw, As, KwAs) }; + (Async) => { exp!(@kw, Async, KwAsync) }; + (Auto) => { exp!(@kw, Auto, KwAuto) }; + (Await) => { exp!(@kw, Await, KwAwait) }; + (Become) => { exp!(@kw, Become, KwBecome) }; + (Box) => { exp!(@kw, Box, KwBox) }; + (Break) => { exp!(@kw, Break, KwBreak) }; + (Catch) => { exp!(@kw, Catch, KwCatch) }; + (Const) => { exp!(@kw, Const, KwConst) }; + (Continue) => { exp!(@kw, Continue, KwContinue) }; + (Crate) => { exp!(@kw, Crate, KwCrate) }; + (Default) => { exp!(@kw, Default, KwDefault) }; + (Dyn) => { exp!(@kw, Dyn, KwDyn) }; + (Else) => { exp!(@kw, Else, KwElse) }; + (Enum) => { exp!(@kw, Enum, KwEnum) }; + (Extern) => { exp!(@kw, Extern, KwExtern) }; + (Fn) => { exp!(@kw, Fn, KwFn) }; + (For) => { exp!(@kw, For, KwFor) }; + (Gen) => { exp!(@kw, Gen, KwGen) }; + (If) => { exp!(@kw, If, KwIf) }; + (Impl) => { exp!(@kw, Impl, KwImpl) }; + (In) => { exp!(@kw, In, KwIn) }; + (Let) => { exp!(@kw, Let, KwLet) }; + (Loop) => { exp!(@kw, Loop, KwLoop) }; + (Macro) => { exp!(@kw, Macro, KwMacro) }; + (MacroRules) => { exp!(@kw, MacroRules, KwMacroRules) }; + (Match) => { exp!(@kw, Match, KwMatch) }; + (Mod) => { exp!(@kw, Mod, KwMod) }; + (Move) => { exp!(@kw, Move, KwMove) }; + (Mut) => { exp!(@kw, Mut, KwMut) }; + (Pub) => { exp!(@kw, Pub, KwPub) }; + (Raw) => { exp!(@kw, Raw, KwRaw) }; + (Ref) => { exp!(@kw, Ref, KwRef) }; + (Return) => { exp!(@kw, Return, KwReturn) }; + (Reuse) => { exp!(@kw, Reuse, KwReuse) }; + (Safe) => { exp!(@kw, Safe, KwSafe) }; + (SelfUpper) => { exp!(@kw, SelfUpper, KwSelfUpper) }; + (Static) => { exp!(@kw, Static, KwStatic) }; + (Struct) => { exp!(@kw, Struct, KwStruct) }; + (Trait) => { exp!(@kw, Trait, KwTrait) }; + (Try) => { exp!(@kw, Try, KwTry) }; + (Type) => { exp!(@kw, Type, KwType) }; + (Underscore) => { exp!(@kw, Underscore, KwUnderscore) }; + (Unsafe) => { exp!(@kw, Unsafe, KwUnsafe) }; + (Use) => { exp!(@kw, Use, KwUse) }; + (Where) => { exp!(@kw, Where, KwWhere) }; + (While) => { exp!(@kw, While, KwWhile) }; + (Yield) => { exp!(@kw, Yield, KwYield) }; + + (AttSyntax) => { exp!(@sym, att_syntax, SymAttSyntax) }; + (ClobberAbi) => { exp!(@sym, clobber_abi, SymClobberAbi) }; + (Inlateout) => { exp!(@sym, inlateout, SymInlateout) }; + (Inout) => { exp!(@sym, inout, SymInout) }; + (Is) => { exp!(@sym, is, SymIs) }; + (Label) => { exp!(@sym, label, SymLabel) }; + (Lateout) => { exp!(@sym, lateout, SymLateout) }; + (MayUnwind) => { exp!(@sym, may_unwind, SymMayUnwind) }; + (Nomem) => { exp!(@sym, nomem, SymNomem) }; + (Noreturn) => { exp!(@sym, noreturn, SymNoreturn) }; + (Nostack) => { exp!(@sym, nostack, SymNostack) }; + (Options) => { exp!(@sym, options, SymOptions) }; + (Out) => { exp!(@sym, out, SymOut) }; + (PreservesFlags) => { exp!(@sym, preserves_flags, SymPreservesFlags) }; + (Pure) => { exp!(@sym, pure, SymPure) }; + (Readonly) => { exp!(@sym, readonly, SymReadonly) }; + (Sym) => { exp!(@sym, sym, SymSym) }; +} + +/// A bitset type designed specifically for `Parser::expected_token_types`, +/// which is very hot. `u128` is the smallest integer that will fit every +/// `TokenType` value. +#[derive(Clone, Copy)] +pub(super) struct TokenTypeSet(u128); + +impl TokenTypeSet { + pub(super) fn new() -> TokenTypeSet { + TokenTypeSet(0) + } + + pub(super) fn is_empty(&self) -> bool { + self.0 == 0 + } + + pub(super) fn insert(&mut self, token_type: TokenType) { + self.0 = self.0 | (1u128 << token_type as u32) + } + + pub(super) fn clear(&mut self) { + self.0 = 0 + } + + pub(super) fn contains(&self, token_type: TokenType) -> bool { + self.0 & (1u128 << token_type as u32) != 0 + } + + pub(super) fn iter(&self) -> TokenTypeSetIter { + TokenTypeSetIter(*self) + } +} + +// The `TokenTypeSet` is a copy of the set being iterated. It initially holds +// the entire set. Each bit is cleared as it is returned. We have finished once +// it is all zeroes. +pub(super) struct TokenTypeSetIter(TokenTypeSet); + +impl Iterator for TokenTypeSetIter { + type Item = TokenType; + + fn next(&mut self) -> Option<TokenType> { + let num_bits: u32 = (std::mem::size_of_val(&self.0.0) * 8) as u32; + assert_eq!(num_bits, 128); + let z = self.0.0.trailing_zeros(); + if z == num_bits { + None + } else { + self.0.0 &= !(1 << z); // clear the trailing 1 bit + Some(TokenType::from_u32(z)) + } + } +} diff --git a/compiler/rustc_parse/src/parser/tokenstream/tests.rs b/compiler/rustc_parse/src/parser/tokenstream/tests.rs index b13b68c266a..037b5b1a9de 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_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index f696074e66a..6497d19a173 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -8,8 +8,7 @@ use rustc_ast::{ TyKind, UnsafeBinderTy, }; use rustc_errors::{Applicability, PResult}; -use rustc_span::symbol::{Ident, kw, sym}; -use rustc_span::{ErrorGuaranteed, Span}; +use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym}; use thin_vec::{ThinVec, thin_vec}; use super::{Parser, PathStyle, SeqSep, TokenType, Trailing}; @@ -19,7 +18,7 @@ use crate::errors::{ HelpUseLatestEdition, InvalidDynKeyword, LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime, NestedCVariadicType, ReturnTypesUseThinArrow, }; -use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; +use crate::{exp, maybe_recover_from_interpolated_ty_qpath, maybe_whole}; /// Signals whether parsing a type should allow `+`. /// @@ -204,7 +203,7 @@ impl<'a> Parser<'a> { recover_return_sign: RecoverReturnSign, ) -> PResult<'a, FnRetTy> { let lo = self.prev_token.span; - Ok(if self.eat(&token::RArrow) { + Ok(if self.eat(exp!(RArrow)) { // FIXME(Centril): Can we unconditionally `allow_plus`? let ty = self.parse_ty_common( allow_plus, @@ -252,28 +251,28 @@ impl<'a> Parser<'a> { let lo = self.token.span; let mut impl_dyn_multi = false; - let kind = if self.check(&token::OpenDelim(Delimiter::Parenthesis)) { + let kind = if self.check(exp!(OpenParen)) { self.parse_ty_tuple_or_parens(lo, allow_plus)? - } else if self.eat(&token::Not) { + } else if self.eat(exp!(Not)) { // Never type `!` TyKind::Never - } else if self.eat(&token::BinOp(token::Star)) { + } else if self.eat(exp!(Star)) { self.parse_ty_ptr()? - } else if self.eat(&token::OpenDelim(Delimiter::Bracket)) { + } else if self.eat(exp!(OpenBracket)) { self.parse_array_or_slice_ty()? - } else if self.check(&token::BinOp(token::And)) || self.check(&token::AndAnd) { + } else if self.check(exp!(And)) || self.check(exp!(AndAnd)) { // Reference self.expect_and()?; self.parse_borrowed_pointee()? } else if self.eat_keyword_noexpect(kw::Typeof) { self.parse_typeof_ty()? - } else if self.eat_keyword(kw::Underscore) { + } else if self.eat_keyword(exp!(Underscore)) { // A type to be inferred `_` TyKind::Infer } else if self.check_fn_front_matter(false, Case::Sensitive) { // Function pointer type self.parse_ty_bare_fn(lo, ThinVec::new(), None, recover_return_sign)? - } else if self.check_keyword(kw::For) { + } else if self.check_keyword(exp!(For)) { // Function pointer type or bound list (trait object type) starting with a poly-trait. // `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T` // `for<'lt> Trait1<'lt> + Trait2 + 'a` @@ -325,7 +324,7 @@ impl<'a> Parser<'a> { self.parse_remaining_bounds_path(lifetime_defs, path, lo, parse_plus)? } } - } else if self.eat_keyword(kw::Impl) { + } else if self.eat_keyword(exp!(Impl)) { self.parse_impl_ty(&mut impl_dyn_multi)? } else if self.is_explicit_dyn_type() { self.parse_dyn_ty(&mut impl_dyn_multi)? @@ -337,7 +336,7 @@ impl<'a> Parser<'a> { self.parse_path_start_ty(lo, allow_plus, ty_generics)? } else if self.can_begin_bound() { self.parse_bare_trait_object(lo, allow_plus)? - } else if self.eat(&token::DotDotDot) { + } else if self.eat(exp!(DotDotDot)) { match allow_c_variadic { AllowCVariadic::Yes => TyKind::CVarArgs, AllowCVariadic::No => { @@ -348,7 +347,7 @@ impl<'a> Parser<'a> { TyKind::Err(guar) } } - } else if self.check_keyword(kw::Unsafe) + } else if self.check_keyword(exp!(Unsafe)) && self.look_ahead(1, |tok| matches!(tok.kind, token::Lt)) { self.parse_unsafe_binder_ty()? @@ -375,7 +374,7 @@ impl<'a> Parser<'a> { fn parse_unsafe_binder_ty(&mut self) -> PResult<'a, TyKind> { let lo = self.token.span; - assert!(self.eat_keyword(kw::Unsafe)); + assert!(self.eat_keyword(exp!(Unsafe))); self.expect_lt()?; let generic_params = self.parse_generic_params()?; self.expect_gt()?; @@ -488,16 +487,16 @@ impl<'a> Parser<'a> { Err(err) => return Err(err), }; - let ty = if self.eat(&token::Semi) { + let ty = if self.eat(exp!(Semi)) { let mut length = self.parse_expr_anon_const()?; - if let Err(e) = self.expect(&token::CloseDelim(Delimiter::Bracket)) { + if let Err(e) = self.expect(exp!(CloseBracket)) { // Try to recover from `X<Y, ...>` when `X::<Y, ...>` works self.check_mistyped_turbofish_with_multiple_type_params(e, &mut length.value)?; - self.expect(&token::CloseDelim(Delimiter::Bracket))?; + self.expect(exp!(CloseBracket))?; } TyKind::Array(elt_ty, length) } else { - self.expect(&token::CloseDelim(Delimiter::Bracket))?; + self.expect(exp!(CloseBracket))?; TyKind::Slice(elt_ty) }; @@ -580,9 +579,9 @@ impl<'a> Parser<'a> { // Parses the `typeof(EXPR)`. // To avoid ambiguity, the type is surrounded by parentheses. fn parse_typeof_ty(&mut self) -> PResult<'a, TyKind> { - self.expect(&token::OpenDelim(Delimiter::Parenthesis))?; + self.expect(exp!(OpenParen))?; let expr = self.parse_expr_anon_const()?; - self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; + self.expect(exp!(CloseParen))?; Ok(TyKind::Typeof(expr)) } @@ -698,15 +697,15 @@ impl<'a> Parser<'a> { let lo = self.token.span; self.expect_lt()?; let (args, _, _) = self.parse_seq_to_before_tokens( - &[&TokenKind::Gt], + &[exp!(Gt)], &[ &TokenKind::Ge, &TokenKind::BinOp(BinOpToken::Shr), &TokenKind::BinOpEq(BinOpToken::Shr), ], - SeqSep::trailing_allowed(token::Comma), + SeqSep::trailing_allowed(exp!(Comma)), |self_| { - if self_.check_keyword(kw::SelfUpper) { + if self_.check_keyword(exp!(SelfUpper)) { self_.bump(); Ok(PreciseCapturingArg::Arg( ast::Path::from_ident(self_.prev_token.ident().unwrap().0), @@ -730,7 +729,7 @@ impl<'a> Parser<'a> { /// Is a `dyn B0 + ... + Bn` type allowed here? fn is_explicit_dyn_type(&mut self) -> bool { - self.check_keyword(kw::Dyn) + self.check_keyword(exp!(Dyn)) && (self.token.uninterpolated_span().at_least_rust_2018() || self.look_ahead(1, |t| { (can_begin_dyn_bound_in_edition_2015(t) || *t == TokenKind::BinOp(token::Star)) @@ -746,7 +745,7 @@ impl<'a> Parser<'a> { self.bump(); // `dyn` // parse dyn* types - let syntax = if self.eat(&TokenKind::BinOp(token::Star)) { + let syntax = if self.eat(exp!(Star)) { self.psess.gated_spans.gate(sym::dyn_star, lo.to(self.prev_token.span)); TraitObjectSyntax::DynStar } else { @@ -773,7 +772,7 @@ impl<'a> Parser<'a> { ) -> PResult<'a, TyKind> { // Simple path let path = self.parse_path_inner(PathStyle::Type, ty_generics)?; - if self.eat(&token::Not) { + if self.eat(exp!(Not)) { // Macro invocation in type position Ok(TyKind::MacCall(P(MacCall { path, args: self.parse_delim_args()? }))) } else if allow_plus == AllowPlus::Yes && self.check_plus() { @@ -826,14 +825,14 @@ impl<'a> Parser<'a> { fn can_begin_bound(&mut self) -> bool { self.check_path() || self.check_lifetime() - || self.check(&token::Not) - || self.check(&token::Question) - || self.check(&token::Tilde) - || self.check_keyword(kw::For) - || self.check(&token::OpenDelim(Delimiter::Parenthesis)) - || self.check_keyword(kw::Const) - || self.check_keyword(kw::Async) - || self.check_keyword(kw::Use) + || self.check(exp!(Not)) + || self.check(exp!(Question)) + || self.check(exp!(Tilde)) + || self.check_keyword(exp!(For)) + || self.check(exp!(OpenParen)) + || self.check_keyword(exp!(Const)) + || self.check_keyword(exp!(Async)) + || self.check_keyword(exp!(Use)) } /// Parses a bound according to the grammar: @@ -843,11 +842,11 @@ impl<'a> Parser<'a> { fn parse_generic_bound(&mut self) -> PResult<'a, GenericBound> { let lo = self.token.span; let leading_token = self.prev_token.clone(); - let has_parens = self.eat(&token::OpenDelim(Delimiter::Parenthesis)); + let has_parens = self.eat(exp!(OpenParen)); let bound = if self.token.is_lifetime() { self.parse_generic_lt_bound(lo, has_parens)? - } else if self.eat_keyword(kw::Use) { + } else if self.eat_keyword(exp!(Use)) { // parse precise captures, if any. This is `use<'lt, 'lt, P, P>`; a list of // lifetimes and ident params (including SelfUpper). These are validated later // for order, duplication, and whether they actually reference params. @@ -920,7 +919,7 @@ impl<'a> Parser<'a> { /// Recover on `('lifetime)` with `(` already eaten. fn recover_paren_lifetime(&mut self, lo: Span) -> PResult<'a, ()> { - self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; + self.expect(exp!(CloseParen))?; let span = lo.to(self.prev_token.span); let sugg = errors::RemoveParens { lo, hi: self.prev_token.span }; @@ -941,13 +940,13 @@ impl<'a> Parser<'a> { /// See `parse_generic_ty_bound` for the complete grammar of trait bound modifiers. fn parse_trait_bound_modifiers(&mut self) -> PResult<'a, TraitBoundModifiers> { let modifier_lo = self.token.span; - let constness = if self.eat(&token::Tilde) { + let constness = if self.eat(exp!(Tilde)) { let tilde = self.prev_token.span; - self.expect_keyword(kw::Const)?; + self.expect_keyword(exp!(Const))?; let span = tilde.to(self.prev_token.span); self.psess.gated_spans.gate(sym::const_trait_impl, span); BoundConstness::Maybe(span) - } else if self.eat_keyword(kw::Const) { + } else if self.eat_keyword(exp!(Const)) { self.psess.gated_spans.gate(sym::const_trait_impl, self.prev_token.span); BoundConstness::Always(self.prev_token.span) } else { @@ -955,7 +954,7 @@ impl<'a> Parser<'a> { }; let asyncness = if self.token.uninterpolated_span().at_least_rust_2018() - && self.eat_keyword(kw::Async) + && self.eat_keyword(exp!(Async)) { self.psess.gated_spans.gate(sym::async_trait_bounds, self.prev_token.span); BoundAsyncness::Async(self.prev_token.span) @@ -975,9 +974,9 @@ impl<'a> Parser<'a> { }; let modifier_hi = self.prev_token.span; - let polarity = if self.eat(&token::Question) { + let polarity = if self.eat(exp!(Question)) { BoundPolarity::Maybe(self.prev_token.span) - } else if self.eat(&token::Not) { + } else if self.eat(exp!(Not)) { self.psess.gated_spans.gate(sym::negative_bounds, self.prev_token.span); BoundPolarity::Negative(self.prev_token.span) } else { @@ -1123,7 +1122,7 @@ impl<'a> Parser<'a> { if self.token.is_like_plus() && leading_token.is_keyword(kw::Dyn) { let bounds = vec![]; self.parse_remaining_bounds(bounds, true)?; - self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; + self.expect(exp!(CloseParen))?; self.dcx().emit_err(errors::IncorrectParensTraitBounds { span: vec![lo, self.prev_token.span], sugg: errors::IncorrectParensTraitBoundsSugg { @@ -1132,7 +1131,7 @@ impl<'a> Parser<'a> { }, }); } else { - self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; + self.expect(exp!(CloseParen))?; } } @@ -1177,7 +1176,7 @@ impl<'a> Parser<'a> { pub(super) fn parse_late_bound_lifetime_defs( &mut self, ) -> PResult<'a, (ThinVec<GenericParam>, Option<Span>)> { - if self.eat_keyword(kw::For) { + if self.eat_keyword(exp!(For)) { let lo = self.token.span; self.expect_lt()?; let params = self.parse_generic_params()?; @@ -1281,7 +1280,7 @@ impl<'a> Parser<'a> { } pub(super) fn check_lifetime(&mut self) -> bool { - self.expected_tokens.push(TokenType::Lifetime); + self.expected_token_types.insert(TokenType::Lifetime); self.token.is_lifetime() } diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index aab3f10bc66..8b6b37c0f8f 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -3,8 +3,7 @@ use rustc_ast::token::Delimiter; use rustc_ast::tokenstream::DelimSpan; use rustc_ast::{ - self as ast, AttrArgs, AttrArgsEq, Attribute, DelimArgs, MetaItem, MetaItemInner, MetaItemKind, - Safety, + self as ast, AttrArgs, Attribute, DelimArgs, MetaItem, MetaItemInner, MetaItemKind, Safety, }; use rustc_errors::{Applicability, FatalError, PResult}; use rustc_feature::{AttributeSafety, AttributeTemplate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute}; @@ -70,7 +69,7 @@ pub fn parse_meta<'a>(psess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Met parse_in(psess, tokens.clone(), "meta list", |p| p.parse_meta_seq_top())?; MetaItemKind::List(nmis) } - AttrArgs::Eq { value: AttrArgsEq::Ast(expr), .. } => { + AttrArgs::Eq { expr, .. } => { if let ast::ExprKind::Lit(token_lit) = expr.kind { let res = ast::MetaItemLit::from_token_lit(token_lit, expr.span); let res = match res { @@ -116,9 +115,6 @@ pub fn parse_meta<'a>(psess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Met return Err(err); } } - AttrArgs::Eq { value: AttrArgsEq::Hir(lit), .. } => { - MetaItemKind::NameValue(lit.clone()) - } }, }) } diff --git a/compiler/rustc_passes/Cargo.toml b/compiler/rustc_passes/Cargo.toml index ed5991459ac..2b8a3b9ce23 100644 --- a/compiler/rustc_passes/Cargo.toml +++ b/compiler/rustc_passes/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_expand = { path = "../rustc_expand" } @@ -16,7 +16,6 @@ rustc_feature = { path = "../rustc_feature" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } -rustc_lexer = { path = "../rustc_lexer" } rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_privacy = { path = "../rustc_privacy" } diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 7a0a518bb51..ba3101e9058 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -211,8 +211,9 @@ passes_doc_invalid = passes_doc_keyword_empty_mod = `#[doc(keyword = "...")]` should be used on empty modules -passes_doc_keyword_invalid_ident = - `{$doc_keyword}` is not a valid identifier +passes_doc_keyword_not_keyword = + nonexistent keyword `{$keyword}` used in `#[doc(keyword = "...")]` + .help = only existing keywords are allowed in core/std passes_doc_keyword_not_mod = `#[doc(keyword = "...")]` should be used on modules @@ -356,6 +357,9 @@ passes_ignored_derived_impls = passes_implied_feature_not_exist = feature `{$implied_by}` implying `{$feature}` does not exist +passes_incorrect_do_not_recommend_args = + `#[diagnostic::do_not_recommend]` does not expect any arguments + passes_incorrect_do_not_recommend_location = `#[diagnostic::do_not_recommend]` can only be placed on trait implementations @@ -562,6 +566,10 @@ passes_no_sanitize = `#[no_sanitize({$attr_str})]` should be applied to {$accepted_kind} .label = not {$accepted_kind} +passes_non_exaustive_with_default_field_values = + `#[non_exhaustive]` can't be used to annotate items with default field values + .label = this struct has default field values + passes_non_exported_macro_invalid_attrs = attribute should be applied to function or closure .label = not a function or closure diff --git a/compiler/rustc_passes/src/abi_test.rs b/compiler/rustc_passes/src/abi_test.rs index 4db8584b884..75a1bb39a74 100644 --- a/compiler/rustc_passes/src/abi_test.rs +++ b/compiler/rustc_passes/src/abi_test.rs @@ -1,11 +1,11 @@ -use rustc_ast::Attribute; +use rustc_hir::Attribute; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_middle::span_bug; use rustc_middle::ty::layout::{FnAbiError, LayoutError}; use rustc_middle::ty::{self, GenericArgs, Instance, Ty, TyCtxt}; use rustc_span::source_map::Spanned; -use rustc_span::symbol::sym; +use rustc_span::sym; use rustc_target::abi::call::FnAbi; use super::layout_test::ensure_wf; diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 074fe77324f..8cf20a378d4 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -7,17 +7,15 @@ use std::cell::Cell; use std::collections::hash_map::Entry; -use rustc_ast::{ - AttrKind, AttrStyle, Attribute, LitKind, MetaItemInner, MetaItemKind, MetaItemLit, ast, -}; +use rustc_ast::{AttrStyle, LitKind, MetaItemInner, MetaItemKind, MetaItemLit, ast}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey}; use rustc_feature::{AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute}; use rustc_hir::def_id::LocalModDefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{ - self as hir, self, AssocItemKind, CRATE_HIR_ID, CRATE_OWNER_ID, FnSig, ForeignItem, HirId, - Item, ItemKind, MethodKind, Safety, Target, TraitItem, + self as hir, self, AssocItemKind, AttrKind, Attribute, CRATE_HIR_ID, CRATE_OWNER_ID, FnSig, + ForeignItem, HirId, Item, ItemKind, MethodKind, Safety, Target, TraitItem, }; use rustc_macros::LintDiagnostic; use rustc_middle::hir::nested_filter; @@ -32,8 +30,7 @@ use rustc_session::lint::builtin::{ UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES, }; use rustc_session::parse::feature_err; -use rustc_span::symbol::{Symbol, kw, sym}; -use rustc_span::{BytePos, DUMMY_SP, Span}; +use rustc_span::{BytePos, DUMMY_SP, Span, Symbol, kw, sym}; use rustc_target::abi::Size; use rustc_target::spec::abi::Abi; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; @@ -118,7 +115,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { for attr in attrs { match attr.path().as_slice() { [sym::diagnostic, sym::do_not_recommend, ..] => { - self.check_do_not_recommend(attr.span, hir_id, target) + self.check_do_not_recommend(attr.span, hir_id, target, attr, item) } [sym::diagnostic, sym::on_unimplemented, ..] => { self.check_diagnostic_on_unimplemented(attr.span, hir_id, target) @@ -127,7 +124,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::coverage, ..] => self.check_coverage(attr, span, target), [sym::optimize, ..] => self.check_optimize(hir_id, attr, span, target), [sym::no_sanitize, ..] => self.check_no_sanitize(attr, span, target), - [sym::non_exhaustive, ..] => self.check_non_exhaustive(hir_id, attr, span, target), + [sym::non_exhaustive, ..] => self.check_non_exhaustive(hir_id, attr, span, target, item), [sym::marker, ..] => self.check_marker(hir_id, attr, span, target), [sym::target_feature, ..] => { self.check_target_feature(hir_id, attr, span, target, attrs) @@ -189,6 +186,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::rustc_coinductive, ..] | [sym::rustc_must_implement_one_of, ..] | [sym::rustc_deny_explicit_impl, ..] + | [sym::rustc_do_not_implement_via_object, ..] | [sym::const_trait, ..] => self.check_must_be_applied_to_trait(attr, span, target), [sym::collapse_debuginfo, ..] => self.check_collapse_debuginfo(attr, span, target), [sym::must_not_suspend, ..] => self.check_must_not_suspend(attr, span, target), @@ -351,8 +349,21 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } /// Checks if `#[diagnostic::do_not_recommend]` is applied on a trait impl. - fn check_do_not_recommend(&self, attr_span: Span, hir_id: HirId, target: Target) { - if !matches!(target, Target::Impl) { + fn check_do_not_recommend( + &self, + attr_span: Span, + hir_id: HirId, + target: Target, + attr: &Attribute, + item: Option<ItemLike<'_>>, + ) { + if !matches!(target, Target::Impl) + || matches!( + item, + Some(ItemLike::Item(hir::Item { kind: hir::ItemKind::Impl(_impl),.. })) + if _impl.of_trait.is_none() + ) + { self.tcx.emit_node_span_lint( UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, hir_id, @@ -360,6 +371,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> { errors::IncorrectDoNotRecommendLocation, ); } + if !attr.is_word() { + self.tcx.emit_node_span_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + hir_id, + attr_span, + errors::DoNotRecommendDoesNotExpectArgs, + ); + } } /// Checks if `#[diagnostic::on_unimplemented]` is applied to a trait definition @@ -666,9 +685,30 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid. - fn check_non_exhaustive(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { + fn check_non_exhaustive( + &self, + hir_id: HirId, + attr: &Attribute, + span: Span, + target: Target, + item: Option<ItemLike<'_>>, + ) { match target { - Target::Struct | Target::Enum | Target::Variant => {} + Target::Struct => { + if let Some(ItemLike::Item(hir::Item { + kind: hir::ItemKind::Struct(hir::VariantData::Struct { fields, .. }, _), + .. + })) = item + && !fields.is_empty() + && fields.iter().any(|f| f.default.is_some()) + { + self.dcx().emit_err(errors::NonExhaustiveWithDefaultFieldValues { + attr_span: attr.span, + defn_span: span, + }); + } + } + Target::Enum | Target::Variant => {} // FIXME(#80564): We permit struct fields, match arms and macro defs to have an // `#[non_exhaustive]` attribute with just a lint, because we previously // erroneously allowed it and some crates used it accidentally, to be compatible @@ -715,7 +755,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { attrs: &[Attribute], ) { match target { - Target::Fn => { + Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) + | Target::Fn => { // `#[target_feature]` is not allowed in lang items. if let Some((lang_item, _)) = hir::lang_items::extract(attrs) // Calling functions with `#[target_feature]` is @@ -732,7 +773,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { }); } } - Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => {} // FIXME: #[target_feature] was previously erroneously allowed on statements and some // crates used this, so only emit a warning. Target::Statement => { @@ -914,6 +954,13 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } fn check_doc_keyword(&self, meta: &MetaItemInner, hir_id: HirId) { + fn is_doc_keyword(s: Symbol) -> bool { + // FIXME: Once rustdoc can handle URL conflicts on case insensitive file systems, we + // can remove the `SelfTy` case here, remove `sym::SelfTy`, and update the + // `#[doc(keyword = "SelfTy")` attribute in `library/std/src/keyword_docs.rs`. + s <= kw::Union || s == sym::SelfTy + } + let doc_keyword = meta.value_str().unwrap_or(kw::Empty); if doc_keyword == kw::Empty { self.doc_attr_str_error(meta, "keyword"); @@ -935,10 +982,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> { return; } } - if !rustc_lexer::is_ident(doc_keyword.as_str()) { - self.dcx().emit_err(errors::DocKeywordInvalidIdent { + if !is_doc_keyword(doc_keyword) { + self.dcx().emit_err(errors::DocKeywordNotKeyword { span: meta.name_value_literal_span().unwrap_or_else(|| meta.span()), - doc_keyword, + keyword: doc_keyword, }); } } @@ -1176,10 +1223,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { specified_inline: &mut Option<(bool, Span)>, aliases: &mut FxHashMap<String, Span>, ) { - if let Some(mi) = attr.meta() - && let Some(list) = mi.meta_item_list() - { - for meta in list { + if let Some(list) = attr.meta_item_list() { + for meta in &list { if let Some(i_meta) = meta.meta_item() { match i_meta.name_or_empty() { sym::alias => { @@ -1279,7 +1324,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttrStyle::Inner => "!", AttrStyle::Outer => "", }, - sugg: (attr.meta().unwrap().span, applicability), + sugg: (attr.span, applicability), }, ); } else if i_meta.has_name(sym::passes) @@ -2141,10 +2186,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { fn check_confusables(&self, attr: &Attribute, target: Target) { match target { Target::Method(MethodKind::Inherent) => { - let Some(meta) = attr.meta() else { - return; - }; - let ast::MetaItem { kind: MetaItemKind::List(ref metas), .. } = meta else { + let Some(metas) = attr.meta_item_list() else { return; }; @@ -2602,7 +2644,7 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { if let AttrKind::Normal(ref p) = attr.kind { tcx.dcx().try_steal_replace_and_emit_err( - p.item.path.span, + p.path.span, StashKey::UndeterminedMacroResolution, err, ); diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index ecf8d34ad84..c5dc8e68fe8 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -22,7 +22,7 @@ use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::lint; use rustc_session::lint::builtin::DEAD_CODE; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::{Symbol, sym}; use crate::errors::{ ChangeFields, IgnoredDerivedImpls, MultipleDeadCodes, ParentInfo, UselessAssignment, diff --git a/compiler/rustc_passes/src/diagnostic_items.rs b/compiler/rustc_passes/src/diagnostic_items.rs index 1c3d3bf3dea..7b02aecdfae 100644 --- a/compiler/rustc_passes/src/diagnostic_items.rs +++ b/compiler/rustc_passes/src/diagnostic_items.rs @@ -9,13 +9,12 @@ //! //! * Compiler internal types like `Ty` and `TyCtxt` -use rustc_ast as ast; -use rustc_hir::OwnerId; use rustc_hir::diagnostic_items::DiagnosticItems; +use rustc_hir::{Attribute, OwnerId}; use rustc_middle::query::{LocalCrate, Providers}; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::{DefId, LOCAL_CRATE}; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::{Symbol, sym}; use crate::errors::DuplicateDiagnosticItemInCrate; @@ -55,7 +54,7 @@ fn report_duplicate_item( } /// Extract the first `rustc_diagnostic_item = "$name"` out of a list of attributes. -fn extract(attrs: &[ast::Attribute]) -> Option<Symbol> { +fn extract(attrs: &[Attribute]) -> Option<Symbol> { attrs.iter().find_map(|attr| { if attr.has_name(sym::rustc_diagnostic_item) { attr.value_str() } else { None } }) diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs index 690808e7527..4949a4316a7 100644 --- a/compiler/rustc_passes/src/entry.rs +++ b/compiler/rustc_passes/src/entry.rs @@ -8,8 +8,7 @@ use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::RemapFileNameExt; use rustc_session::config::{CrateType, EntryFnType, RemapPathScopeComponents, sigpipe}; -use rustc_span::symbol::sym; -use rustc_span::{Span, Symbol}; +use rustc_span::{Span, Symbol, sym}; use crate::errors::{ AttrOnlyInFunctions, ExternMain, MultipleRustcMain, MultipleStartFunctions, NoMainErr, diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index fdc7e1bba2f..163325f2a3c 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -20,6 +20,10 @@ use crate::lang_items::Duplicate; #[diag(passes_incorrect_do_not_recommend_location)] pub(crate) struct IncorrectDoNotRecommendLocation; +#[derive(LintDiagnostic)] +#[diag(passes_incorrect_do_not_recommend_args)] +pub(crate) struct DoNotRecommendDoesNotExpectArgs; + #[derive(Diagnostic)] #[diag(passes_autodiff_attr)] pub(crate) struct AutoDiffAttr { @@ -116,6 +120,15 @@ pub(crate) struct NonExhaustiveWrongLocation { } #[derive(Diagnostic)] +#[diag(passes_non_exaustive_with_default_field_values)] +pub(crate) struct NonExhaustiveWithDefaultFieldValues { + #[primary_span] + pub attr_span: Span, + #[label] + pub defn_span: Span, +} + +#[derive(Diagnostic)] #[diag(passes_should_be_applied_to_trait)] pub(crate) struct AttrShouldBeAppliedToTrait { #[primary_span] @@ -216,18 +229,19 @@ pub(crate) struct DocKeywordEmptyMod { } #[derive(Diagnostic)] -#[diag(passes_doc_keyword_not_mod)] -pub(crate) struct DocKeywordNotMod { +#[diag(passes_doc_keyword_not_keyword)] +#[help] +pub(crate) struct DocKeywordNotKeyword { #[primary_span] pub span: Span, + pub keyword: Symbol, } #[derive(Diagnostic)] -#[diag(passes_doc_keyword_invalid_ident)] -pub(crate) struct DocKeywordInvalidIdent { +#[diag(passes_doc_keyword_not_mod)] +pub(crate) struct DocKeywordNotMod { #[primary_span] pub span: Span, - pub doc_keyword: Symbol, } #[derive(Diagnostic)] diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index 7ccbc7bdc57..f9cb8c9b927 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -22,6 +22,10 @@ impl NodeStats { fn new() -> NodeStats { NodeStats { count: 0, size: 0 } } + + fn accum_size(&self) -> usize { + self.count * self.size + } } struct Node { @@ -121,11 +125,9 @@ impl<'k> StatCollector<'k> { // We will soon sort, so the initial order does not matter. #[allow(rustc::potential_query_instability)] let mut nodes: Vec<_> = self.nodes.iter().collect(); - nodes.sort_by_cached_key(|(label, node)| { - (node.stats.count * node.stats.size, label.to_owned()) - }); + nodes.sort_by_cached_key(|(label, node)| (node.stats.accum_size(), label.to_owned())); - let total_size = nodes.iter().map(|(_, node)| node.stats.count * node.stats.size).sum(); + let total_size = nodes.iter().map(|(_, node)| node.stats.accum_size()).sum(); let total_count = nodes.iter().map(|(_, node)| node.stats.count).sum(); eprintln!("{prefix} {title}"); @@ -138,7 +140,7 @@ impl<'k> StatCollector<'k> { let percent = |m, n| (m * 100) as f64 / n as f64; for (label, node) in nodes { - let size = node.stats.count * node.stats.size; + let size = node.stats.accum_size(); eprintln!( "{} {:<18}{:>10} ({:4.1}%){:>14}{:>14}", prefix, @@ -152,10 +154,12 @@ impl<'k> StatCollector<'k> { // We will soon sort, so the initial order does not matter. #[allow(rustc::potential_query_instability)] let mut subnodes: Vec<_> = node.subnodes.iter().collect(); - subnodes.sort_by_key(|(_, subnode)| subnode.count * subnode.size); + subnodes.sort_by_cached_key(|(label, subnode)| { + (subnode.accum_size(), label.to_owned()) + }); for (label, subnode) in subnodes { - let size = subnode.count * subnode.size; + let size = subnode.accum_size(); eprintln!( "{} - {:<18}{:>10} ({:4.1}%){:>14}", prefix, @@ -500,7 +504,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { hir_visit::walk_assoc_item_constraint(self, constraint) } - fn visit_attribute(&mut self, attr: &'v ast::Attribute) { + fn visit_attribute(&mut self, attr: &'v hir::Attribute) { self.record("Attribute", None, attr); } diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs index 19f00aa3c1b..fe1140d893e 100644 --- a/compiler/rustc_passes/src/lang_items.rs +++ b/compiler/rustc_passes/src/lang_items.rs @@ -16,8 +16,7 @@ use rustc_hir::{LangItem, LanguageItems, MethodKind, Target}; use rustc_middle::query::Providers; use rustc_middle::ty::{ResolverAstLowering, TyCtxt}; use rustc_session::cstore::ExternCrate; -use rustc_span::Span; -use rustc_span::symbol::kw::Empty; +use rustc_span::{Span, kw}; use crate::errors::{ DuplicateLangItem, IncorrectTarget, LangItemOnIncorrectTarget, UnknownLangItem, @@ -99,7 +98,7 @@ impl<'ast, 'tcx> LanguageItemCollector<'ast, 'tcx> { { let lang_item_name = lang_item.name(); let crate_name = self.tcx.crate_name(item_def_id.krate); - let mut dependency_of = Empty; + let mut dependency_of = kw::Empty; let is_local = item_def_id.is_local(); let path = if is_local { String::new() @@ -113,8 +112,8 @@ impl<'ast, 'tcx> LanguageItemCollector<'ast, 'tcx> { }; let first_defined_span = self.item_spans.get(&original_def_id).copied(); - let mut orig_crate_name = Empty; - let mut orig_dependency_of = Empty; + let mut orig_crate_name = kw::Empty; + let mut orig_dependency_of = kw::Empty; let orig_is_local = original_def_id.is_local(); let orig_path = if orig_is_local { String::new() diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs index bb90b5a1e31..1133cf93304 100644 --- a/compiler/rustc_passes/src/layout_test.rs +++ b/compiler/rustc_passes/src/layout_test.rs @@ -1,13 +1,12 @@ use rustc_abi::{HasDataLayout, TargetDataLayout}; -use rustc_ast::Attribute; +use rustc_hir::Attribute; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_middle::span_bug; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutError, LayoutOfHelpers}; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::Span; use rustc_span::source_map::Spanned; -use rustc_span::symbol::sym; +use rustc_span::{Span, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::TyCtxtInferExt; use rustc_trait_selection::traits; diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs index 8a360c017ad..600c46eb3d0 100644 --- a/compiler/rustc_passes/src/lib_features.rs +++ b/compiler/rustc_passes/src/lib_features.rs @@ -4,15 +4,14 @@ //! but are not declared in one single location (unlike lang features), which means we need to //! collect them instead. -use rustc_ast::Attribute; -use rustc_attr::VERSION_PLACEHOLDER; +use rustc_attr_parsing::VERSION_PLACEHOLDER; +use rustc_hir::Attribute; use rustc_hir::intravisit::Visitor; use rustc_middle::hir::nested_filter; use rustc_middle::middle::lib_features::{FeatureStability, LibFeatures}; use rustc_middle::query::{LocalCrate, Providers}; use rustc_middle::ty::TyCtxt; -use rustc_span::symbol::Symbol; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol, sym}; use crate::errors::{FeaturePreviouslyDeclared, FeatureStableTwice}; diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 034f7308c4a..b85a987c641 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -96,8 +96,7 @@ use rustc_middle::query::Providers; use rustc_middle::span_bug; use rustc_middle::ty::{self, RootVariableMinCaptureList, Ty, TyCtxt}; use rustc_session::lint; -use rustc_span::symbol::{Symbol, kw, sym}; -use rustc_span::{BytePos, Span}; +use rustc_span::{BytePos, Span, Symbol, kw, sym}; use tracing::{debug, instrument}; use self::LiveNodeKind::*; diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs index 766a9e1bdad..cad488bd71d 100644 --- a/compiler/rustc_passes/src/naked_functions.rs +++ b/compiler/rustc_passes/src/naked_functions.rs @@ -10,8 +10,7 @@ use rustc_middle::query::Providers; use rustc_middle::span_bug; use rustc_middle::ty::TyCtxt; use rustc_session::lint::builtin::UNDEFINED_NAKED_FUNCTION_ABI; -use rustc_span::Span; -use rustc_span::symbol::sym; +use rustc_span::{Span, sym}; use rustc_target::spec::abi::Abi; use crate::errors::{ diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 2809ad453ff..d3637dac6b7 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -4,9 +4,9 @@ use std::mem::replace; use std::num::NonZero; -use rustc_attr::{ +use rustc_attr_parsing::{ self as attr, ConstStability, DeprecatedSince, Stability, StabilityLevel, StableSince, - Unstable, UnstableReason, VERSION_PLACEHOLDER, + UnstableReason, VERSION_PLACEHOLDER, }; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet}; @@ -25,8 +25,7 @@ use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::lint; use rustc_session::lint::builtin::{INEFFECTIVE_UNSTABLE_TRAIT_IMPL, USELESS_DEPRECATED}; -use rustc_span::Span; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::{Span, Symbol, sym}; use tracing::{debug, info}; use crate::errors; @@ -199,7 +198,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { // this is *almost surely* an accident. if let ( &Some(DeprecatedSince::RustcVersion(dep_since)), - &attr::Stable { since: stab_since, .. }, + &attr::StabilityLevel::Stable { since: stab_since, .. }, ) = (&depr.as_ref().map(|(d, _)| d.since), &stab.level) { match stab_since { @@ -224,15 +223,17 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { // Stable *language* features shouldn't be used as unstable library features. // (Not doing this for stable library features is checked by tidy.) - if let Stability { level: Unstable { .. }, feature } = stab { + if let Stability { level: StabilityLevel::Unstable { .. }, feature } = stab { if ACCEPTED_LANG_FEATURES.iter().find(|f| f.name == feature).is_some() { self.tcx .dcx() .emit_err(errors::UnstableAttrForAlreadyStableFeature { span, item_sp }); } } - if let Stability { level: Unstable { implied_by: Some(implied_by), .. }, feature } = - stab + if let Stability { + level: StabilityLevel::Unstable { implied_by: Some(implied_by), .. }, + feature, + } = stab { self.index.implications.insert(implied_by, feature); } @@ -278,8 +279,10 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { // Stable *language* features shouldn't be used as unstable library features. // (Not doing this for stable library features is checked by tidy.) - if let Some((ConstStability { level: Unstable { .. }, feature, .. }, const_span)) = - const_stab + if let Some(( + ConstStability { level: StabilityLevel::Unstable { .. }, feature, .. }, + const_span, + )) = const_stab { if ACCEPTED_LANG_FEATURES.iter().find(|f| f.name == feature).is_some() { self.tcx.dcx().emit_err(errors::UnstableAttrForAlreadyStableFeature { @@ -314,7 +317,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { }); if let Some(ConstStability { - level: Unstable { implied_by: Some(implied_by), .. }, + level: StabilityLevel::Unstable { implied_by: Some(implied_by), .. }, feature, .. }) = const_stab @@ -780,7 +783,11 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { // error if all involved types and traits are stable, because // it will have no effect. // See: https://github.com/rust-lang/rust/issues/55436 - if let Some((Stability { level: attr::Unstable { .. }, .. }, span)) = stab { + if let Some(( + Stability { level: attr::StabilityLevel::Unstable { .. }, .. }, + span, + )) = stab + { let mut c = CheckTraitImplStable { tcx: self.tcx, fully_stable: true }; c.visit_ty(self_ty); c.visit_trait_ref(t); diff --git a/compiler/rustc_privacy/Cargo.toml b/compiler/rustc_privacy/Cargo.toml index f998e0ff154..eb48155919f 100644 --- a/compiler/rustc_privacy/Cargo.toml +++ b/compiler/rustc_privacy/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start rustc_ast = { path = "../rustc_ast" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 3057f13e3a7..c50c9007a01 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -37,11 +37,10 @@ use rustc_middle::ty::{ }; use rustc_middle::{bug, span_bug}; use rustc_session::lint; -use rustc_span::Span; use rustc_span::hygiene::Transparency; -use rustc_span::symbol::{Ident, kw, sym}; +use rustc_span::{Ident, Span, kw, sym}; use tracing::debug; -use {rustc_attr as attr, rustc_hir as hir}; +use {rustc_attr_parsing as attr, rustc_hir as hir}; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_query_system/src/ich/hcx.rs b/compiler/rustc_query_system/src/ich/hcx.rs index ad4b194eb50..6b737ceb50a 100644 --- a/compiler/rustc_query_system/src/ich/hcx.rs +++ b/compiler/rustc_query_system/src/ich/hcx.rs @@ -6,8 +6,7 @@ use rustc_hir::definitions::DefPathHash; use rustc_session::Session; use rustc_session::cstore::Untracked; use rustc_span::source_map::SourceMap; -use rustc_span::symbol::Symbol; -use rustc_span::{BytePos, CachingSourceMapView, DUMMY_SP, SourceFile, Span, SpanData}; +use rustc_span::{BytePos, CachingSourceMapView, DUMMY_SP, SourceFile, Span, SpanData, Symbol}; use crate::ich; diff --git a/compiler/rustc_query_system/src/ich/impls_syntax.rs b/compiler/rustc_query_system/src/ich/impls_syntax.rs index 5a72e80a0a5..480fd497728 100644 --- a/compiler/rustc_query_system/src/ich/impls_syntax.rs +++ b/compiler/rustc_query_system/src/ich/impls_syntax.rs @@ -1,18 +1,17 @@ //! This module contains `HashStable` implementations for various data types -//! from `rustc_ast` in no particular order. +//! from various crates in no particular order. -use std::assert_matches::assert_matches; - -use rustc_ast as ast; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_hir as hir; use rustc_span::SourceFile; use smallvec::SmallVec; use crate::ich::StableHashingContext; impl<'ctx> rustc_target::HashStableContext for StableHashingContext<'ctx> {} +impl<'ctx> rustc_ast::HashStableContext for StableHashingContext<'ctx> {} -impl<'a> HashStable<StableHashingContext<'a>> for [ast::Attribute] { +impl<'a> HashStable<StableHashingContext<'a>> for [hir::Attribute] { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { if self.is_empty() { self.len().hash_stable(hcx, hasher); @@ -20,7 +19,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for [ast::Attribute] { } // Some attributes are always ignored during hashing. - let filtered: SmallVec<[&ast::Attribute; 8]> = self + let filtered: SmallVec<[&hir::Attribute; 8]> = self .iter() .filter(|attr| { !attr.is_doc_comment() @@ -35,30 +34,23 @@ impl<'a> HashStable<StableHashingContext<'a>> for [ast::Attribute] { } } -impl<'ctx> rustc_ast::HashStableContext for StableHashingContext<'ctx> { - fn hash_attr(&mut self, attr: &ast::Attribute, hasher: &mut StableHasher) { +impl<'ctx> rustc_hir::HashStableContext for StableHashingContext<'ctx> { + fn hash_attr(&mut self, attr: &hir::Attribute, hasher: &mut StableHasher) { // Make sure that these have been filtered out. debug_assert!(!attr.ident().is_some_and(|ident| self.is_ignored_attr(ident.name))); debug_assert!(!attr.is_doc_comment()); - let ast::Attribute { kind, id: _, style, span } = attr; - if let ast::AttrKind::Normal(normal) = kind { - normal.item.hash_stable(self, hasher); + let hir::Attribute { kind, id: _, style, span } = attr; + if let hir::AttrKind::Normal(item) = kind { + item.hash_stable(self, hasher); style.hash_stable(self, hasher); span.hash_stable(self, hasher); - assert_matches!( - normal.tokens.as_ref(), - None, - "Tokens should have been removed during lowering!" - ); } else { unreachable!(); } } } -impl<'ctx> rustc_hir::HashStableContext for StableHashingContext<'ctx> {} - impl<'a> HashStable<StableHashingContext<'a>> for SourceFile { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let SourceFile { diff --git a/compiler/rustc_query_system/src/ich/mod.rs b/compiler/rustc_query_system/src/ich/mod.rs index 8f28b1d4c80..852d93b711f 100644 --- a/compiler/rustc_query_system/src/ich/mod.rs +++ b/compiler/rustc_query_system/src/ich/mod.rs @@ -1,6 +1,6 @@ //! ICH - Incremental Compilation Hash -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::{Symbol, sym}; pub use self::hcx::StableHashingContext; diff --git a/compiler/rustc_resolve/Cargo.toml b/compiler/rustc_resolve/Cargo.toml index b71853b871d..309227176d4 100644 --- a/compiler/rustc_resolve/Cargo.toml +++ b/compiler/rustc_resolve/Cargo.toml @@ -10,7 +10,7 @@ pulldown-cmark = { version = "0.11", features = ["html"], default-features = fal rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_expand = { path = "../rustc_expand" } diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 924b8afa329..3fe1eed243f 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -12,7 +12,7 @@ use rustc_ast::{ self as ast, AssocItem, AssocItemKind, Block, ForeignItem, ForeignItemKind, Impl, Item, ItemKind, MetaItemKind, NodeId, StmtKind, }; -use rustc_attr as attr; +use rustc_attr_parsing as attr; use rustc_data_structures::sync::Lrc; use rustc_expand::base::ResolverExpand; use rustc_expand::expand::AstFragment; @@ -22,9 +22,8 @@ use rustc_metadata::creader::LoadedMacro; use rustc_middle::metadata::ModChild; use rustc_middle::ty::Feed; use rustc_middle::{bug, ty}; -use rustc_span::Span; use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind}; -use rustc_span::symbol::{Ident, Symbol, kw, sym}; +use rustc_span::{Ident, Span, Symbol, kw, sym}; use tracing::debug; use crate::Namespace::{MacroNS, TypeNS, ValueNS}; @@ -634,7 +633,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { } ast::UseTreeKind::Glob => { let kind = ImportKind::Glob { - is_prelude: attr::contains_name(&item.attrs, sym::prelude_import), + is_prelude: ast::attr::contains_name(&item.attrs, sym::prelude_import), max_vis: Cell::new(None), id, }; @@ -777,7 +776,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { expansion.to_expn_id(), item.span, parent.no_implicit_prelude - || attr::contains_name(&item.attrs, sym::no_implicit_prelude), + || ast::attr::contains_name(&item.attrs, sym::no_implicit_prelude), ); self.r.define(parent, ident, TypeNS, (module, vis, sp, expansion)); @@ -835,7 +834,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { // If the structure is marked as non_exhaustive then lower the visibility // to within the crate. let mut ctor_vis = if vis.is_public() - && attr::contains_name(&item.attrs, sym::non_exhaustive) + && ast::attr::contains_name(&item.attrs, sym::non_exhaustive) { ty::Visibility::Restricted(CRATE_DEF_ID) } else { @@ -1176,11 +1175,11 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { } fn proc_macro_stub(&self, item: &ast::Item) -> Option<(MacroKind, Ident, Span)> { - if attr::contains_name(&item.attrs, sym::proc_macro) { + if ast::attr::contains_name(&item.attrs, sym::proc_macro) { return Some((MacroKind::Bang, item.ident, item.span)); - } else if attr::contains_name(&item.attrs, sym::proc_macro_attribute) { + } else if ast::attr::contains_name(&item.attrs, sym::proc_macro_attribute) { return Some((MacroKind::Attr, item.ident, item.span)); - } else if let Some(attr) = attr::find_by_name(&item.attrs, sym::proc_macro_derive) { + } else if let Some(attr) = ast::attr::find_by_name(&item.attrs, sym::proc_macro_derive) { if let Some(meta_item_inner) = attr.meta_item_list().and_then(|list| list.get(0).cloned()) { @@ -1233,7 +1232,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { if macro_rules { let ident = ident.normalize_to_macros_2_0(); self.r.macro_names.insert(ident); - let is_macro_export = attr::contains_name(&item.attrs, sym::macro_export); + let is_macro_export = ast::attr::contains_name(&item.attrs, sym::macro_export); let vis = if is_macro_export { ty::Visibility::Public } else { @@ -1507,7 +1506,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for BuildReducedGraphVisitor<'a, 'ra, 'tcx> { // If the variant is marked as non_exhaustive then lower the visibility to within the crate. let ctor_vis = - if vis.is_public() && attr::contains_name(&variant.attrs, sym::non_exhaustive) { + if vis.is_public() && ast::attr::contains_name(&variant.attrs, sym::non_exhaustive) { ty::Visibility::Restricted(CRATE_DEF_ID) } else { vis diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index e36055e8575..5a605df88c2 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -33,8 +33,7 @@ use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::{ MACRO_USE_EXTERN_CRATE, UNUSED_EXTERN_CRATES, UNUSED_IMPORTS, UNUSED_QUALIFICATIONS, }; -use rustc_span::symbol::{Ident, kw}; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Ident, Span, kw}; use crate::imports::{Import, ImportKind}; use crate::{LexicalScopeBinding, NameBindingKind, Resolver, module_to_string}; diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 66492842581..87024c487df 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -7,9 +7,8 @@ use rustc_expand::expand::AstFragment; use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind}; use rustc_hir::def_id::LocalDefId; -use rustc_span::Span; use rustc_span::hygiene::LocalExpnId; -use rustc_span::symbol::{Symbol, kw, sym}; +use rustc_span::{Span, Symbol, kw, sym}; use tracing::debug; use crate::{ImplTraitContext, InvocationParent, Resolver}; @@ -356,6 +355,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { let kind = match self.impl_trait_context { ImplTraitContext::Universal => DefKind::TyParam, ImplTraitContext::Existential => DefKind::OpaqueTy, + ImplTraitContext::InBinding => return visit::walk_ty(self, ty), }; let id = self.create_def(*id, name, kind, ty.span); match self.impl_trait_context { @@ -365,6 +365,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { ImplTraitContext::Existential => { self.with_parent(id, |this| visit::walk_ty(this, ty)) } + ImplTraitContext::InBinding => unreachable!(), }; } _ => visit::walk_ty(self, ty), @@ -374,6 +375,13 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { fn visit_stmt(&mut self, stmt: &'a Stmt) { match stmt.kind { StmtKind::MacCall(..) => self.visit_macro_invoc(stmt.id), + // FIXME(impl_trait_in_bindings): We don't really have a good way of + // introducing the right `ImplTraitContext` here for all the cases we + // care about, in case we want to introduce ITIB to other positions + // such as turbofishes (e.g. `foo::<impl Fn()>(|| {})`). + StmtKind::Let(ref local) => self.with_impl_trait(ImplTraitContext::InBinding, |this| { + visit::walk_local(this, local) + }), _ => visit::walk_stmt(self, stmt), } } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 368eb3c26c7..9795299ed6d 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -28,8 +28,7 @@ use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; use rustc_span::hygiene::MacroKind; use rustc_span::source_map::SourceMap; -use rustc_span::symbol::{Ident, Symbol, kw, sym}; -use rustc_span::{BytePos, Span, SyntaxContext}; +use rustc_span::{BytePos, Ident, Span, Symbol, SyntaxContext, kw, sym}; use thin_vec::{ThinVec, thin_vec}; use tracing::debug; @@ -2817,11 +2816,11 @@ fn show_candidates( path_strings.sort_by(|a, b| a.0.cmp(&b.0)); path_strings.dedup_by(|a, b| a.0 == b.0); let core_path_strings = - path_strings.extract_if(|p| p.0.starts_with("core::")).collect::<Vec<_>>(); + path_strings.extract_if(.., |p| p.0.starts_with("core::")).collect::<Vec<_>>(); let std_path_strings = - path_strings.extract_if(|p| p.0.starts_with("std::")).collect::<Vec<_>>(); + path_strings.extract_if(.., |p| p.0.starts_with("std::")).collect::<Vec<_>>(); let foreign_crate_path_strings = - path_strings.extract_if(|p| !p.0.starts_with("crate::")).collect::<Vec<_>>(); + path_strings.extract_if(.., |p| !p.0.starts_with("crate::")).collect::<Vec<_>>(); // We list the `crate` local paths first. // Then we list the `std`/`core` paths. diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 24f5a812a82..3bfe98f7091 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -1,8 +1,7 @@ use rustc_errors::codes::*; use rustc_errors::{Applicability, ElidedLifetimeInPathSubdiag, MultiSpan}; use rustc_macros::{Diagnostic, Subdiagnostic}; -use rustc_span::Span; -use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::{Ident, Span, Symbol}; use crate::Res; use crate::late::PatternSource; diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 5906a682f3e..45e87edc53e 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -9,8 +9,7 @@ use rustc_session::lint::builtin::PROC_MACRO_DERIVE_RESOLUTION_FALLBACK; use rustc_session::parse::feature_err; use rustc_span::def_id::LocalDefId; use rustc_span::hygiene::{ExpnId, ExpnKind, LocalExpnId, MacroKind, SyntaxContext}; -use rustc_span::symbol::{Ident, kw}; -use rustc_span::{Span, sym}; +use rustc_span::{Ident, Span, kw, sym}; use tracing::{debug, instrument}; use crate::errors::{ParamKindInEnumDiscriminant, ParamKindInNonTrivialAnonConst}; diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 2ed3f4d2c4f..5b1d8d622bd 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -17,10 +17,9 @@ use rustc_session::lint::builtin::{ AMBIGUOUS_GLOB_REEXPORTS, HIDDEN_GLOB_REEXPORTS, PUB_USE_OF_PRIVATE_EXTERN_CRATE, REDUNDANT_IMPORTS, UNUSED_IMPORTS, }; -use rustc_span::Span; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::hygiene::LocalExpnId; -use rustc_span::symbol::{Ident, Symbol, kw}; +use rustc_span::{Ident, Span, Symbol, kw}; use smallvec::SmallVec; use tracing::debug; diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index bde11428d40..7324d3fe786 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -19,7 +19,9 @@ use rustc_ast::visit::{ use rustc_ast::*; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_errors::codes::*; -use rustc_errors::{Applicability, DiagArgValue, IntoDiagArg, StashKey, Suggestions}; +use rustc_errors::{ + Applicability, DiagArgValue, ErrorGuaranteed, IntoDiagArg, StashKey, Suggestions, +}; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, NonMacroAttrKind, PartialRes, PerNS}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LOCAL_CRATE, LocalDefId}; @@ -31,8 +33,7 @@ use rustc_session::config::{CrateType, ResolveDocLinks}; use rustc_session::lint::{self, BuiltinLintDiag}; use rustc_session::parse::feature_err; use rustc_span::source_map::{Spanned, respan}; -use rustc_span::symbol::{Ident, Symbol, kw, sym}; -use rustc_span::{BytePos, Span, SyntaxContext}; +use rustc_span::{BytePos, Ident, Span, Symbol, SyntaxContext, kw, sym}; use smallvec::{SmallVec, smallvec}; use tracing::{debug, instrument, trace}; @@ -264,12 +265,17 @@ impl RibKind<'_> { #[derive(Debug)] pub(crate) struct Rib<'ra, R = Res> { pub bindings: IdentMap<R>, + pub patterns_with_skipped_bindings: FxHashMap<DefId, Vec<(Span, Result<(), ErrorGuaranteed>)>>, pub kind: RibKind<'ra>, } impl<'ra, R> Rib<'ra, R> { fn new(kind: RibKind<'ra>) -> Rib<'ra, R> { - Rib { bindings: Default::default(), kind } + Rib { + bindings: Default::default(), + patterns_with_skipped_bindings: Default::default(), + kind, + } } } @@ -3775,6 +3781,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { /// When a whole or-pattern has been dealt with, the thing happens. /// /// See the implementation and `fresh_binding` for more details. + #[tracing::instrument(skip(self, bindings), level = "debug")] fn resolve_pattern_inner( &mut self, pat: &Pat, @@ -3783,7 +3790,6 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { ) { // Visit all direct subpatterns of this pattern. pat.walk(&mut |pat| { - debug!("resolve_pattern pat={:?} node={:?}", pat, pat.kind); match pat.kind { PatKind::Ident(bmode, ident, ref sub) => { // First try to resolve the identifier as some existing entity, @@ -3809,8 +3815,9 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { PatKind::Path(ref qself, ref path) => { self.smart_resolve_path(pat.id, qself, path, PathSource::Pat); } - PatKind::Struct(ref qself, ref path, ..) => { + PatKind::Struct(ref qself, ref path, ref _fields, ref rest) => { self.smart_resolve_path(pat.id, qself, path, PathSource::Struct); + self.record_patterns_with_skipped_bindings(pat, rest); } PatKind::Or(ref ps) => { // Add a new set of bindings to the stack. `Or` here records that when a @@ -3843,6 +3850,30 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { }); } + fn record_patterns_with_skipped_bindings(&mut self, pat: &Pat, rest: &ast::PatFieldsRest) { + match rest { + ast::PatFieldsRest::Rest | ast::PatFieldsRest::Recovered(_) => { + // Record that the pattern doesn't introduce all the bindings it could. + if let Some(partial_res) = self.r.partial_res_map.get(&pat.id) + && let Some(res) = partial_res.full_res() + && let Some(def_id) = res.opt_def_id() + { + self.ribs[ValueNS] + .last_mut() + .unwrap() + .patterns_with_skipped_bindings + .entry(def_id) + .or_default() + .push((pat.span, match rest { + ast::PatFieldsRest::Recovered(guar) => Err(*guar), + _ => Ok(()), + })); + } + } + ast::PatFieldsRest::None => {} + } + } + fn fresh_binding( &mut self, ident: Ident, @@ -4294,7 +4325,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { #[inline] /// If we're actually rustdoc then avoid giving a name resolution error for `cfg()` items or - // an invalid `use foo::*;` was found, which can cause unbounded ammounts of "item not found" + // an invalid `use foo::*;` was found, which can cause unbounded amounts of "item not found" // errors. We silence them all. fn should_report_errs(&self) -> bool { !(self.r.tcx.sess.opts.actually_rustdoc && self.in_func_body) diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 09f3e848766..6ee02e9f47f 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -27,8 +27,7 @@ use rustc_session::{Session, lint}; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; use rustc_span::hygiene::MacroKind; -use rustc_span::symbol::{Ident, Symbol, kw, sym}; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use thin_vec::ThinVec; use tracing::debug; @@ -430,6 +429,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { let mut err = self.r.dcx().struct_span_err(base_error.span, base_error.msg.clone()); err.code(code); + self.detect_missing_binding_available_from_pattern(&mut err, path, following_seg); self.suggest_at_operator_in_slice_pat_with_range(&mut err, path); self.suggest_swapping_misplaced_self_ty_and_trait(&mut err, source, res, base_error.span); @@ -628,7 +628,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { // Try to filter out intrinsics candidates, as long as we have // some other candidates to suggest. let intrinsic_candidates: Vec<_> = candidates - .extract_if(|sugg| { + .extract_if(.., |sugg| { let path = path_names_to_string(&sugg.path); path.starts_with("core::intrinsics::") || path.starts_with("std::intrinsics::") }) @@ -1120,6 +1120,48 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { true } + fn detect_missing_binding_available_from_pattern( + &mut self, + err: &mut Diag<'_>, + path: &[Segment], + following_seg: Option<&Segment>, + ) { + let [segment] = path else { return }; + let None = following_seg else { return }; + for rib in self.ribs[ValueNS].iter().rev() { + for (def_id, spans) in &rib.patterns_with_skipped_bindings { + if let Some(fields) = self.r.field_idents(*def_id) { + for field in fields { + if field.name == segment.ident.name { + if spans.iter().all(|(_, had_error)| had_error.is_err()) { + // This resolution error will likely be fixed by fixing a + // syntax error in a pattern, so it is irrelevant to the user. + let multispan: MultiSpan = + spans.iter().map(|(s, _)| *s).collect::<Vec<_>>().into(); + err.span_note( + multispan, + "this pattern had a recovered parse error which likely lost \ + the expected fields", + ); + err.downgrade_to_delayed_bug(); + } + let ty = self.r.tcx.item_name(*def_id); + for (span, _) in spans { + err.span_label( + *span, + format!( + "this pattern doesn't include `{field}`, which is \ + available in `{ty}`", + ), + ); + } + } + } + } + } + } + } + fn suggest_at_operator_in_slice_pat_with_range( &mut self, err: &mut Diag<'_>, diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 94adfcd3b91..7b950b97d30 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -70,8 +70,7 @@ use rustc_query_system::ich::StableHashingContext; use rustc_session::lint::builtin::PRIVATE_MACRO_USE; use rustc_session::lint::{BuiltinLintDiag, LintBuffer}; use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind, SyntaxContext, Transparency}; -use rustc_span::symbol::{Ident, Symbol, kw, sym}; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use smallvec::{SmallVec, smallvec}; use tracing::debug; @@ -190,6 +189,7 @@ impl InvocationParent { enum ImplTraitContext { Existential, Universal, + InBinding, } /// Used for tracking import use types which will be used for redundant import checking. diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 669a9c2428f..b24e343c58d 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -4,10 +4,11 @@ use std::cell::Cell; use std::mem; +use rustc_ast::attr::AttributeExt; use rustc_ast::expand::StrippedCfgItem; use rustc_ast::{self as ast, Crate, Inline, ItemKind, ModKind, NodeId, attr}; use rustc_ast_pretty::pprust; -use rustc_attr::StabilityLevel; +use rustc_attr_parsing::StabilityLevel; use rustc_data_structures::intern::Interned; use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, StashKey}; @@ -32,8 +33,7 @@ use rustc_session::parse::feature_err; use rustc_span::edit_distance::edit_distance; use rustc_span::edition::Edition; use rustc_span::hygiene::{self, AstPass, ExpnData, ExpnKind, LocalExpnId, MacroKind}; -use rustc_span::symbol::{Ident, Symbol, kw, sym}; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use crate::Namespace::*; use crate::errors::{ @@ -689,8 +689,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { && let [namespace, attribute, ..] = &*path.segments && namespace.ident.name == sym::diagnostic && !(attribute.ident.name == sym::on_unimplemented - || (attribute.ident.name == sym::do_not_recommend - && self.tcx.features().do_not_recommend())) + || attribute.ident.name == sym::do_not_recommend) { let distance = edit_distance(attribute.ident.name.as_str(), sym::on_unimplemented.as_str(), 5); @@ -1126,7 +1125,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { &mut self, macro_def: &ast::MacroDef, ident: Ident, - attrs: &[ast::Attribute], + attrs: &[impl AttributeExt], span: Span, node_id: NodeId, edition: Edition, diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs index 65128ceb866..84e43d0e016 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -5,12 +5,13 @@ use pulldown_cmark::{ BrokenLink, BrokenLinkCallback, CowStr, Event, LinkType, Options, Parser, Tag, }; use rustc_ast as ast; +use rustc_ast::attr::AttributeExt; use rustc_ast::util::comments::beautify_doc_string; use rustc_data_structures::fx::FxIndexMap; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::DefId; -use rustc_span::symbol::{Symbol, kw, sym}; -use rustc_span::{DUMMY_SP, InnerSpan, Span}; +use rustc_span::{DUMMY_SP, InnerSpan, Span, Symbol, kw, sym}; +use thin_vec::ThinVec; use tracing::{debug, trace}; #[derive(Clone, Copy, PartialEq, Eq, Debug)] @@ -192,19 +193,24 @@ pub fn add_doc_fragment(out: &mut String, frag: &DocFragment) { } } -pub fn attrs_to_doc_fragments<'a>( - attrs: impl Iterator<Item = (&'a ast::Attribute, Option<DefId>)>, +pub fn attrs_to_doc_fragments<'a, A: AttributeExt + Clone + 'a>( + attrs: impl Iterator<Item = (&'a A, Option<DefId>)>, doc_only: bool, -) -> (Vec<DocFragment>, ast::AttrVec) { +) -> (Vec<DocFragment>, ThinVec<A>) { let mut doc_fragments = Vec::new(); - let mut other_attrs = ast::AttrVec::new(); + let mut other_attrs = ThinVec::<A>::new(); for (attr, item_id) in attrs { if let Some((doc_str, comment_kind)) = attr.doc_str_and_comment_kind() { let doc = beautify_doc_string(doc_str, comment_kind); let (span, kind) = if attr.is_doc_comment() { - (attr.span, DocFragmentKind::SugaredDoc) + (attr.span(), DocFragmentKind::SugaredDoc) } else { - (span_for_value(attr), DocFragmentKind::RawDoc) + ( + attr.value_span() + .map(|i| i.with_ctxt(attr.span().ctxt())) + .unwrap_or(attr.span()), + DocFragmentKind::RawDoc, + ) }; let fragment = DocFragment { span, doc, kind, item_id, indent: 0 }; doc_fragments.push(fragment); @@ -218,16 +224,6 @@ pub fn attrs_to_doc_fragments<'a>( (doc_fragments, other_attrs) } -fn span_for_value(attr: &ast::Attribute) -> Span { - if let ast::AttrKind::Normal(normal) = &attr.kind - && let ast::AttrArgs::Eq { value, .. } = &normal.item.args - { - value.span().with_ctxt(attr.span.ctxt()) - } else { - attr.span - } -} - /// Return the doc-comments on this item, grouped by the module they came from. /// The module can be different if this is a re-export with added documentation. /// @@ -353,12 +349,15 @@ pub fn strip_generics_from_path(path_str: &str) -> Result<Box<str>, MalformedGen /// //// If there are no doc-comments, return true. /// FIXME(#78591): Support both inner and outer attributes on the same item. -pub fn inner_docs(attrs: &[ast::Attribute]) -> bool { - attrs.iter().find(|a| a.doc_str().is_some()).map_or(true, |a| a.style == ast::AttrStyle::Inner) +pub fn inner_docs(attrs: &[impl AttributeExt]) -> bool { + attrs + .iter() + .find(|a| a.doc_str().is_some()) + .map_or(true, |a| a.style() == ast::AttrStyle::Inner) } /// Has `#[rustc_doc_primitive]` or `#[doc(keyword)]`. -pub fn has_primitive_or_keyword_docs(attrs: &[ast::Attribute]) -> bool { +pub fn has_primitive_or_keyword_docs(attrs: &[impl AttributeExt]) -> bool { for attr in attrs { if attr.has_name(sym::rustc_doc_primitive) { return true; @@ -408,7 +407,7 @@ pub fn may_be_doc_link(link_type: LinkType) -> bool { /// Simplified version of `preprocessed_markdown_links` from rustdoc. /// Must return at least the same links as it, but may add some more links on top of that. -pub(crate) fn attrs_to_preprocessed_links(attrs: &[ast::Attribute]) -> Vec<Box<str>> { +pub(crate) fn attrs_to_preprocessed_links<A: AttributeExt + Clone>(attrs: &[A]) -> Vec<Box<str>> { let (doc_fragments, _) = attrs_to_doc_fragments(attrs.iter().map(|attr| (attr, None)), true); let doc = prepare_to_doc_link_resolution(&doc_fragments).into_values().next().unwrap(); diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 936c2ca87d6..047e920e688 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -147,18 +147,24 @@ pub enum InstrumentCoverage { Yes, } -/// Individual flag values controlled by `-Z coverage-options`. +/// Individual flag values controlled by `-Zcoverage-options`. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)] pub struct CoverageOptions { pub level: CoverageLevel, - /// `-Z coverage-options=no-mir-spans`: Don't extract block coverage spans + /// `-Zcoverage-options=no-mir-spans`: Don't extract block coverage spans /// from MIR statements/terminators, making it easier to inspect/debug /// branch and MC/DC coverage mappings. /// /// For internal debugging only. If other code changes would make it hard /// to keep supporting this flag, remove it. pub no_mir_spans: bool, + + /// `-Zcoverage-options=discard-all-spans-in-codegen`: During codgen, + /// discard all coverage spans as though they were invalid. Needed by + /// regression tests for #133606, because we don't have an easy way to + /// reproduce it from actual source code. + pub discard_all_spans_in_codegen: bool, } /// Controls whether branch coverage or MC/DC coverage is enabled. @@ -1076,7 +1082,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_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs index 43cf1324edd..3426858495b 100644 --- a/compiler/rustc_session/src/config/cfg.rs +++ b/compiler/rustc_session/src/config/cfg.rs @@ -28,7 +28,7 @@ use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_lint_defs::BuiltinLintDiag; use rustc_lint_defs::builtin::EXPLICIT_BUILTIN_CFGS_IN_FLAGS; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::{Symbol, sym}; use rustc_target::spec::{PanicStrategy, RelocModel, SanitizerSet, TARGETS, Target, TargetTuple}; use crate::Session; diff --git a/compiler/rustc_session/src/cstore.rs b/compiler/rustc_session/src/cstore.rs index 3e2cbc18933..c8a5c22ad12 100644 --- a/compiler/rustc_session/src/cstore.rs +++ b/compiler/rustc_session/src/cstore.rs @@ -13,8 +13,7 @@ use rustc_hir::def_id::{ }; use rustc_hir::definitions::{DefKey, DefPath, DefPathHash, Definitions}; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; -use rustc_span::Span; -use rustc_span::symbol::Symbol; +use rustc_span::{Span, Symbol}; use crate::search_paths::PathKind; use crate::utils::NativeLibKind; @@ -43,7 +42,7 @@ pub enum CrateDepKind { /// A dependency that is only used for its macros. MacrosOnly, /// A dependency that is always injected into the dependency list and so - /// doesn't need to be linked to an rlib, e.g., the injected allocator. + /// doesn't need to be linked to an rlib, e.g., the injected panic runtime. Implicit, /// A dependency that is required by an rlib version of this crate. /// Ordinary `extern crate`s result in `Explicit` dependencies. diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index 0b4470b2b0f..dcf86d1a408 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -29,9 +29,6 @@ pub mod output; pub use getopts; -mod version; -pub use version::RustcVersion; - rustc_fluent_macro::fluent_messages! { "../messages.ftl" } /// Requirements for a `StableHashingContext` to be used in this crate. diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 3873a203bd3..3772a4a08af 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1037,6 +1037,7 @@ pub mod parse { "condition" => slot.level = CoverageLevel::Condition, "mcdc" => slot.level = CoverageLevel::Mcdc, "no-mir-spans" => slot.no_mir_spans = true, + "discard-all-spans-in-codegen" => slot.discard_all_spans_in_codegen = true, _ => return false, } } diff --git a/compiler/rustc_session/src/output.rs b/compiler/rustc_session/src/output.rs index 2b2ba50d3fb..ff0419d06bf 100644 --- a/compiler/rustc_session/src/output.rs +++ b/compiler/rustc_session/src/output.rs @@ -3,9 +3,7 @@ use std::path::Path; use rustc_ast::{self as ast, attr}; -use rustc_errors::FatalError; -use rustc_span::symbol::sym; -use rustc_span::{Span, Symbol}; +use rustc_span::{Span, Symbol, sym}; use crate::Session; use crate::config::{self, CrateType, Input, OutFileName, OutputFilenames, OutputType}; @@ -91,11 +89,10 @@ pub fn find_crate_name(sess: &Session, attrs: &[ast::Attribute]) -> Symbol { } pub fn validate_crate_name(sess: &Session, s: Symbol, sp: Option<Span>) { - let mut err_count = 0; + let mut guar = None; { if s.is_empty() { - err_count += 1; - sess.dcx().emit_err(CrateNameEmpty { span: sp }); + guar = Some(sess.dcx().emit_err(CrateNameEmpty { span: sp })); } for c in s.as_str().chars() { if c.is_alphanumeric() { @@ -104,8 +101,7 @@ pub fn validate_crate_name(sess: &Session, s: Symbol, sp: Option<Span>) { if c == '_' { continue; } - err_count += 1; - sess.dcx().emit_err(InvalidCharacterInCrateName { + guar = Some(sess.dcx().emit_err(InvalidCharacterInCrateName { span: sp, character: c, crate_name: s, @@ -114,12 +110,12 @@ pub fn validate_crate_name(sess: &Session, s: Symbol, sp: Option<Span>) { } else { None }, - }); + })); } } - if err_count > 0 { - FatalError.raise(); + if let Some(guar) = guar { + guar.raise_fatal(); } } diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 21c11655110..90361efed84 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -79,7 +79,7 @@ impl SymbolGallery { // todo: this function now accepts `Session` instead of `ParseSess` and should be relocated /// Construct a diagnostic for a language feature error due to the given `span`. -/// The `feature`'s `Symbol` is the one you used in `unstable.rs` and `rustc_span::symbols`. +/// The `feature`'s `Symbol` is the one you used in `unstable.rs` and `rustc_span::symbol`. #[track_caller] pub fn feature_err( sess: &Session, diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 9f6106f9cfb..54bb4622963 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -352,6 +352,11 @@ impl Session { self.opts.unstable_opts.coverage_options.no_mir_spans } + /// True if `-Zcoverage-options=discard-all-spans-in-codegen` was passed. + pub fn coverage_discard_all_spans_in_codegen(&self) -> bool { + self.opts.unstable_opts.coverage_options.discard_all_spans_in_codegen + } + pub fn is_sanitizer_cfi_enabled(&self) -> bool { self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI) } diff --git a/compiler/rustc_smir/Cargo.toml b/compiler/rustc_smir/Cargo.toml index 1230667ee91..29ce24e8b78 100644 --- a/compiler/rustc_smir/Cargo.toml +++ b/compiler/rustc_smir/Cargo.toml @@ -7,9 +7,9 @@ edition = "2021" # tidy-alphabetical-start rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } -rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_hir = { path = "../rustc_hir" } +rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_middle = { path = "../rustc_middle" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index 64d241067a8..dc63ea1999e 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -318,7 +318,7 @@ macro_rules! run_driver { ($args:expr, $callback:expr $(, $with_tcx:ident)?) => {{ use rustc_driver::{Callbacks, Compilation, RunCompiler}; use rustc_middle::ty::TyCtxt; - use rustc_interface::{interface, Queries}; + use rustc_interface::interface; use stable_mir::CompilerError; use std::ops::ControlFlow; diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index 9025b47c422..9793a4d4162 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -251,11 +251,10 @@ impl<'tcx> Context for TablesWrapper<'tcx> { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let did = tables[def_id]; - let attr_name: Vec<_> = - attr.iter().map(|seg| rustc_span::symbol::Symbol::intern(&seg)).collect(); + let attr_name: Vec<_> = attr.iter().map(|seg| rustc_span::Symbol::intern(&seg)).collect(); tcx.get_attrs_by_path(did, &attr_name) .map(|attribute| { - let attr_str = rustc_ast_pretty::pprust::attribute_to_string(attribute); + let attr_str = rustc_hir_pretty::attribute_to_string(&tcx, attribute); let span = attribute.span; stable_mir::crate_def::Attribute::new(attr_str, span.stable(&mut *tables)) }) @@ -266,17 +265,16 @@ impl<'tcx> Context for TablesWrapper<'tcx> { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let did = tables[def_id]; - let filter_fn = move |a: &&rustc_ast::ast::Attribute| { - matches!(a.kind, rustc_ast::ast::AttrKind::Normal(_)) - }; + let filter_fn = + move |a: &&rustc_hir::Attribute| matches!(a.kind, rustc_hir::AttrKind::Normal(_)); let attrs_iter = if let Some(did) = did.as_local() { tcx.hir().attrs(tcx.local_def_id_to_hir_id(did)).iter().filter(filter_fn) } else { - tcx.item_attrs(did).iter().filter(filter_fn) + tcx.attrs_for_def(did).iter().filter(filter_fn) }; attrs_iter .map(|attribute| { - let attr_str = rustc_ast_pretty::pprust::attribute_to_string(attribute); + let attr_str = rustc_hir_pretty::attribute_to_string(&tcx, attribute); let span = attribute.span; stable_mir::crate_def::Attribute::new(attr_str, span.stable(&mut *tables)) }) diff --git a/compiler/rustc_smir/src/rustc_smir/convert/abi.rs b/compiler/rustc_smir/src/rustc_smir/convert/abi.rs index af24fd23f50..b39a15a8633 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/abi.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/abi.rs @@ -167,6 +167,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::Variants<rustc_abi::FieldIdx, rustc_abi:: rustc_abi::Variants::Single { index } => { VariantsShape::Single { index: index.stable(tables) } } + rustc_abi::Variants::Empty => VariantsShape::Empty, rustc_abi::Variants::Multiple { tag, tag_encoding, tag_field, variants } => { VariantsShape::Multiple { tag: tag.stable(tables), diff --git a/compiler/rustc_span/src/edit_distance.rs b/compiler/rustc_span/src/edit_distance.rs index 4f120db48f0..8a2baaa42e2 100644 --- a/compiler/rustc_span/src/edit_distance.rs +++ b/compiler/rustc_span/src/edit_distance.rs @@ -11,7 +11,7 @@ use std::{cmp, mem}; -use crate::symbol::Symbol; +use crate::Symbol; #[cfg(test)] mod tests; diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index bd8b93bb4d1..d5c2a337b4c 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -25,7 +25,6 @@ #![feature(hash_set_entry)] #![feature(if_let_guard)] #![feature(let_chains)] -#![feature(min_specialization)] #![feature(negative_impls)] #![feature(read_buf)] #![feature(round_char_boundary)] @@ -68,7 +67,7 @@ mod span_encoding; pub use span_encoding::{DUMMY_SP, Span}; pub mod symbol; -pub use symbol::{Symbol, sym}; +pub use symbol::{Ident, MacroRulesNormalizedIdent, Symbol, kw, sym}; mod analyze_source_file; pub mod fatal_error; diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index e3708896ba9..123e4b1f01f 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", @@ -306,6 +321,7 @@ symbols! { RwLockWriteGuard, Saturating, SeekFrom, + SelfTy, Send, SeqCst, Sized, @@ -1190,6 +1206,7 @@ symbols! { loongarch_target_feature, loop_break_value, lt, + m68k_target_feature, macro_at_most_once_rep, macro_attributes_in_derive_output, macro_escape, @@ -1700,6 +1717,7 @@ symbols! { rustc_diagnostic_macros, rustc_dirty, rustc_do_not_const_check, + rustc_do_not_implement_via_object, rustc_doc_primitive, rustc_driver, rustc_dummy, @@ -2476,13 +2494,6 @@ impl fmt::Display for Symbol { } } -// takes advantage of `str::to_string` specialization -impl ToString for Symbol { - fn to_string(&self) -> String { - self.as_str().to_string() - } -} - impl<CTX> HashStable<CTX> for Symbol { #[inline] fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { @@ -2594,6 +2605,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 } @@ -2611,8 +2627,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 { @@ -2650,6 +2666,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 { @@ -2688,41 +2709,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<Symbol> { - 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<Symbol> { + (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<Self::Item> { - 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/compiler/rustc_symbol_mangling/src/test.rs b/compiler/rustc_symbol_mangling/src/test.rs index 73bd8d7555b..7fda81126c4 100644 --- a/compiler/rustc_symbol_mangling/src/test.rs +++ b/compiler/rustc_symbol_mangling/src/test.rs @@ -7,7 +7,7 @@ use rustc_hir::def_id::LocalDefId; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{GenericArgs, Instance, TyCtxt}; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::{Symbol, sym}; use crate::errors::{Kind, TestOutput}; diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index def741e3546..a801b3e53a1 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -17,7 +17,7 @@ use rustc_middle::ty::{ self, EarlyBinder, FloatTy, GenericArg, GenericArgKind, Instance, IntTy, ReifyReason, Ty, TyCtxt, TypeVisitable, TypeVisitableExt, UintTy, }; -use rustc_span::symbol::kw; +use rustc_span::kw; pub(super) fn mangle<'tcx>( tcx: TyCtxt<'tcx>, @@ -439,7 +439,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { let sig = sig_tys.with(hdr); self.push("F"); self.in_binder(&sig, |cx, sig| { - if sig.safety == hir::Safety::Unsafe { + if sig.safety.is_unsafe() { cx.push("U"); } match sig.abi { diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index 204d5d53361..1292f46f0c9 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -35,7 +35,7 @@ macro_rules! def_reg_class { impl $arch_regclass { pub fn name(self) -> rustc_span::Symbol { match self { - $(Self::$class => rustc_span::symbol::sym::$class,)* + $(Self::$class => rustc_span::sym::$class,)* } } @@ -511,7 +511,7 @@ impl InlineAsmRegClass { Self::Msp430(r) => r.name(), Self::M68k(r) => r.name(), Self::CSKY(r) => r.name(), - Self::Err => rustc_span::symbol::sym::reg, + Self::Err => rustc_span::sym::reg, } } diff --git a/compiler/rustc_target/src/callconv/loongarch.rs b/compiler/rustc_target/src/callconv/loongarch.rs index d1234c3cc91..8bf61cb1337 100644 --- a/compiler/rustc_target/src/callconv/loongarch.rs +++ b/compiler/rustc_target/src/callconv/loongarch.rs @@ -116,7 +116,7 @@ where FieldsShape::Arbitrary { .. } => { match arg_layout.variants { abi::Variants::Multiple { .. } => return Err(CannotUseFpConv), - abi::Variants::Single { .. } => (), + abi::Variants::Single { .. } | abi::Variants::Empty => (), } for i in arg_layout.fields.index_by_increasing_offset() { let field = arg_layout.field(cx, i); diff --git a/compiler/rustc_target/src/callconv/riscv.rs b/compiler/rustc_target/src/callconv/riscv.rs index c0298edb5ab..4d858392c97 100644 --- a/compiler/rustc_target/src/callconv/riscv.rs +++ b/compiler/rustc_target/src/callconv/riscv.rs @@ -122,7 +122,7 @@ where FieldsShape::Arbitrary { .. } => { match arg_layout.variants { abi::Variants::Multiple { .. } => return Err(CannotUseFpConv), - abi::Variants::Single { .. } => (), + abi::Variants::Single { .. } | abi::Variants::Empty => (), } for i in arg_layout.fields.index_by_increasing_offset() { let field = arg_layout.field(cx, i); diff --git a/compiler/rustc_target/src/callconv/x86_64.rs b/compiler/rustc_target/src/callconv/x86_64.rs index bd101b23ea1..37aecf323a1 100644 --- a/compiler/rustc_target/src/callconv/x86_64.rs +++ b/compiler/rustc_target/src/callconv/x86_64.rs @@ -65,7 +65,7 @@ where } match &layout.variants { - abi::Variants::Single { .. } => {} + abi::Variants::Single { .. } | abi::Variants::Empty => {} abi::Variants::Multiple { variants, .. } => { // Treat enum variants like union members. for variant_idx in variants.indices() { diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 7d308c6c662..ad746d3f26b 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -46,7 +46,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_fs_util::try_canonicalize; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; -use rustc_span::symbol::{Symbol, kw, sym}; +use rustc_span::{Symbol, kw, sym}; use serde_json::Value; use tracing::debug; @@ -2213,6 +2213,10 @@ pub struct TargetOptions { /// `-Ctarget-cpu` but can be overwritten with `-Ctarget-features`. /// Corresponds to `llc -mattr=$features`. /// Note that these are LLVM feature names, not Rust feature names! + /// + /// Generally it is a bad idea to use negative target features because they often interact very + /// poorly with how `-Ctarget-cpu` works. Instead, try to use a lower "base CPU" and enable the + /// features you want to use. pub features: StaticCow<str>, /// Direct or use GOT indirect to reference external data symbols pub direct_access_external_data: Option<bool>, @@ -2605,15 +2609,11 @@ impl TargetOptions { } pub(crate) fn has_feature(&self, search_feature: &str) -> bool { - self.features.split(',').any(|f| { - if let Some(f) = f.strip_prefix('+') - && f == search_feature - { - true - } else { - false - } - }) + self.features.split(',').any(|f| f.strip_prefix('+').is_some_and(|f| f == search_feature)) + } + + pub(crate) fn has_neg_feature(&self, search_feature: &str) -> bool { + self.features.split(',').any(|f| f.strip_prefix('-').is_some_and(|f| f == search_feature)) } } @@ -3166,7 +3166,7 @@ impl Target { // Note that the `lp64e` is still unstable as it's not (yet) part of the ELF psABI. check_matches!( &*self.llvm_abiname, - "lp64" | "lp64f" | "lp64d" | "lp64q" | "lp64e", + "lp64" | "lp64f" | "lp64d" | "lp64e", "invalid RISC-V ABI name" ); } diff --git a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs index 952a00c52a2..f763c37f535 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs @@ -13,8 +13,8 @@ pub(crate) fn target() -> Target { llvm_target: "powerpc64le-unknown-linux-musl".into(), metadata: crate::spec::TargetMetadata { description: Some("64-bit PowerPC Linux with musl 1.2.3, Little Endian".into()), - tier: Some(3), - host_tools: Some(false), + tier: Some(2), + host_tools: Some(true), std: Some(true), }, pointer_width: 64, diff --git a/compiler/rustc_target/src/spec/targets/sparcv9_sun_solaris.rs b/compiler/rustc_target/src/spec/targets/sparcv9_sun_solaris.rs index fdc9628f78e..770da60da9e 100644 --- a/compiler/rustc_target/src/spec/targets/sparcv9_sun_solaris.rs +++ b/compiler/rustc_target/src/spec/targets/sparcv9_sun_solaris.rs @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "sparcv9-sun-solaris".into(), metadata: crate::spec::TargetMetadata { - description: Some("SPARC Solaris 11, illumos".into()), + description: Some("SPARC Solaris 11.4".into()), tier: Some(2), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_solaris.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_solaris.rs index 28dd3c426c7..843568a4792 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_pc_solaris.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_solaris.rs @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "x86_64-pc-solaris".into(), metadata: crate::spec::TargetMetadata { - description: Some("64-bit Solaris 11, illumos".into()), + description: Some("64-bit Solaris 11.4".into()), tier: Some(2), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 29d3f826a15..5372437b0d2 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -3,7 +3,7 @@ //! and Rust adds some features that do not correspond to LLVM features at all. use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::{Symbol, sym}; use crate::spec::Target; @@ -20,7 +20,7 @@ pub const RUSTC_SPECIAL_FEATURES: &[&str] = &["backchain"]; /// `Toggleability` is the type storing whether (un)stable features can be toggled: /// this is initially a function since it can depend on `Target`, but for stable hashing /// it needs to be something hashable to we have to make the type generic. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone)] pub enum Stability<Toggleability> { /// This target feature is stable, it can be used in `#[target_feature]` and /// `#[cfg(target_feature)]`. @@ -39,16 +39,26 @@ pub enum Stability<Toggleability> { allow_toggle: Toggleability, }, /// This feature can not be set via `-Ctarget-feature` or `#[target_feature]`, it can only be - /// set in the basic target definition. It is never set in `cfg(target_feature)`. Used in + /// set in the target spec. It is never set in `cfg(target_feature)`. Used in /// particular for features that change the floating-point ABI. Forbidden { reason: &'static str }, } -/// `Stability` where `allow_toggle` has not been computed yet. /// Returns `Ok` if the toggle is allowed, `Err` with an explanation of not. -pub type StabilityUncomputed = Stability<fn(&Target) -> Result<(), &'static str>>; +/// The `bool` indicates whether the feature is being enabled (`true`) or disabled. +pub type AllowToggleUncomputed = fn(&Target, bool) -> Result<(), &'static str>; + +/// The computed result of whether a feature can be enabled/disabled on the current target. +#[derive(Debug, Clone)] +pub struct AllowToggleComputed { + enable: Result<(), &'static str>, + disable: Result<(), &'static str>, +} + +/// `Stability` where `allow_toggle` has not been computed yet. +pub type StabilityUncomputed = Stability<AllowToggleUncomputed>; /// `Stability` where `allow_toggle` has already been computed. -pub type StabilityComputed = Stability<Result<(), &'static str>>; +pub type StabilityComputed = Stability<AllowToggleComputed>; impl<CTX, Toggleability: HashStable<CTX>> HashStable<CTX> for Stability<Toggleability> { #[inline] @@ -69,11 +79,20 @@ impl<CTX, Toggleability: HashStable<CTX>> HashStable<CTX> for Stability<Toggleab } } +impl<CTX> HashStable<CTX> for AllowToggleComputed { + #[inline] + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { + let AllowToggleComputed { enable, disable } = self; + enable.hash_stable(hcx, hasher); + disable.hash_stable(hcx, hasher); + } +} + impl<Toggleability> Stability<Toggleability> { /// Returns whether the feature can be used in `cfg(target_feature)` ever. /// (It might still be nightly-only even if this returns `true`, so make sure to also check /// `requires_nightly`.) - pub fn in_cfg(self) -> bool { + pub fn in_cfg(&self) -> bool { !matches!(self, Stability::Forbidden { .. }) } @@ -85,8 +104,8 @@ impl<Toggleability> Stability<Toggleability> { /// Before calling this, ensure the feature is even permitted for this use: /// - for `#[target_feature]`/`-Ctarget-feature`, check `allow_toggle()` /// - for `cfg(target_feature)`, check `in_cfg` - pub fn requires_nightly(self) -> Option<Symbol> { - match self { + pub fn requires_nightly(&self) -> Option<Symbol> { + match *self { Stability::Unstable { nightly_feature, .. } => Some(nightly_feature), Stability::Stable { .. } => None, Stability::Forbidden { .. } => panic!("forbidden features should not reach this far"), @@ -95,35 +114,49 @@ impl<Toggleability> Stability<Toggleability> { } impl StabilityUncomputed { - pub fn compute_toggleability(self, target: &Target) -> StabilityComputed { + pub fn compute_toggleability(&self, target: &Target) -> StabilityComputed { use Stability::*; - match self { - Stable { allow_toggle } => Stable { allow_toggle: allow_toggle(target) }, + let compute = |f: AllowToggleUncomputed| AllowToggleComputed { + enable: f(target, true), + disable: f(target, false), + }; + match *self { + Stable { allow_toggle } => Stable { allow_toggle: compute(allow_toggle) }, Unstable { nightly_feature, allow_toggle } => { - Unstable { nightly_feature, allow_toggle: allow_toggle(target) } + Unstable { nightly_feature, allow_toggle: compute(allow_toggle) } } Forbidden { reason } => Forbidden { reason }, } } + + pub fn toggle_allowed(&self, target: &Target, enable: bool) -> Result<(), &'static str> { + use Stability::*; + match *self { + Stable { allow_toggle } => allow_toggle(target, enable), + Unstable { allow_toggle, .. } => allow_toggle(target, enable), + Forbidden { reason } => Err(reason), + } + } } impl StabilityComputed { /// Returns whether the feature may be toggled via `#[target_feature]` or `-Ctarget-feature`. /// (It might still be nightly-only even if this returns `true`, so make sure to also check /// `requires_nightly`.) - pub fn allow_toggle(self) -> Result<(), &'static str> { - match self { + pub fn toggle_allowed(&self, enable: bool) -> Result<(), &'static str> { + let allow_toggle = match self { Stability::Stable { allow_toggle } => allow_toggle, Stability::Unstable { allow_toggle, .. } => allow_toggle, - Stability::Forbidden { reason } => Err(reason), - } + Stability::Forbidden { reason } => return Err(reason), + }; + if enable { allow_toggle.enable } else { allow_toggle.disable } } } // Constructors for the list below, defaulting to "always allow toggle". -const STABLE: StabilityUncomputed = Stability::Stable { allow_toggle: |_target| Ok(()) }; +const STABLE: StabilityUncomputed = Stability::Stable { allow_toggle: |_target, _enable| Ok(()) }; const fn unstable(nightly_feature: Symbol) -> StabilityUncomputed { - Stability::Unstable { nightly_feature, allow_toggle: |_target| Ok(()) } + Stability::Unstable { nightly_feature, allow_toggle: |_target, _enable| Ok(()) } } // Here we list target features that rustc "understands": they can be used in `#[target_feature]` @@ -184,7 +217,7 @@ const ARM_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ "fpregs", Stability::Unstable { nightly_feature: sym::arm_target_feature, - allow_toggle: |target: &Target| { + allow_toggle: |target: &Target, _enable| { // Only allow toggling this if the target has `soft-float` set. With `soft-float`, // `fpregs` isn't needed so changing it cannot affect the ABI. if target.has_feature("soft-float") { @@ -257,6 +290,7 @@ const AARCH64_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ ("flagm", STABLE, &[]), // FEAT_FLAGM2 ("flagm2", unstable(sym::aarch64_unstable_target_feature), &[]), + ("fp-armv8", Stability::Forbidden { reason: "Rust ties `fp-armv8` to `neon`" }, &[]), // FEAT_FP16 // Rust ties FP and Neon: https://github.com/rust-lang/rust/pull/91608 ("fp16", STABLE, &["neon"]), @@ -292,7 +326,28 @@ const AARCH64_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ // FEAT_MTE & FEAT_MTE2 ("mte", STABLE, &[]), // FEAT_AdvSimd & FEAT_FP - ("neon", STABLE, &[]), + ( + "neon", + Stability::Stable { + allow_toggle: |target, enable| { + if target.abi == "softfloat" { + // `neon` has no ABI implications for softfloat targets, we can allow this. + Ok(()) + } else if enable + && !target.has_neg_feature("fp-armv8") + && !target.has_neg_feature("neon") + { + // neon is enabled by default, and has not been disabled, so enabling it again + // is redundant and we can permit it. Forbidding this would be a breaking change + // since this feature is stable. + Ok(()) + } else { + Err("unsound on hard-float targets because it changes float ABI") + } + }, + }, + &[], + ), // FEAT_PAUTH (address authentication) ("paca", STABLE, &[]), // FEAT_PAUTH (generic authentication) @@ -481,7 +536,7 @@ const X86_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ "x87", Stability::Unstable { nightly_feature: sym::x87_target_feature, - allow_toggle: |target: &Target| { + allow_toggle: |target: &Target, _enable| { // Only allow toggling this if the target has `soft-float` set. With `soft-float`, // `fpregs` isn't needed so changing it cannot affect the ABI. if target.has_feature("soft-float") { @@ -535,9 +590,76 @@ const RISCV_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ // tidy-alphabetical-start ("a", STABLE, &["zaamo", "zalrsc"]), ("c", STABLE, &[]), - ("d", unstable(sym::riscv_target_feature), &["f"]), - ("e", unstable(sym::riscv_target_feature), &[]), - ("f", unstable(sym::riscv_target_feature), &[]), + ( + "d", + Stability::Unstable { + nightly_feature: sym::riscv_target_feature, + allow_toggle: |target, enable| match &*target.llvm_abiname { + "ilp32d" | "lp64d" if !enable => { + // The ABI requires the `d` feature, so it cannot be disabled. + Err("feature is required by ABI") + } + "ilp32e" if enable => { + // ilp32e is incompatible with features that need aligned load/stores > 32 bits, + // like `d`. + Err("feature is incompatible with ABI") + } + _ => Ok(()), + }, + }, + &["f"], + ), + ( + "e", + Stability::Unstable { + // Given that this is a negative feature, consider this before stabilizing: + // does it really make sense to enable this feature in an individual + // function with `#[target_feature]`? + nightly_feature: sym::riscv_target_feature, + allow_toggle: |target, enable| { + match &*target.llvm_abiname { + _ if !enable => { + // Disabling this feature means we can use more registers (x16-x31). + // The "e" ABIs treat them as caller-save, so it is safe to use them only + // in some parts of a program while the rest doesn't know they even exist. + // On other ABIs, the feature is already disabled anyway. + Ok(()) + } + "ilp32e" | "lp64e" => { + // Embedded ABIs should already have the feature anyway, it's fine to enable + // it again from an ABI perspective. + Ok(()) + } + _ => { + // *Not* an embedded ABI. Enabling `e` is invalid. + Err("feature is incompatible with ABI") + } + } + }, + }, + &[], + ), + ( + "f", + Stability::Unstable { + nightly_feature: sym::riscv_target_feature, + allow_toggle: |target, enable| { + match &*target.llvm_abiname { + "ilp32f" | "ilp32d" | "lp64f" | "lp64d" if !enable => { + // The ABI requires the `f` feature, so it cannot be disabled. + Err("feature is required by ABI") + } + _ => Ok(()), + } + }, + }, + &[], + ), + ( + "forced-atomics", + Stability::Forbidden { reason: "unsound because it changes the ABI of atomic operations" }, + &[], + ), ("m", STABLE, &[]), ("relax", unstable(sym::riscv_target_feature), &[]), ("unaligned-scalar-mem", unstable(sym::riscv_target_feature), &[]), @@ -668,6 +790,20 @@ const SPARC_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ // tidy-alphabetical-end ]; +const M68K_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + ("isa-68000", unstable(sym::m68k_target_feature), &[]), + ("isa-68010", unstable(sym::m68k_target_feature), &["isa-68000"]), + ("isa-68020", unstable(sym::m68k_target_feature), &["isa-68010"]), + ("isa-68030", unstable(sym::m68k_target_feature), &["isa-68020"]), + ("isa-68040", unstable(sym::m68k_target_feature), &["isa-68030", "isa-68882"]), + ("isa-68060", unstable(sym::m68k_target_feature), &["isa-68040"]), + // FPU + ("isa-68881", unstable(sym::m68k_target_feature), &[]), + ("isa-68882", unstable(sym::m68k_target_feature), &["isa-68881"]), + // tidy-alphabetical-end +]; + /// When rustdoc is running, provide a list of all known features so that all their respective /// primitives may be documented. /// @@ -687,6 +823,7 @@ pub fn all_rust_features() -> impl Iterator<Item = (&'static str, StabilityUncom .chain(LOONGARCH_FEATURES) .chain(IBMZ_FEATURES) .chain(SPARC_FEATURES) + .chain(M68K_FEATURES) .cloned() .map(|(f, s, _)| (f, s)) } @@ -734,6 +871,7 @@ impl Target { "loongarch64" => LOONGARCH_FEATURES, "s390x" => IBMZ_FEATURES, "sparc" | "sparc64" => SPARC_FEATURES, + "m68k" => M68K_FEATURES, _ => &[], } } @@ -751,7 +889,7 @@ impl Target { "sparc" | "sparc64" => SPARC_FEATURES_FOR_CORRECT_VECTOR_ABI, "hexagon" => HEXAGON_FEATURES_FOR_CORRECT_VECTOR_ABI, "mips" | "mips32r6" | "mips64" | "mips64r6" => MIPS_FEATURES_FOR_CORRECT_VECTOR_ABI, - "bpf" => &[], // no vector ABI + "bpf" | "m68k" => &[], // no vector ABI "csky" => CSKY_FEATURES_FOR_CORRECT_VECTOR_ABI, // FIXME: for some tier3 targets, we are overly cautious and always give warnings // when passing args in vector registers. diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml index e29ed9a4b56..b13a753c4ed 100644 --- a/compiler/rustc_trait_selection/Cargo.toml +++ b/compiler/rustc_trait_selection/Cargo.toml @@ -9,7 +9,7 @@ itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_ir = { path = "../rustc_ast_ir" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index f856a8d7abb..ee5ce19cb4d 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -63,10 +63,11 @@ use rustc_hir::{self as hir}; use rustc_macros::extension; use rustc_middle::bug; use rustc_middle::dep_graph::DepContext; +use rustc_middle::traits::PatternOriginExpr; use rustc_middle::ty::error::{ExpectedFound, TypeError, TypeErrorToStringExt}; use rustc_middle::ty::print::{PrintError, PrintTraitRefExt as _, with_forced_trimmed_paths}; use rustc_middle::ty::{ - self, List, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, + self, List, ParamEnv, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, }; use rustc_span::def_id::LOCAL_CRATE; @@ -77,7 +78,7 @@ use crate::error_reporting::TypeErrCtxt; use crate::errors::{ObligationCauseFailureCode, TypeErrorAdditionalDiags}; use crate::infer; use crate::infer::relate::{self, RelateResult, TypeRelation}; -use crate::infer::{InferCtxt, TypeTrace, ValuePairs}; +use crate::infer::{InferCtxt, InferCtxtExt as _, TypeTrace, ValuePairs}; use crate::solve::deeply_normalize_for_diagnostics; use crate::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, @@ -433,15 +434,22 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { cause: &ObligationCause<'tcx>, exp_found: Option<ty::error::ExpectedFound<Ty<'tcx>>>, terr: TypeError<'tcx>, + param_env: Option<ParamEnv<'tcx>>, ) { match *cause.code() { - ObligationCauseCode::Pattern { origin_expr: true, span: Some(span), root_ty } => { - let ty = self.resolve_vars_if_possible(root_ty); - if !matches!(ty.kind(), ty::Infer(ty::InferTy::TyVar(_) | ty::InferTy::FreshTy(_))) - { + ObligationCauseCode::Pattern { + origin_expr: Some(origin_expr), + span: Some(span), + root_ty, + } => { + let expected_ty = self.resolve_vars_if_possible(root_ty); + if !matches!( + expected_ty.kind(), + ty::Infer(ty::InferTy::TyVar(_) | ty::InferTy::FreshTy(_)) + ) { // don't show type `_` if span.desugaring_kind() == Some(DesugaringKind::ForLoop) - && let ty::Adt(def, args) = ty.kind() + && let ty::Adt(def, args) = expected_ty.kind() && Some(def.did()) == self.tcx.get_diagnostic_item(sym::Option) { err.span_label( @@ -449,22 +457,48 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { format!("this is an iterator with items of type `{}`", args.type_at(0)), ); } else { - err.span_label(span, format!("this expression has type `{ty}`")); + err.span_label(span, format!("this expression has type `{expected_ty}`")); } } if let Some(ty::error::ExpectedFound { found, .. }) = exp_found - && ty.boxed_ty() == Some(found) - && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) + && let Ok(mut peeled_snippet) = + self.tcx.sess.source_map().span_to_snippet(origin_expr.peeled_span) { - err.span_suggestion( - span, - "consider dereferencing the boxed value", - format!("*{snippet}"), - Applicability::MachineApplicable, - ); + // Parentheses are needed for cases like as casts. + // We use the peeled_span for deref suggestions. + // It's also safe to use for box, since box only triggers if there + // wasn't a reference to begin with. + if origin_expr.peeled_prefix_suggestion_parentheses { + peeled_snippet = format!("({peeled_snippet})"); + } + + // Try giving a box suggestion first, as it is a special case of the + // deref suggestion. + if expected_ty.boxed_ty() == Some(found) { + err.span_suggestion_verbose( + span, + "consider dereferencing the boxed value", + format!("*{peeled_snippet}"), + Applicability::MachineApplicable, + ); + } else if let Some(param_env) = param_env + && let Some(prefix) = self.should_deref_suggestion_on_mismatch( + param_env, + found, + expected_ty, + origin_expr, + ) + { + err.span_suggestion_verbose( + span, + "consider dereferencing to access the inner value using the Deref trait", + format!("{prefix}{peeled_snippet}"), + Applicability::MaybeIncorrect, + ); + } } } - ObligationCauseCode::Pattern { origin_expr: false, span: Some(span), .. } => { + ObligationCauseCode::Pattern { origin_expr: None, span: Some(span), .. } => { err.span_label(span, "expected due to this"); } ObligationCauseCode::BlockTailExpression( @@ -618,6 +652,45 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } + /// Determines whether deref_to == <deref_from as Deref>::Target, and if so, + /// returns a prefix that should be added to deref_from as a suggestion. + fn should_deref_suggestion_on_mismatch( + &self, + param_env: ParamEnv<'tcx>, + deref_to: Ty<'tcx>, + deref_from: Ty<'tcx>, + origin_expr: PatternOriginExpr, + ) -> Option<String> { + // origin_expr contains stripped away versions of our expression. + // We'll want to use that to avoid suggesting things like *&x. + // However, the type that we have access to hasn't been stripped away, + // so we need to ignore the first n dereferences, where n is the number + // that's been stripped away in origin_expr. + + // Find a way to autoderef from deref_from to deref_to. + let Some((num_derefs, (after_deref_ty, _))) = (self.autoderef_steps)(deref_from) + .into_iter() + .enumerate() + .find(|(_, (ty, _))| self.infcx.can_eq(param_env, *ty, deref_to)) + else { + return None; + }; + + if num_derefs <= origin_expr.peeled_count { + return None; + } + + let deref_part = "*".repeat(num_derefs - origin_expr.peeled_count); + + // If the user used a reference in the original expression, they probably + // want the suggestion to still give a reference. + if deref_from.is_ref() && !after_deref_ty.is_ref() { + Some(format!("&{deref_part}")) + } else { + Some(deref_part) + } + } + /// Given that `other_ty` is the same as a type argument for `name` in `sub`, populate `value` /// highlighting `name` and every type argument that isn't at `pos` (which is `other_ty`), and /// populate `other_value` with `other_ty`. @@ -751,7 +824,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn cmp_fn_sig( &self, sig1: &ty::PolyFnSig<'tcx>, + fn_def1: Option<(DefId, &'tcx [ty::GenericArg<'tcx>])>, sig2: &ty::PolyFnSig<'tcx>, + fn_def2: Option<(DefId, &'tcx [ty::GenericArg<'tcx>])>, ) -> (DiagStyledString, DiagStyledString) { let sig1 = &(self.normalize_fn_sig)(*sig1); let sig2 = &(self.normalize_fn_sig)(*sig2); @@ -857,6 +932,25 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { (values.1).0.extend(x2.0); } + let fmt = |(did, args)| format!(" {{{}}}", self.tcx.def_path_str_with_args(did, args)); + + match (fn_def1, fn_def2) { + (None, None) => {} + (Some(fn_def1), Some(fn_def2)) => { + let path1 = fmt(fn_def1); + let path2 = fmt(fn_def2); + let same_path = path1 == path2; + values.0.push(path1, !same_path); + values.1.push(path2, !same_path); + } + (Some(fn_def1), None) => { + values.0.push_highlighted(fmt(fn_def1)); + } + (None, Some(fn_def2)) => { + values.1.push_highlighted(fmt(fn_def2)); + } + } + values } @@ -1245,36 +1339,21 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { (ty::FnDef(did1, args1), ty::FnDef(did2, args2)) => { let sig1 = self.tcx.fn_sig(*did1).instantiate(self.tcx, args1); let sig2 = self.tcx.fn_sig(*did2).instantiate(self.tcx, args2); - let mut values = self.cmp_fn_sig(&sig1, &sig2); - let path1 = format!(" {{{}}}", self.tcx.def_path_str_with_args(*did1, args1)); - let path2 = format!(" {{{}}}", self.tcx.def_path_str_with_args(*did2, args2)); - let same_path = path1 == path2; - values.0.push(path1, !same_path); - values.1.push(path2, !same_path); - values + self.cmp_fn_sig(&sig1, Some((*did1, args1)), &sig2, Some((*did2, args2))) } (ty::FnDef(did1, args1), ty::FnPtr(sig_tys2, hdr2)) => { let sig1 = self.tcx.fn_sig(*did1).instantiate(self.tcx, args1); - let mut values = self.cmp_fn_sig(&sig1, &sig_tys2.with(*hdr2)); - values.0.push_highlighted(format!( - " {{{}}}", - self.tcx.def_path_str_with_args(*did1, args1) - )); - values + self.cmp_fn_sig(&sig1, Some((*did1, args1)), &sig_tys2.with(*hdr2), None) } (ty::FnPtr(sig_tys1, hdr1), ty::FnDef(did2, args2)) => { let sig2 = self.tcx.fn_sig(*did2).instantiate(self.tcx, args2); - let mut values = self.cmp_fn_sig(&sig_tys1.with(*hdr1), &sig2); - values - .1 - .push_normal(format!(" {{{}}}", self.tcx.def_path_str_with_args(*did2, args2))); - values + self.cmp_fn_sig(&sig_tys1.with(*hdr1), None, &sig2, Some((*did2, args2))) } (ty::FnPtr(sig_tys1, hdr1), ty::FnPtr(sig_tys2, hdr2)) => { - self.cmp_fn_sig(&sig_tys1.with(*hdr1), &sig_tys2.with(*hdr2)) + self.cmp_fn_sig(&sig_tys1.with(*hdr1), None, &sig_tys2.with(*hdr2), None) } _ => { @@ -1406,8 +1485,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { Variable(ty::error::ExpectedFound<Ty<'a>>), Fixed(&'static str), } - let (expected_found, exp_found, is_simple_error, values) = match values { - None => (None, Mismatch::Fixed("type"), false, None), + let (expected_found, exp_found, is_simple_error, values, param_env) = match values { + None => (None, Mismatch::Fixed("type"), false, None, None), Some(ty::ParamEnvAnd { param_env, value: values }) => { let mut values = self.resolve_vars_if_possible(values); if self.next_trait_solver() { @@ -1459,7 +1538,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { diag.downgrade_to_delayed_bug(); return; }; - (Some(vals), exp_found, is_simple_error, Some(values)) + (Some(vals), exp_found, is_simple_error, Some(values), Some(param_env)) } }; @@ -1791,7 +1870,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // It reads better to have the error origin as the final // thing. - self.note_error_origin(diag, cause, exp_found, terr); + self.note_error_origin(diag, cause, exp_found, terr, param_env); debug!(?diag); } @@ -2029,7 +2108,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if exp_found.references_error() { return None; } - let (exp, fnd) = self.cmp_fn_sig(&exp_found.expected, &exp_found.found); + let (exp, fnd) = self.cmp_fn_sig(&exp_found.expected, None, &exp_found.found, None); Some((exp, fnd, None)) } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index af3b5e0d5d4..2dcf3760d2f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -17,10 +17,10 @@ use rustc_middle::ty::{ self, GenericArg, GenericArgKind, GenericArgsRef, InferConst, IsSuggestable, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeckResults, }; -use rustc_span::symbol::{Ident, sym}; -use rustc_span::{BytePos, DUMMY_SP, FileName, Span}; +use rustc_span::{BytePos, DUMMY_SP, FileName, Ident, Span, sym}; use tracing::{debug, instrument, warn}; +use super::nice_region_error::placeholder_error::Highlighted; use crate::error_reporting::TypeErrCtxt; use crate::errors::{ AmbiguousImpl, AmbiguousReturn, AnnotationRequired, InferenceBadError, @@ -279,8 +279,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { pub fn extract_inference_diagnostics_data( &self, arg: GenericArg<'tcx>, - highlight: Option<ty::print::RegionHighlightMode<'tcx>>, + highlight: ty::print::RegionHighlightMode<'tcx>, ) -> InferenceDiagnosticsData { + let tcx = self.tcx; match arg.unpack() { GenericArgKind::Type(ty) => { if let ty::Infer(ty::TyVar(ty_vid)) = *ty.kind() { @@ -300,13 +301,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS); - if let Some(highlight) = highlight { - printer.region_highlight_mode = highlight; - } - ty.print(&mut printer).unwrap(); InferenceDiagnosticsData { - name: printer.into_buffer(), + name: Highlighted { highlight, ns: Namespace::TypeNS, tcx, value: ty } + .to_string(), span: None, kind: UnderspecifiedArgKind::Type { prefix: ty.prefix_string(self.tcx) }, parent: None, @@ -325,13 +322,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } debug_assert!(!origin.span.is_dummy()); - let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::ValueNS); - if let Some(highlight) = highlight { - printer.region_highlight_mode = highlight; - } - ct.print(&mut printer).unwrap(); InferenceDiagnosticsData { - name: printer.into_buffer(), + name: Highlighted { highlight, ns: Namespace::ValueNS, tcx, value: ct } + .to_string(), span: Some(origin.span), kind: UnderspecifiedArgKind::Const { is_parameter: false }, parent: None, @@ -343,13 +336,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // FIXME: Ideally we should look into the generic constant // to figure out which inference var is actually unresolved so that // this path is unreachable. - let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::ValueNS); - if let Some(highlight) = highlight { - printer.region_highlight_mode = highlight; - } - ct.print(&mut printer).unwrap(); InferenceDiagnosticsData { - name: printer.into_buffer(), + name: Highlighted { highlight, ns: Namespace::ValueNS, tcx, value: ct } + .to_string(), span: None, kind: UnderspecifiedArgKind::Const { is_parameter: false }, parent: None, @@ -422,7 +411,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { should_label_span: bool, ) -> Diag<'a> { let arg = self.resolve_vars_if_possible(arg); - let arg_data = self.extract_inference_diagnostics_data(arg, None); + let arg_data = + self.extract_inference_diagnostics_data(arg, ty::print::RegionHighlightMode::default()); let Some(typeck_results) = &self.typeck_results else { // If we don't have any typeck results we're outside diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/different_lifetimes.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/different_lifetimes.rs index 7f847253439..9cb58a9d45b 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/different_lifetimes.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/different_lifetimes.rs @@ -63,26 +63,16 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } // Determine whether the sub and sup consist of both anonymous (elided) regions. - let anon_reg_sup = self.tcx().is_suitable_region(self.generic_param_scope, sup)?; + let sup_info = self.tcx().is_suitable_region(self.generic_param_scope, sup)?; - let anon_reg_sub = self.tcx().is_suitable_region(self.generic_param_scope, sub)?; - let scope_def_id_sup = anon_reg_sup.def_id; - let bregion_sup = anon_reg_sup.bound_region; - let scope_def_id_sub = anon_reg_sub.def_id; - let bregion_sub = anon_reg_sub.bound_region; + let sub_info = self.tcx().is_suitable_region(self.generic_param_scope, sub)?; - let ty_sup = find_anon_type(self.tcx(), self.generic_param_scope, sup, &bregion_sup)?; + let ty_sup = find_anon_type(self.tcx(), self.generic_param_scope, sup)?; - let ty_sub = find_anon_type(self.tcx(), self.generic_param_scope, sub, &bregion_sub)?; + let ty_sub = find_anon_type(self.tcx(), self.generic_param_scope, sub)?; - debug!( - "try_report_anon_anon_conflict: found_param1={:?} sup={:?} br1={:?}", - ty_sub, sup, bregion_sup - ); - debug!( - "try_report_anon_anon_conflict: found_param2={:?} sub={:?} br2={:?}", - ty_sup, sub, bregion_sub - ); + debug!("try_report_anon_anon_conflict: found_param1={:?} sup={:?}", ty_sub, sup); + debug!("try_report_anon_anon_conflict: found_param2={:?} sub={:?}", ty_sup, sub); let (ty_sup, ty_fndecl_sup) = ty_sup; let (ty_sub, ty_fndecl_sub) = ty_sub; @@ -93,9 +83,9 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { self.find_param_with_region(sub, sub)?; let sup_is_ret_type = - self.is_return_type_anon(scope_def_id_sup, bregion_sup, ty_fndecl_sup); + self.is_return_type_anon(sup_info.scope, sup_info.region_def_id, ty_fndecl_sup); let sub_is_ret_type = - self.is_return_type_anon(scope_def_id_sub, bregion_sub, ty_fndecl_sub); + self.is_return_type_anon(sub_info.scope, sub_info.region_def_id, ty_fndecl_sub); debug!( "try_report_anon_anon_conflict: sub_is_ret_type={:?} sup_is_ret_type={:?}", diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs index cd621fd1a39..2a487a48a8e 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs @@ -1,7 +1,7 @@ use core::ops::ControlFlow; use rustc_hir as hir; -use rustc_hir::def_id::LocalDefId; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter; @@ -28,16 +28,15 @@ pub fn find_anon_type<'tcx>( tcx: TyCtxt<'tcx>, generic_param_scope: LocalDefId, region: Region<'tcx>, - br: &ty::BoundRegionKind, ) -> Option<(&'tcx hir::Ty<'tcx>, &'tcx hir::FnSig<'tcx>)> { let anon_reg = tcx.is_suitable_region(generic_param_scope, region)?; - let fn_sig = tcx.hir_node_by_def_id(anon_reg.def_id).fn_sig()?; + let fn_sig = tcx.hir_node_by_def_id(anon_reg.scope).fn_sig()?; fn_sig .decl .inputs .iter() - .find_map(|arg| find_component_for_bound_region(tcx, arg, br)) + .find_map(|arg| find_component_for_bound_region(tcx, arg, anon_reg.region_def_id)) .map(|ty| (ty, fn_sig)) } @@ -46,9 +45,9 @@ pub fn find_anon_type<'tcx>( fn find_component_for_bound_region<'tcx>( tcx: TyCtxt<'tcx>, arg: &'tcx hir::Ty<'tcx>, - br: &ty::BoundRegionKind, + region_def_id: DefId, ) -> Option<&'tcx hir::Ty<'tcx>> { - FindNestedTypeVisitor { tcx, bound_region: *br, current_index: ty::INNERMOST } + FindNestedTypeVisitor { tcx, region_def_id, current_index: ty::INNERMOST } .visit_ty(arg) .break_value() } @@ -62,9 +61,8 @@ fn find_component_for_bound_region<'tcx>( // specific part of the type in the error message. struct FindNestedTypeVisitor<'tcx> { tcx: TyCtxt<'tcx>, - // The bound_region corresponding to the Refree(freeregion) - // associated with the anonymous region we are looking for. - bound_region: ty::BoundRegionKind, + // The `DefId` of the region we're looking for. + region_def_id: DefId, current_index: ty::DebruijnIndex, } @@ -96,16 +94,13 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { hir::TyKind::Ref(lifetime, _) => { // the lifetime of the Ref let hir_id = lifetime.hir_id; - match (self.tcx.named_bound_var(hir_id), self.bound_region) { + match self.tcx.named_bound_var(hir_id) { // Find the index of the named region that was part of the // error. We will then search the function parameters for a bound // region at the right depth with the same index - ( - Some(rbv::ResolvedArg::EarlyBound(id)), - ty::BoundRegionKind::Named(def_id, _), - ) => { - debug!("EarlyBound id={:?} def_id={:?}", id, def_id); - if id.to_def_id() == def_id { + Some(rbv::ResolvedArg::EarlyBound(id)) => { + debug!("EarlyBound id={:?}", id); + if id.to_def_id() == self.region_def_id { return ControlFlow::Break(arg); } } @@ -113,31 +108,25 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { // Find the index of the named region that was part of the // error. We will then search the function parameters for a bound // region at the right depth with the same index - ( - Some(rbv::ResolvedArg::LateBound(debruijn_index, _, id)), - ty::BoundRegionKind::Named(def_id, _), - ) => { + Some(rbv::ResolvedArg::LateBound(debruijn_index, _, id)) => { debug!( "FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}", debruijn_index ); - debug!("LateBound id={:?} def_id={:?}", id, def_id); - if debruijn_index == self.current_index && id.to_def_id() == def_id { + debug!("LateBound id={:?}", id); + if debruijn_index == self.current_index + && id.to_def_id() == self.region_def_id + { return ControlFlow::Break(arg); } } - ( - Some( - rbv::ResolvedArg::StaticLifetime - | rbv::ResolvedArg::Free(_, _) - | rbv::ResolvedArg::EarlyBound(_) - | rbv::ResolvedArg::LateBound(_, _, _) - | rbv::ResolvedArg::Error(_), - ) - | None, - _, - ) => { + Some( + rbv::ResolvedArg::StaticLifetime + | rbv::ResolvedArg::Free(_, _) + | rbv::ResolvedArg::Error(_), + ) + | None => { debug!("no arg found"); } } @@ -151,7 +140,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { return if intravisit::walk_ty( &mut TyPathVisitor { tcx: self.tcx, - bound_region: self.bound_region, + region_def_id: self.region_def_id, current_index: self.current_index, }, arg, @@ -179,7 +168,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { // specific part of the type in the error message. struct TyPathVisitor<'tcx> { tcx: TyCtxt<'tcx>, - bound_region: ty::BoundRegionKind, + region_def_id: DefId, current_index: ty::DebruijnIndex, } @@ -192,38 +181,29 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> { } fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) -> Self::Result { - match (self.tcx.named_bound_var(lifetime.hir_id), self.bound_region) { + match self.tcx.named_bound_var(lifetime.hir_id) { // the lifetime of the TyPath! - (Some(rbv::ResolvedArg::EarlyBound(id)), ty::BoundRegionKind::Named(def_id, _)) => { - debug!("EarlyBound id={:?} def_id={:?}", id, def_id); - if id.to_def_id() == def_id { + Some(rbv::ResolvedArg::EarlyBound(id)) => { + debug!("EarlyBound id={:?}", id); + if id.to_def_id() == self.region_def_id { return ControlFlow::Break(()); } } - ( - Some(rbv::ResolvedArg::LateBound(debruijn_index, _, id)), - ty::BoundRegionKind::Named(def_id, _), - ) => { + Some(rbv::ResolvedArg::LateBound(debruijn_index, _, id)) => { debug!("FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}", debruijn_index,); debug!("id={:?}", id); - debug!("def_id={:?}", def_id); - if debruijn_index == self.current_index && id.to_def_id() == def_id { + if debruijn_index == self.current_index && id.to_def_id() == self.region_def_id { return ControlFlow::Break(()); } } - ( - Some( - rbv::ResolvedArg::StaticLifetime - | rbv::ResolvedArg::EarlyBound(_) - | rbv::ResolvedArg::LateBound(_, _, _) - | rbv::ResolvedArg::Free(_, _) - | rbv::ResolvedArg::Error(_), - ) - | None, - _, - ) => { + Some( + rbv::ResolvedArg::StaticLifetime + | rbv::ResolvedArg::Free(_, _) + | rbv::ResolvedArg::Error(_), + ) + | None => { debug!("no arg found"); } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/named_anon_conflict.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/named_anon_conflict.rs index 9fa5a8ac637..aa7935a29f0 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/named_anon_conflict.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/named_anon_conflict.rs @@ -3,7 +3,7 @@ use rustc_errors::Diag; use rustc_middle::ty; -use rustc_span::symbol::kw; +use rustc_span::kw; use tracing::debug; use crate::error_reporting::infer::nice_region_error::NiceRegionError; @@ -54,13 +54,13 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let param = anon_param_info.param; let new_ty = anon_param_info.param_ty; let new_ty_span = anon_param_info.param_ty_span; - let br = anon_param_info.bound_region; let is_first = anon_param_info.is_first; - let scope_def_id = region_info.def_id; + let scope_def_id = region_info.scope; let is_impl_item = region_info.is_impl_item; - match br { - ty::BoundRegionKind::Named(_, kw::UnderscoreLifetime) | ty::BoundRegionKind::Anon => {} + match anon_param_info.kind { + ty::LateParamRegionKind::Named(_, kw::UnderscoreLifetime) + | ty::LateParamRegionKind::Anon(_) => {} _ => { /* not an anonymous region */ debug!("try_report_named_anon_conflict: not an anonymous region"); @@ -73,7 +73,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { return None; } - if find_anon_type(self.tcx(), self.generic_param_scope, anon, &br).is_some() + if find_anon_type(self.tcx(), self.generic_param_scope, anon).is_some() && self.is_self_anon(is_first, scope_def_id) { return None; diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_error.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_error.rs index 4398af76ab2..aaaefd81d19 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_error.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_error.rs @@ -21,9 +21,10 @@ use crate::traits::{ObligationCause, ObligationCauseCode}; // HACK(eddyb) maybe move this in a more central location. #[derive(Copy, Clone)] pub struct Highlighted<'tcx, T> { - tcx: TyCtxt<'tcx>, - highlight: RegionHighlightMode<'tcx>, - value: T, + pub tcx: TyCtxt<'tcx>, + pub highlight: RegionHighlightMode<'tcx>, + pub value: T, + pub ns: Namespace, } impl<'tcx, T> IntoDiagArg for Highlighted<'tcx, T> @@ -37,7 +38,7 @@ where impl<'tcx, T> Highlighted<'tcx, T> { fn map<U>(self, f: impl FnOnce(T) -> U) -> Highlighted<'tcx, U> { - Highlighted { tcx: self.tcx, highlight: self.highlight, value: f(self.value) } + Highlighted { tcx: self.tcx, highlight: self.highlight, value: f(self.value), ns: self.ns } } } @@ -46,7 +47,7 @@ where T: for<'a> Print<'tcx, FmtPrinter<'a, 'tcx>>, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS); + let mut printer = ty::print::FmtPrinter::new(self.tcx, self.ns); printer.region_highlight_mode = self.highlight; self.value.print(&mut printer)?; @@ -381,6 +382,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { tcx: self.tcx(), highlight: RegionHighlightMode::default(), value: trait_ref, + ns: Namespace::TypeNS, }; let same_self_type = actual_trait_ref.self_ty() == expected_trait_ref.self_ty(); diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs index 9a7bdaa5b57..3416a17624e 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs @@ -11,9 +11,8 @@ use rustc_hir::{ use rustc_middle::ty::{ self, AssocItemContainer, StaticLifetimeVisitor, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor, }; -use rustc_span::Span; use rustc_span::def_id::LocalDefId; -use rustc_span::symbol::Ident; +use rustc_span::{Ident, Span}; use tracing::debug; use crate::error_reporting::infer::nice_region_error::NiceRegionError; @@ -50,7 +49,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { // This may have a closure and it would cause ICE // through `find_param_with_region` (#78262). let anon_reg_sup = tcx.is_suitable_region(self.generic_param_scope, *sup_r)?; - let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id); + let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.scope); if fn_returns.is_empty() { return None; } @@ -196,7 +195,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let mut err = self.tcx().dcx().create_err(diag); - let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id); + let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.scope); let mut override_error_code = None; if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sup_origin @@ -250,7 +249,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { Some(arg), captures, Some((param.param_ty_span, param.param_ty.to_string())), - Some(anon_reg_sup.def_id), + Some(anon_reg_sup.scope), ); let reported = err.emit(); diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs index 592ade8ede2..95dd1b28a39 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs @@ -2,18 +2,19 @@ use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; -use rustc_hir::def::Res; +use rustc_hir::def::{Namespace, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_middle::hir::nested_filter; use rustc_middle::traits::ObligationCauseCode; use rustc_middle::ty::error::ExpectedFound; use rustc_middle::ty::print::RegionHighlightMode; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitor}; +use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; use rustc_span::Span; use tracing::debug; use crate::error_reporting::infer::nice_region_error::NiceRegionError; +use crate::error_reporting::infer::nice_region_error::placeholder_error::Highlighted; use crate::errors::{ConsiderBorrowingParamHelp, RelationshipHelp, TraitImplDiff}; use crate::infer::{RegionResolutionError, Subtype, ValuePairs}; @@ -32,19 +33,14 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { _, ) = error.clone() && let (Subtype(sup_trace), Subtype(sub_trace)) = (&sup_origin, &sub_origin) - && let ObligationCauseCode::CompareImplItem { trait_item_def_id, .. } = + && let &ObligationCauseCode::CompareImplItem { trait_item_def_id, .. } = sub_trace.cause.code() && sub_trace.values == sup_trace.values && let ValuePairs::PolySigs(ExpectedFound { expected, found }) = sub_trace.values { // FIXME(compiler-errors): Don't like that this needs `Ty`s, but // all of the region highlighting machinery only deals with those. - let guar = self.emit_err( - var_origin.span(), - Ty::new_fn_ptr(self.cx.tcx, expected), - Ty::new_fn_ptr(self.cx.tcx, found), - *trait_item_def_id, - ); + let guar = self.emit_err(var_origin.span(), expected, found, trait_item_def_id); return Some(guar); } None @@ -53,11 +49,11 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { fn emit_err( &self, sp: Span, - expected: Ty<'tcx>, - found: Ty<'tcx>, - trait_def_id: DefId, + expected: ty::PolyFnSig<'tcx>, + found: ty::PolyFnSig<'tcx>, + trait_item_def_id: DefId, ) -> ErrorGuaranteed { - let trait_sp = self.tcx().def_span(trait_def_id); + let trait_sp = self.tcx().def_span(trait_item_def_id); // Mark all unnamed regions in the type with a number. // This diagnostic is called in response to lifetime errors, so be informative. @@ -67,10 +63,10 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } impl<'tcx> HighlightBuilder<'tcx> { - fn build(ty: Ty<'tcx>) -> RegionHighlightMode<'tcx> { + fn build(sig: ty::PolyFnSig<'tcx>) -> RegionHighlightMode<'tcx> { let mut builder = HighlightBuilder { highlight: RegionHighlightMode::default(), counter: 1 }; - builder.visit_ty(ty); + sig.visit_with(&mut builder); builder.highlight } } @@ -85,16 +81,21 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } let expected_highlight = HighlightBuilder::build(expected); - let expected = self - .cx - .extract_inference_diagnostics_data(expected.into(), Some(expected_highlight)) - .name; + let tcx = self.cx.tcx; + let expected = Highlighted { + highlight: expected_highlight, + ns: Namespace::TypeNS, + tcx, + value: expected, + } + .to_string(); let found_highlight = HighlightBuilder::build(found); let found = - self.cx.extract_inference_diagnostics_data(found.into(), Some(found_highlight)).name; + Highlighted { highlight: found_highlight, ns: Namespace::TypeNS, tcx, value: found } + .to_string(); // Get the span of all the used type parameters in the method. - let assoc_item = self.tcx().associated_item(trait_def_id); + let assoc_item = self.tcx().associated_item(trait_item_def_id); let mut visitor = TypeParamSpanVisitor { tcx: self.tcx(), types: vec![] }; match assoc_item.kind { ty::AssocKind::Fn => { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs index 218d2e753ef..445937ad169 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs @@ -2,7 +2,7 @@ //! anonymous regions. use rustc_hir as hir; -use rustc_hir::def_id::LocalDefId; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::{self, Binder, Region, Ty, TyCtxt, TypeFoldable}; use rustc_span::Span; @@ -17,8 +17,8 @@ pub struct AnonymousParamInfo<'tcx> { pub param: &'tcx hir::Param<'tcx>, /// The type corresponding to the anonymous region parameter. pub param_ty: Ty<'tcx>, - /// The ty::BoundRegionKind corresponding to the anonymous region. - pub bound_region: ty::BoundRegionKind, + /// The `ty::LateParamRegionKind` corresponding to the anonymous region. + pub kind: ty::LateParamRegionKind, /// The `Span` of the parameter type. pub param_ty_span: Span, /// Signals that the argument is the first parameter in the declaration. @@ -43,11 +43,11 @@ pub fn find_param_with_region<'tcx>( anon_region: Region<'tcx>, replace_region: Region<'tcx>, ) -> Option<AnonymousParamInfo<'tcx>> { - let (id, bound_region) = match *anon_region { - ty::ReLateParam(late_param) => (late_param.scope, late_param.bound_region), + let (id, kind) = match *anon_region { + ty::ReLateParam(late_param) => (late_param.scope, late_param.kind), ty::ReEarlyParam(ebr) => { let region_def = tcx.generics_of(generic_param_scope).region_param(ebr, tcx).def_id; - (tcx.parent(region_def), ty::BoundRegionKind::Named(region_def, ebr.name)) + (tcx.parent(region_def), ty::LateParamRegionKind::Named(region_def, ebr.name)) } _ => return None, // not a free region }; @@ -96,13 +96,7 @@ pub fn find_param_with_region<'tcx>( let ty_hir_id = fn_decl.inputs[index].hir_id; let param_ty_span = hir.span(ty_hir_id); let is_first = index == 0; - AnonymousParamInfo { - param, - param_ty: new_param_ty, - param_ty_span, - bound_region, - is_first, - } + AnonymousParamInfo { param, param_ty: new_param_ty, param_ty_span, kind, is_first } }) }) } @@ -122,7 +116,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { pub(super) fn is_return_type_anon( &self, scope_def_id: LocalDefId, - br: ty::BoundRegionKind, + region_def_id: DefId, hir_sig: &hir::FnSig<'_>, ) -> Option<Span> { let fn_ty = self.tcx().type_of(scope_def_id).instantiate_identity(); @@ -135,8 +129,8 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { None }; return match future_output { - Some(output) if self.includes_region(output, br) => Some(span), - None if self.includes_region(ret_ty, br) => Some(span), + Some(output) if self.includes_region(output, region_def_id) => Some(span), + None if self.includes_region(ret_ty, region_def_id) => Some(span), _ => None, }; } @@ -146,12 +140,15 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { fn includes_region( &self, ty: Binder<'tcx, impl TypeFoldable<TyCtxt<'tcx>>>, - region: ty::BoundRegionKind, + region_def_id: DefId, ) -> bool { let late_bound_regions = self.tcx().collect_referenced_late_bound_regions(ty); // We are only checking is any region meets the condition so order doesn't matter #[allow(rustc::potential_query_instability)] - late_bound_regions.iter().any(|r| *r == region) + late_bound_regions.iter().any(|r| match *r { + ty::BoundRegionKind::Named(def_id, _) => def_id == region_def_id, + _ => false, + }) } // Here we check for the case where anonymous region diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note.rs index c4dac1a936c..beae9962f7f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/note.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note.rs @@ -3,7 +3,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::traits::ObligationCauseCode; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{self, IsSuggestable, Region, Ty}; -use rustc_span::symbol::kw; +use rustc_span::kw; use tracing::debug; use super::ObligationCauseAsDiagArg; diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index 9fd7dccc57c..98b5fb2052f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -12,8 +12,7 @@ use rustc_middle::bug; use rustc_middle::traits::ObligationCauseCode; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{self, IsSuggestable, Region, Ty, TyCtxt, TypeVisitableExt as _}; -use rustc_span::symbol::kw; -use rustc_span::{BytePos, ErrorGuaranteed, Span, Symbol}; +use rustc_span::{BytePos, ErrorGuaranteed, Span, Symbol, kw}; use rustc_type_ir::Upcast as _; use tracing::{debug, instrument}; @@ -790,7 +789,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let lifetime_scope = match sub.kind() { ty::ReStatic => hir::def_id::CRATE_DEF_ID, _ => match self.tcx.is_suitable_region(generic_param_scope, sub) { - Some(info) => info.def_id, + Some(info) => info.scope, None => generic_param_scope, }, }; @@ -864,13 +863,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - let (lifetime_def_id, lifetime_scope) = - match self.tcx.is_suitable_region(generic_param_scope, lifetime) { - Some(info) if !lifetime.has_name() => { - (info.bound_region.get_id().unwrap().expect_local(), info.def_id) - } - _ => return lifetime.get_name_or_anon().to_string(), - }; + let (lifetime_def_id, lifetime_scope) = match self + .tcx + .is_suitable_region(generic_param_scope, lifetime) + { + Some(info) if !lifetime.has_name() => (info.region_def_id.expect_local(), info.scope), + _ => return lifetime.get_name_or_anon().to_string(), + }; let new_lt = { let generics = self.tcx.generics_of(lifetime_scope); @@ -1096,14 +1095,13 @@ fn msg_span_from_named_region<'tcx>( (text, Some(span)) } ty::ReLateParam(ref fr) => { - if !fr.bound_region.is_named() - && let Some((ty, _)) = - find_anon_type(tcx, generic_param_scope, region, &fr.bound_region) + if !fr.kind.is_named() + && let Some((ty, _)) = find_anon_type(tcx, generic_param_scope, region) { ("the anonymous lifetime defined here".to_string(), Some(ty.span)) } else { - match fr.bound_region { - ty::BoundRegionKind::Named(param_def_id, name) => { + match fr.kind { + ty::LateParamRegionKind::Named(param_def_id, name) => { let span = tcx.def_span(param_def_id); let text = if name == kw::UnderscoreLifetime { "the anonymous lifetime as defined here".to_string() @@ -1112,7 +1110,7 @@ fn msg_span_from_named_region<'tcx>( }; (text, Some(span)) } - ty::BoundRegionKind::Anon => ( + ty::LateParamRegionKind::Anon(_) => ( "the anonymous lifetime as defined here".to_string(), Some(tcx.def_span(generic_param_scope)), ), diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs index fc2d0ba36f0..08775df5ac9 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs @@ -210,7 +210,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { (Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code() { ObligationCauseCode::Pattern { span: Some(then_span), origin_expr, .. } => { - origin_expr.then_some(ConsiderAddingAwait::FutureSugg { + origin_expr.is_some().then_some(ConsiderAddingAwait::FutureSugg { span: then_span.shrink_to_hi(), }) } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 4fb02f60943..d41f8f46c17 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -27,8 +27,7 @@ use rustc_middle::ty::{ self, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast, }; use rustc_middle::{bug, span_bug}; -use rustc_span::symbol::sym; -use rustc_span::{BytePos, DUMMY_SP, Span, Symbol}; +use rustc_span::{BytePos, DUMMY_SP, Span, Symbol, sym}; use tracing::{debug, instrument}; use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote}; diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index f10314c1c9e..a401fcf3505 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -1,21 +1,21 @@ use std::iter; use std::path::PathBuf; -use rustc_ast::{AttrArgs, AttrKind, Attribute, MetaItemInner}; +use rustc_ast::MetaItemInner; use rustc_data_structures::fx::FxHashMap; use rustc_errors::codes::*; use rustc_errors::{ErrorGuaranteed, struct_span_code_err}; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::{AttrArgs, AttrKind, Attribute}; use rustc_macros::LintDiagnostic; use rustc_middle::bug; use rustc_middle::ty::print::PrintTraitRefExt as _; use rustc_middle::ty::{self, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, TyCtxt}; use rustc_parse_format::{ParseMode, Parser, Piece, Position}; use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES; -use rustc_span::Span; -use rustc_span::symbol::{Symbol, kw, sym}; +use rustc_span::{Span, Symbol, kw, sym}; use tracing::{debug, info}; -use {rustc_attr as attr, rustc_hir as hir}; +use {rustc_attr_parsing as attr, rustc_hir as hir}; use super::{ObligationCauseCode, PredicateObligation}; use crate::error_reporting::TypeErrCtxt; @@ -639,7 +639,7 @@ impl<'tcx> OnUnimplementedDirective { let report_span = match &item.args { AttrArgs::Empty => item.path.span, AttrArgs::Delimited(args) => args.dspan.entire(), - AttrArgs::Eq { eq_span, value } => eq_span.to(value.span()), + AttrArgs::Eq { eq_span, expr } => eq_span.to(expr.span), }; if let Some(item_def_id) = item_def_id.as_local() { @@ -654,7 +654,7 @@ impl<'tcx> OnUnimplementedDirective { } } else if is_diagnostic_namespace_variant { match &attr.kind { - AttrKind::Normal(p) if !matches!(p.item.args, AttrArgs::Empty) => { + AttrKind::Normal(p) if !matches!(p.args, AttrArgs::Empty) => { if let Some(item_def_id) = item_def_id.as_local() { tcx.emit_node_span_lint( UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs index c47c2169691..d82acc4e054 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs @@ -1,8 +1,6 @@ use std::fmt; -use rustc_errors::{ - Diag, E0275, EmissionGuarantee, ErrorGuaranteed, FatalError, struct_span_code_err, -}; +use rustc_errors::{Diag, E0275, EmissionGuarantee, ErrorGuaranteed, struct_span_code_err}; use rustc_hir::def::Namespace; use rustc_hir::def_id::LOCAL_CRATE; use rustc_infer::traits::{Obligation, PredicateObligation}; @@ -52,8 +50,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ) -> ! { let mut err = self.build_overflow_error(cause, span, suggest_increasing_limit); mutate(&mut err); - err.emit(); - FatalError.raise(); + err.emit().raise_fatal(); } pub fn build_overflow_error( diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 94682f501a8..6a8f7f4ee35 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -20,7 +20,8 @@ use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; use rustc_hir::{ - CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, HirId, Node, is_range_literal, + CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, HirId, Node, expr_needs_parens, + is_range_literal, }; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk}; use rustc_middle::hir::map; @@ -37,8 +38,9 @@ use rustc_middle::ty::{ }; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::LocalDefId; -use rustc_span::symbol::{Ident, Symbol, kw, sym}; -use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, ExpnKind, MacroKind, Span}; +use rustc_span::{ + BytePos, DUMMY_SP, DesugaringKind, ExpnKind, Ident, MacroKind, Span, Symbol, kw, sym, +}; use tracing::{debug, instrument}; use super::{ @@ -1391,13 +1393,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let Some(expr) = expr_finder.result else { return false; }; - let needs_parens = match expr.kind { - // parenthesize if needed (Issue #46756) - hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true, - // parenthesize borrows of range literals (Issue #54505) - _ if is_range_literal(expr) => true, - _ => false, - }; + let needs_parens = expr_needs_parens(expr); let span = if needs_parens { span } else { span.shrink_to_lo() }; let suggestions = if !needs_parens { diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index 68fe90f0de2..700c79a7065 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -14,8 +14,7 @@ use rustc_hir::{FnRetTy, GenericParamKind, Node}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::print::{PrintTraitRefExt as _, TraitRefPrintOnlyTraitPath}; use rustc_middle::ty::{self, Binder, ClosureKind, FnSig, PolyTraitRef, Region, Ty, TyCtxt}; -use rustc_span::symbol::{Ident, Symbol, kw}; -use rustc_span::{BytePos, Span}; +use rustc_span::{BytePos, Ident, Span, Symbol, kw}; use crate::error_reporting::infer::ObligationCauseAsDiagArg; use crate::error_reporting::infer::need_type_info::UnderspecifiedArgKind; @@ -517,7 +516,7 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { return false; }; - let node = self.tcx.hir_node_by_def_id(anon_reg.def_id); + let node = self.tcx.hir_node_by_def_id(anon_reg.scope); let is_impl = matches!(&node, hir::Node::ImplItem(_)); let (generics, parent_generics) = match node { hir::Node::Item(&hir::Item { @@ -527,7 +526,7 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { | hir::Node::TraitItem(&hir::TraitItem { ref generics, .. }) | hir::Node::ImplItem(&hir::ImplItem { ref generics, .. }) => ( generics, - match self.tcx.parent_hir_node(self.tcx.local_def_id_to_hir_id(anon_reg.def_id)) + match self.tcx.parent_hir_node(self.tcx.local_def_id_to_hir_id(anon_reg.scope)) { hir::Node::Item(hir::Item { kind: hir::ItemKind::Trait(_, _, ref generics, ..), diff --git a/compiler/rustc_trait_selection/src/errors/note_and_explain.rs b/compiler/rustc_trait_selection/src/errors/note_and_explain.rs index cc0a637a78e..b752dbf3093 100644 --- a/compiler/rustc_trait_selection/src/errors/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/errors/note_and_explain.rs @@ -2,8 +2,7 @@ use rustc_errors::{Diag, EmissionGuarantee, IntoDiagArg, SubdiagMessageOp, Subdi use rustc_hir::def_id::LocalDefId; use rustc_middle::bug; use rustc_middle::ty::{self, TyCtxt}; -use rustc_span::Span; -use rustc_span::symbol::kw; +use rustc_span::{Span, kw}; use crate::error_reporting::infer::nice_region_error::find_anon_type; use crate::fluent_generated as fluent; @@ -40,15 +39,14 @@ impl<'a> DescriptionCtx<'a> { } } ty::ReLateParam(ref fr) => { - if !fr.bound_region.is_named() - && let Some((ty, _)) = - find_anon_type(tcx, generic_param_scope, region, &fr.bound_region) + if !fr.kind.is_named() + && let Some((ty, _)) = find_anon_type(tcx, generic_param_scope, region) { (Some(ty.span), "defined_here", String::new()) } else { let scope = fr.scope.expect_local(); - match fr.bound_region { - ty::BoundRegionKind::Named(_, name) => { + match fr.kind { + ty::LateParamRegionKind::Named(_, name) => { let span = if let Some(param) = tcx .hir() .get_generics(scope) @@ -64,7 +62,7 @@ impl<'a> DescriptionCtx<'a> { (Some(span), "as_defined", name.to_string()) } } - ty::BoundRegionKind::Anon => { + ty::LateParamRegionKind::Anon(_) => { let span = Some(tcx.def_span(scope)); (span, "defined_here", String::new()) } diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index 11d72106b22..1af383e9200 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -23,6 +23,7 @@ #![feature(extract_if)] #![feature(if_let_guard)] #![feature(iter_intersperse)] +#![feature(iterator_try_reduce)] #![feature(let_chains)] #![feature(never_type)] #![feature(rustdoc_internals)] diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index 97cde67799c..9b8c9ff6bb8 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -123,8 +123,6 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< ) }); - assert_eq!(region_constraints.member_constraints, vec![]); - let mut seen = FxHashSet::default(); region_constraints .outlives diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index a98871b2d60..971d3a81102 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -21,8 +21,7 @@ use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableEx use rustc_middle::ty::{self, Ty, TyCtxt, TypingMode}; pub use rustc_next_trait_solver::coherence::*; use rustc_next_trait_solver::solve::SolverDelegateEvalExt; -use rustc_span::symbol::sym; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Span, sym}; use tracing::{debug, instrument, warn}; use super::ObligationCtxt; diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index d216ae72913..7a67b943e94 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -84,7 +84,7 @@ pub fn type_allowed_to_implement_copy<'tcx>( return Err(CopyImplementationError::HasDestructor); } - if impl_safety == hir::Safety::Safe && self_type.has_unsafe_fields() { + if impl_safety.is_safe() && self_type.has_unsafe_fields() { return Err(CopyImplementationError::HasUnsafeFields); } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 069fab6a6e6..1dcd0d0dfb8 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -447,7 +447,7 @@ pub fn normalize_param_env_or_error<'tcx>( // This works fairly well because trait matching does not actually care about param-env // TypeOutlives predicates - these are normally used by regionck. let outlives_predicates: Vec<_> = predicates - .extract_if(|predicate| { + .extract_if(.., |predicate| { matches!(predicate.kind().skip_binder(), ty::ClauseKind::TypeOutlives(..)) }) .collect(); @@ -599,7 +599,7 @@ pub fn try_evaluate_const<'tcx>( // even though it is not something we should ever actually encounter. // // Array repeat expr counts are allowed to syntactically use generic parameters - // but must not actually depend on them in order to evalaute succesfully. This means + // but must not actually depend on them in order to evalaute successfully. This means // that it is actually fine to evalaute them in their own environment rather than with // the actually provided generic arguments. tcx.dcx().delayed_bug( diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index 88c11e55b7a..23dabe32ff2 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -4,11 +4,10 @@ use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds; use rustc_macros::extension; use rustc_middle::infer::canonical::{OriginalQueryValues, QueryRegionConstraints}; -use rustc_middle::span_bug; pub use rustc_middle::traits::query::OutlivesBound; use rustc_middle::ty::{self, ParamEnv, Ty, TypeFolder, TypeVisitableExt}; use rustc_span::def_id::LocalDefId; -use tracing::{debug, instrument}; +use tracing::instrument; use crate::infer::InferCtxt; use crate::traits::{ObligationCause, ObligationCtxt}; @@ -86,16 +85,12 @@ fn implied_outlives_bounds<'a, 'tcx>( bounds.retain(|bound| !bound.has_placeholders()); if !constraints.is_empty() { - debug!(?constraints); - if !constraints.member_constraints.is_empty() { - span_bug!(span, "{:#?}", constraints.member_constraints); - } - + let QueryRegionConstraints { outlives } = constraints; // Instantiation may have produced new inference variables and constraints on those // variables. Process these constraints. let ocx = ObligationCtxt::new(infcx); let cause = ObligationCause::misc(span, body_id); - for &constraint in &constraints.outlives { + for &constraint in &outlives { ocx.register_obligation(infcx.query_outlives_constraint_to_obligation( constraint, cause.clone(), diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 5f1dbfeedfb..4bccd3450bc 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -17,7 +17,7 @@ use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, Term, Ty, TyCtxt, TypingMode, Upcast}; use rustc_middle::{bug, span_bug}; -use rustc_span::symbol::sym; +use rustc_span::sym; use thin_vec::thin_vec; use tracing::{debug, instrument}; diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index a618d96ce95..54fce914bb6 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -180,11 +180,8 @@ where span, )?; output.error_info = error_info; - if let Some(constraints) = output.constraints { - region_constraints - .member_constraints - .extend(constraints.member_constraints.iter().cloned()); - region_constraints.outlives.extend(constraints.outlives.iter().cloned()); + if let Some(QueryRegionConstraints { outlives }) = output.constraints { + region_constraints.outlives.extend(outlives.iter().cloned()); } output.constraints = if region_constraints.is_empty() { None diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 25fe43e3a0e..0462b1d9ee7 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -31,8 +31,7 @@ use rustc_middle::ty::{ self, GenericArgsRef, PolyProjectionPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, TypingMode, Upcast, }; -use rustc_span::Symbol; -use rustc_span::symbol::sym; +use rustc_span::{Symbol, sym}; use tracing::{debug, instrument, trace}; use self::EvaluationResult::*; @@ -445,7 +444,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Winnow, but record the exact outcome of evaluation, which // is needed for specialization. Propagate overflow if it occurs. - let mut candidates = candidates + let candidates = candidates .into_iter() .map(|c| match self.evaluate_candidate(stack, &c) { Ok(eval) if eval.may_apply() => { @@ -458,40 +457,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .flat_map(Result::transpose) .collect::<Result<Vec<_>, _>>()?; - debug!(?stack, ?candidates, "winnowed to {} candidates", candidates.len()); - - let has_non_region_infer = stack.obligation.predicate.has_non_region_infer(); - - // If there are STILL multiple candidates, we can further - // reduce the list by dropping duplicates -- including - // resolving specializations. - if candidates.len() > 1 { - let mut i = 0; - while i < candidates.len() { - let should_drop_i = (0..candidates.len()).filter(|&j| i != j).any(|j| { - self.candidate_should_be_dropped_in_favor_of( - &candidates[i], - &candidates[j], - has_non_region_infer, - ) == DropVictim::Yes - }); - if should_drop_i { - debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len()); - candidates.swap_remove(i); - } else { - debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len()); - i += 1; - - // If there are *STILL* multiple candidates, give up - // and report ambiguity. - if i > 1 { - debug!("multiple matches, ambig"); - return Ok(None); - } - } - } - } - + debug!(?stack, ?candidates, "{} potentially applicable candidates", candidates.len()); // If there are *NO* candidates, then there are no impls -- // that we know of, anyway. Note that in the case where there // are unbound type variables within the obligation, it might @@ -508,13 +474,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // to have emitted at least one. if stack.obligation.predicate.references_error() { debug!(?stack.obligation.predicate, "found error type in predicate, treating as ambiguous"); - return Ok(None); + Ok(None) + } else { + Err(Unimplemented) + } + } else { + let has_non_region_infer = stack.obligation.predicate.has_non_region_infer(); + if let Some(candidate) = self.winnow_candidates(has_non_region_infer, candidates) { + self.filter_reservation_impls(candidate) + } else { + Ok(None) } - return Err(Unimplemented); } - - // Just one candidate left. - self.filter_reservation_impls(candidates.pop().unwrap().candidate) } /////////////////////////////////////////////////////////////////////////// @@ -1803,18 +1774,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -enum DropVictim { - Yes, - No, -} - -impl DropVictim { - fn drop_if(should_drop: bool) -> DropVictim { - if should_drop { DropVictim::Yes } else { DropVictim::No } - } -} - /// ## Winnowing /// /// Winnowing is the process of attempting to resolve ambiguity by @@ -1822,131 +1781,149 @@ impl DropVictim { /// type variables and then we also attempt to evaluate recursive /// bounds to see if they are satisfied. impl<'tcx> SelectionContext<'_, 'tcx> { - /// Returns `DropVictim::Yes` if `victim` should be dropped in favor of - /// `other`. Generally speaking we will drop duplicate - /// candidates and prefer where-clause candidates. + /// If there are multiple ways to prove a trait goal, we make some + /// *fairly arbitrary* choices about which candidate is actually used. /// - /// See the comment for "SelectionCandidate" for more details. - #[instrument(level = "debug", skip(self))] - fn candidate_should_be_dropped_in_favor_of( + /// For more details, look at the implementation of this method :) + #[instrument(level = "debug", skip(self), ret)] + fn winnow_candidates( &mut self, - victim: &EvaluatedCandidate<'tcx>, - other: &EvaluatedCandidate<'tcx>, has_non_region_infer: bool, - ) -> DropVictim { - if victim.candidate == other.candidate { - return DropVictim::Yes; + mut candidates: Vec<EvaluatedCandidate<'tcx>>, + ) -> Option<SelectionCandidate<'tcx>> { + if candidates.len() == 1 { + return Some(candidates.pop().unwrap().candidate); + } + + // We prefer trivial builtin candidates, i.e. builtin impls without any nested + // requirements, over all others. This is a fix for #53123 and prevents winnowing + // from accidentally extending the lifetime of a variable. + let mut trivial_builtin = candidates + .iter() + .filter(|c| matches!(c.candidate, BuiltinCandidate { has_nested: false })); + if let Some(_trivial) = trivial_builtin.next() { + // There should only ever be a single trivial builtin candidate + // as they would otherwise overlap. + debug_assert_eq!(trivial_builtin.next(), None); + return Some(BuiltinCandidate { has_nested: false }); } - // Check if a bound would previously have been removed when normalizing - // the param_env so that it can be given the lowest priority. See - // #50825 for the motivation for this. - let is_global = - |cand: ty::PolyTraitPredicate<'tcx>| cand.is_global() && !cand.has_bound_vars(); + // Before we consider where-bounds, we have to deduplicate them here and also + // drop where-bounds in case the same where-bound exists without bound vars. + // This is necessary as elaborating super-trait bounds may result in duplicates. + 'search_victim: loop { + for (i, this) in candidates.iter().enumerate() { + let ParamCandidate(this) = this.candidate else { continue }; + for (j, other) in candidates.iter().enumerate() { + if i == j { + continue; + } - // (*) Prefer `BuiltinCandidate { has_nested: false }`, `PointeeCandidate`, - // or `DiscriminantKindCandidate` to anything else. - // - // This is a fix for #53123 and prevents winnowing from accidentally extending the - // lifetime of a variable. - match (&other.candidate, &victim.candidate) { - // FIXME(@jswrenn): this should probably be more sophisticated - (TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => DropVictim::No, - - // (*) - (BuiltinCandidate { has_nested: false }, _) => DropVictim::Yes, - (_, BuiltinCandidate { has_nested: false }) => DropVictim::No, - - (ParamCandidate(other), ParamCandidate(victim)) => { - let same_except_bound_vars = other.skip_binder().trait_ref - == victim.skip_binder().trait_ref - && other.skip_binder().polarity == victim.skip_binder().polarity - && !other.skip_binder().trait_ref.has_escaping_bound_vars(); - if same_except_bound_vars { - // See issue #84398. In short, we can generate multiple ParamCandidates which are - // the same except for unused bound vars. Just pick the one with the fewest bound vars - // or the current one if tied (they should both evaluate to the same answer). This is - // probably best characterized as a "hack", since we might prefer to just do our - // best to *not* create essentially duplicate candidates in the first place. - DropVictim::drop_if(other.bound_vars().len() <= victim.bound_vars().len()) - } else { - DropVictim::No + let ParamCandidate(other) = other.candidate else { continue }; + if this == other { + candidates.remove(j); + continue 'search_victim; + } + + if this.skip_binder().trait_ref == other.skip_binder().trait_ref + && this.skip_binder().polarity == other.skip_binder().polarity + && !this.skip_binder().trait_ref.has_escaping_bound_vars() + { + candidates.remove(j); + continue 'search_victim; + } } } - ( - ParamCandidate(other_cand), - ImplCandidate(..) - | AutoImplCandidate - | ClosureCandidate { .. } - | AsyncClosureCandidate - | AsyncFnKindHelperCandidate - | CoroutineCandidate - | FutureCandidate - | IteratorCandidate - | AsyncIteratorCandidate - | FnPointerCandidate { .. } - | BuiltinObjectCandidate - | BuiltinUnsizeCandidate - | TraitUpcastingUnsizeCandidate(_) - | BuiltinCandidate { .. } - | TraitAliasCandidate - | ObjectCandidate(_) - | ProjectionCandidate(_), - ) => { - // We have a where clause so don't go around looking - // for impls. Arbitrarily give param candidates priority - // over projection and object candidates. - // - // Global bounds from the where clause should be ignored - // here (see issue #50825). - DropVictim::drop_if(!is_global(*other_cand)) - } - (ObjectCandidate(_) | ProjectionCandidate(_), ParamCandidate(victim_cand)) => { - // Prefer these to a global where-clause bound - // (see issue #50825). - if is_global(*victim_cand) { DropVictim::Yes } else { DropVictim::No } + break; + } + + // The next highest priority is for non-global where-bounds. However, while we don't + // prefer global where-clauses here, we do bail with ambiguity when encountering both + // a global and a non-global where-clause. + // + // Our handling of where-bounds is generally fairly messy but necessary for backwards + // compatability, see #50825 for why we need to handle global where-bounds like this. + let is_global = |c: ty::PolyTraitPredicate<'tcx>| c.is_global() && !c.has_bound_vars(); + let param_candidates = candidates + .iter() + .filter_map(|c| if let ParamCandidate(p) = c.candidate { Some(p) } else { None }); + let mut has_global_bounds = false; + let mut param_candidate = None; + for c in param_candidates { + if is_global(c) { + has_global_bounds = true; + } else if param_candidate.replace(c).is_some() { + // Ambiguity, two potentially different where-clauses + return None; } - ( - ImplCandidate(_) - | AutoImplCandidate - | ClosureCandidate { .. } - | AsyncClosureCandidate - | AsyncFnKindHelperCandidate - | CoroutineCandidate - | FutureCandidate - | IteratorCandidate - | AsyncIteratorCandidate - | FnPointerCandidate { .. } - | BuiltinObjectCandidate - | BuiltinUnsizeCandidate - | TraitUpcastingUnsizeCandidate(_) - | BuiltinCandidate { has_nested: true } - | TraitAliasCandidate, - ParamCandidate(victim_cand), - ) => { - // Prefer these to a global where-clause bound - // (see issue #50825). - DropVictim::drop_if( - is_global(*victim_cand) && other.evaluation.must_apply_modulo_regions(), - ) + } + if let Some(predicate) = param_candidate { + // Ambiguity, a global and a non-global where-bound. + if has_global_bounds { + return None; + } else { + return Some(ParamCandidate(predicate)); } + } + + // Prefer alias-bounds over blanket impls for rigid associated types. This is + // fairly arbitrary but once again necessary for backwards compatibility. + // If there are multiple applicable candidates which don't affect type inference, + // choose the one with the lowest index. + let alias_bound = candidates + .iter() + .filter_map(|c| if let ProjectionCandidate(i) = c.candidate { Some(i) } else { None }) + .try_reduce(|c1, c2| if has_non_region_infer { None } else { Some(c1.min(c2)) }); + match alias_bound { + Some(Some(index)) => return Some(ProjectionCandidate(index)), + Some(None) => {} + None => return None, + } + + // Need to prioritize builtin trait object impls as `<dyn Any as Any>::type_id` + // should use the vtable method and not the method provided by the user-defined + // impl `impl<T: ?Sized> Any for T { .. }`. This really shouldn't exist but is + // necessary due to #57893. We again arbitrarily prefer the applicable candidate + // with the lowest index. + let object_bound = candidates + .iter() + .filter_map(|c| if let ObjectCandidate(i) = c.candidate { Some(i) } else { None }) + .try_reduce(|c1, c2| if has_non_region_infer { None } else { Some(c1.min(c2)) }); + match object_bound { + Some(Some(index)) => return Some(ObjectCandidate(index)), + Some(None) => {} + None => return None, + } - (ProjectionCandidate(i), ProjectionCandidate(j)) - | (ObjectCandidate(i), ObjectCandidate(j)) => { - // Arbitrarily pick the lower numbered candidate for backwards - // compatibility reasons. Don't let this affect inference. - DropVictim::drop_if(i < j && !has_non_region_infer) + // Finally, handle overlapping user-written impls. + let impls = candidates.iter().filter_map(|c| { + if let ImplCandidate(def_id) = c.candidate { + Some((def_id, c.evaluation)) + } else { + None } - (ObjectCandidate(_), ProjectionCandidate(_)) - | (ProjectionCandidate(_), ObjectCandidate(_)) => { - bug!("Have both object and projection candidate") + }); + let mut impl_candidate = None; + for c in impls { + if let Some(prev) = impl_candidate.replace(c) { + if self.prefer_lhs_over_victim(has_non_region_infer, c, prev) { + // Ok, prefer `c` over the previous entry + } else if self.prefer_lhs_over_victim(has_non_region_infer, prev, c) { + // Ok, keep `prev` instead of the new entry + impl_candidate = Some(prev); + } else { + // Ambiguity, two potentially different where-clauses + return None; + } } - - // Arbitrarily give projection and object candidates priority. - ( - ObjectCandidate(_) | ProjectionCandidate(_), - ImplCandidate(..) + } + if let Some((def_id, _evaluation)) = impl_candidate { + // Don't use impl candidates which overlap with other candidates. + // This should pretty much only ever happen with malformed impls. + if candidates.iter().all(|c| match c.candidate { + BuiltinCandidate { has_nested: _ } + | TransmutabilityCandidate | AutoImplCandidate | ClosureCandidate { .. } | AsyncClosureCandidate @@ -1955,155 +1932,113 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | FutureCandidate | IteratorCandidate | AsyncIteratorCandidate - | FnPointerCandidate { .. } - | BuiltinObjectCandidate - | BuiltinUnsizeCandidate + | FnPointerCandidate + | TraitAliasCandidate | TraitUpcastingUnsizeCandidate(_) - | BuiltinCandidate { .. } - | TraitAliasCandidate, - ) => DropVictim::Yes, - - ( - ImplCandidate(..) - | AutoImplCandidate - | ClosureCandidate { .. } - | AsyncClosureCandidate - | AsyncFnKindHelperCandidate - | CoroutineCandidate - | FutureCandidate - | IteratorCandidate - | AsyncIteratorCandidate - | FnPointerCandidate { .. } | BuiltinObjectCandidate - | BuiltinUnsizeCandidate - | TraitUpcastingUnsizeCandidate(_) - | BuiltinCandidate { .. } - | TraitAliasCandidate, - ObjectCandidate(_) | ProjectionCandidate(_), - ) => DropVictim::No, - - (&ImplCandidate(other_def), &ImplCandidate(victim_def)) => { - // See if we can toss out `victim` based on specialization. - // While this requires us to know *for sure* that the `other` impl applies - // we still use modulo regions here. - // - // This is fine as specialization currently assumes that specializing - // impls have to be always applicable, meaning that the only allowed - // region constraints may be constraints also present on the default impl. - let tcx = self.tcx(); - if other.evaluation.must_apply_modulo_regions() - && tcx.specializes((other_def, victim_def)) - { - return DropVictim::Yes; - } - - match tcx.impls_are_allowed_to_overlap(other_def, victim_def) { - // For #33140 the impl headers must be exactly equal, the trait must not have - // any associated items and there are no where-clauses. - // - // We can just arbitrarily drop one of the impls. - Some(ty::ImplOverlapKind::FutureCompatOrderDepTraitObjects) => { - assert_eq!(other.evaluation, victim.evaluation); - DropVictim::Yes - } - // For candidates which already reference errors it doesn't really - // matter what we do 🤷 - Some(ty::ImplOverlapKind::Permitted { marker: false }) => { - DropVictim::drop_if(other.evaluation.must_apply_considering_regions()) - } - Some(ty::ImplOverlapKind::Permitted { marker: true }) => { - // Subtle: If the predicate we are evaluating has inference - // variables, do *not* allow discarding candidates due to - // marker trait impls. - // - // Without this restriction, we could end up accidentally - // constraining inference variables based on an arbitrarily - // chosen trait impl. - // - // Imagine we have the following code: - // - // ```rust - // #[marker] trait MyTrait {} - // impl MyTrait for u8 {} - // impl MyTrait for bool {} - // ``` - // - // And we are evaluating the predicate `<_#0t as MyTrait>`. - // - // During selection, we will end up with one candidate for each - // impl of `MyTrait`. If we were to discard one impl in favor - // of the other, we would be left with one candidate, causing - // us to "successfully" select the predicate, unifying - // _#0t with (for example) `u8`. - // - // However, we have no reason to believe that this unification - // is correct - we've essentially just picked an arbitrary - // *possibility* for _#0t, and required that this be the *only* - // possibility. - // - // Eventually, we will either: - // 1) Unify all inference variables in the predicate through - // some other means (e.g. type-checking of a function). We will - // then be in a position to drop marker trait candidates - // without constraining inference variables (since there are - // none left to constrain) - // 2) Be left with some unconstrained inference variables. We - // will then correctly report an inference error, since the - // existence of multiple marker trait impls tells us nothing - // about which one should actually apply. - DropVictim::drop_if( - !has_non_region_infer - && other.evaluation.must_apply_considering_regions(), - ) - } - None => DropVictim::No, - } + | BuiltinUnsizeCandidate => false, + // Non-global param candidates have already been handled, global + // where-bounds get ignored. + ParamCandidate(_) | ImplCandidate(_) => true, + ProjectionCandidate(_) | ObjectCandidate(_) => unreachable!(), + }) { + return Some(ImplCandidate(def_id)); + } else { + return None; } + } - (AutoImplCandidate, ImplCandidate(_)) | (ImplCandidate(_), AutoImplCandidate) => { - DropVictim::No - } + if candidates.len() == 1 { + Some(candidates.pop().unwrap().candidate) + } else { + // Also try ignoring all global where-bounds and check whether we end + // with a unique candidate in this case. + let mut not_a_global_where_bound = candidates + .into_iter() + .filter(|c| !matches!(c.candidate, ParamCandidate(p) if is_global(p))); + not_a_global_where_bound + .next() + .map(|c| c.candidate) + .filter(|_| not_a_global_where_bound.next().is_none()) + } + } - (AutoImplCandidate, _) | (_, AutoImplCandidate) => { - bug!( - "default implementations shouldn't be recorded \ - when there are other global candidates: {:?} {:?}", - other, - victim - ); + fn prefer_lhs_over_victim( + &self, + has_non_region_infer: bool, + (lhs, lhs_evaluation): (DefId, EvaluationResult), + (victim, victim_evaluation): (DefId, EvaluationResult), + ) -> bool { + let tcx = self.tcx(); + // See if we can toss out `victim` based on specialization. + // + // While this requires us to know *for sure* that the `lhs` impl applies + // we still use modulo regions here. This is fine as specialization currently + // assumes that specializing impls have to be always applicable, meaning that + // the only allowed region constraints may be constraints also present on the default impl. + if lhs_evaluation.must_apply_modulo_regions() { + if tcx.specializes((lhs, victim)) { + return true; } + } - // Everything else is ambiguous - ( - ImplCandidate(_) - | ClosureCandidate { .. } - | AsyncClosureCandidate - | AsyncFnKindHelperCandidate - | CoroutineCandidate - | FutureCandidate - | IteratorCandidate - | AsyncIteratorCandidate - | FnPointerCandidate { .. } - | BuiltinObjectCandidate - | BuiltinUnsizeCandidate - | TraitUpcastingUnsizeCandidate(_) - | BuiltinCandidate { has_nested: true } - | TraitAliasCandidate, - ImplCandidate(_) - | ClosureCandidate { .. } - | AsyncClosureCandidate - | AsyncFnKindHelperCandidate - | CoroutineCandidate - | FutureCandidate - | IteratorCandidate - | AsyncIteratorCandidate - | FnPointerCandidate { .. } - | BuiltinObjectCandidate - | BuiltinUnsizeCandidate - | TraitUpcastingUnsizeCandidate(_) - | BuiltinCandidate { has_nested: true } - | TraitAliasCandidate, - ) => DropVictim::No, + match tcx.impls_are_allowed_to_overlap(lhs, victim) { + // For #33140 the impl headers must be exactly equal, the trait must not have + // any associated items and there are no where-clauses. + // + // We can just arbitrarily drop one of the impls. + Some(ty::ImplOverlapKind::FutureCompatOrderDepTraitObjects) => { + assert_eq!(lhs_evaluation, victim_evaluation); + true + } + // For candidates which already reference errors it doesn't really + // matter what we do 🤷 + Some(ty::ImplOverlapKind::Permitted { marker: false }) => { + lhs_evaluation.must_apply_considering_regions() + } + Some(ty::ImplOverlapKind::Permitted { marker: true }) => { + // Subtle: If the predicate we are evaluating has inference + // variables, do *not* allow discarding candidates due to + // marker trait impls. + // + // Without this restriction, we could end up accidentally + // constraining inference variables based on an arbitrarily + // chosen trait impl. + // + // Imagine we have the following code: + // + // ```rust + // #[marker] trait MyTrait {} + // impl MyTrait for u8 {} + // impl MyTrait for bool {} + // ``` + // + // And we are evaluating the predicate `<_#0t as MyTrait>`. + // + // During selection, we will end up with one candidate for each + // impl of `MyTrait`. If we were to discard one impl in favor + // of the other, we would be left with one candidate, causing + // us to "successfully" select the predicate, unifying + // _#0t with (for example) `u8`. + // + // However, we have no reason to believe that this unification + // is correct - we've essentially just picked an arbitrary + // *possibility* for _#0t, and required that this be the *only* + // possibility. + // + // Eventually, we will either: + // 1) Unify all inference variables in the predicate through + // some other means (e.g. type-checking of a function). We will + // then be in a position to drop marker trait candidates + // without constraining inference variables (since there are + // none left to constrain) + // 2) Be left with some unconstrained inference variables. We + // will then correctly report an inference error, since the + // existence of multiple marker trait impls tells us nothing + // about which one should actually apply. + !has_non_region_infer && lhs_evaluation.must_apply_considering_regions() + } + None => false, } } } diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index 1430cfae51f..401b41c796d 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -483,15 +483,19 @@ fn report_negative_positive_conflict<'tcx>( negative_impl_def_id: DefId, positive_impl_def_id: DefId, ) -> ErrorGuaranteed { - tcx.dcx() - .create_err(NegativePositiveConflict { - impl_span: tcx.def_span(local_impl_def_id), - trait_desc: overlap.trait_ref, - self_ty: overlap.self_ty, - negative_impl_span: tcx.span_of_impl(negative_impl_def_id), - positive_impl_span: tcx.span_of_impl(positive_impl_def_id), - }) - .emit() + let mut diag = tcx.dcx().create_err(NegativePositiveConflict { + impl_span: tcx.def_span(local_impl_def_id), + trait_desc: overlap.trait_ref, + self_ty: overlap.self_ty, + negative_impl_span: tcx.span_of_impl(negative_impl_def_id), + positive_impl_span: tcx.span_of_impl(positive_impl_def_id), + }); + + for cause in &overlap.intercrate_ambiguity_causes { + cause.add_intercrate_ambiguity_hint(&mut diag); + } + + diag.emit() } fn report_conflicting_impls<'tcx>( diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index 83463babc4f..6ce9969aefe 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -338,16 +338,11 @@ pub(crate) mod rustc { }; match layout.variants() { + Variants::Empty => Ok(Self::uninhabited()), Variants::Single { index } => { - // Hilariously, `Single` is used even for 0-variant enums; - // `index` is just junk in that case. - if ty.ty_adt_def().unwrap().variants().is_empty() { - Ok(Self::uninhabited()) - } else { - // `Variants::Single` on enums with variants denotes that - // the enum delegates its layout to the variant at `index`. - layout_of_variant(*index, None) - } + // `Variants::Single` on enums with variants denotes that + // the enum delegates its layout to the variant at `index`. + layout_of_variant(*index, None) } Variants::Multiple { tag, tag_encoding, tag_field, .. } => { // `Variants::Multiple` denotes an enum with multiple @@ -500,6 +495,10 @@ pub(crate) mod rustc { (ty, layout): (Ty<'tcx>, Layout<'tcx>), i: FieldIdx, ) -> Ty<'tcx> { + // We cannot use `ty_and_layout_field` to retrieve the field type, since + // `ty_and_layout_field` erases regions in the returned type. We must + // not erase regions here, since we may need to ultimately emit outlives + // obligations as a consequence of the transmutability analysis. match ty.kind() { ty::Adt(def, args) => { match layout.variants { @@ -507,6 +506,7 @@ pub(crate) mod rustc { let field = &def.variant(index).fields[i]; field.ty(cx.tcx(), args) } + Variants::Empty => panic!("there is no field in Variants::Empty types"), // Discriminant field for enums (where applicable). Variants::Multiple { tag, .. } => { assert_eq!(i.as_usize(), 0); diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs index f7651e49cdd..ad86813c87e 100644 --- a/compiler/rustc_transmute/src/lib.rs +++ b/compiler/rustc_transmute/src/lib.rs @@ -131,7 +131,7 @@ mod rustc { c: Const<'tcx>, ) -> Option<Self> { use rustc_middle::ty::ScalarInt; - use rustc_span::symbol::sym; + use rustc_span::sym; let Some((cv, ty)) = c.try_to_valtree() else { return None; diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index f97e8d48c8e..8f9f66db1bd 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -6,7 +6,7 @@ use rustc_hir::intravisit::{self, Visitor}; use rustc_middle::query::Providers; use rustc_middle::ty::{self, ImplTraitInTraitData, TyCtxt}; use rustc_middle::{bug, span_bug}; -use rustc_span::symbol::kw; +use rustc_span::kw; pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 0d656f1b63b..a3b2ed07d4b 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -23,8 +23,7 @@ use rustc_middle::ty::{ TypeVisitableExt, }; use rustc_session::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo}; -use rustc_span::sym; -use rustc_span::symbol::Symbol; +use rustc_span::{Symbol, sym}; use tracing::{debug, instrument, trace}; use {rustc_abi as abi, rustc_hir as hir}; @@ -1105,15 +1104,13 @@ fn variant_info_for_adt<'tcx>( }; match layout.variants { + Variants::Empty => (vec![], None), + Variants::Single { index } => { - if !adt_def.variants().is_empty() && layout.fields != FieldsShape::Primitive { - debug!("print-type-size `{:#?}` variant {}", layout, adt_def.variant(index).name); - let variant_def = &adt_def.variant(index); - let fields: Vec<_> = variant_def.fields.iter().map(|f| f.name).collect(); - (vec![build_variant_info(Some(variant_def.name), &fields, layout)], None) - } else { - (vec![], None) - } + debug!("print-type-size `{:#?}` variant {}", layout, adt_def.variant(index).name); + let variant_def = &adt_def.variant(index); + let fields: Vec<_> = variant_def.fields.iter().map(|f| f.name).collect(); + (vec![build_variant_info(Some(variant_def.name), &fields, layout)], None) } Variants::Multiple { tag, ref tag_encoding, .. } => { diff --git a/compiler/rustc_ty_utils/src/layout/invariant.rs b/compiler/rustc_ty_utils/src/layout/invariant.rs index f39b87622f4..8d5403ed324 100644 --- a/compiler/rustc_ty_utils/src/layout/invariant.rs +++ b/compiler/rustc_ty_utils/src/layout/invariant.rs @@ -241,63 +241,81 @@ pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayou check_layout_abi(cx, layout); - if let Variants::Multiple { variants, tag, tag_encoding, .. } = &layout.variants { - if let TagEncoding::Niche { niche_start, untagged_variant, niche_variants } = tag_encoding { - let niche_size = tag.size(cx); - assert!(*niche_start <= niche_size.unsigned_int_max()); - for (idx, variant) in variants.iter_enumerated() { - // Ensure all inhabited variants are accounted for. - if !variant.is_uninhabited() { - assert!(idx == *untagged_variant || niche_variants.contains(&idx)); - } - } + match &layout.variants { + Variants::Empty => { + assert!(layout.is_uninhabited()); } - for variant in variants.iter() { - // No nested "multiple". - assert_matches!(variant.variants, Variants::Single { .. }); - // Variants should have the same or a smaller size as the full thing, - // and same for alignment. - if variant.size > layout.size { - bug!( - "Type with size {} bytes has variant with size {} bytes: {layout:#?}", - layout.size.bytes(), - variant.size.bytes(), - ) + Variants::Single { index } => { + if let Some(variants) = layout.ty.variant_range(tcx) { + assert!(variants.contains(index)); + } else { + // Types without variants use `0` as dummy variant index. + assert!(index.as_u32() == 0); } - if variant.align.abi > layout.align.abi { - bug!( - "Type with alignment {} bytes has variant with alignment {} bytes: {layout:#?}", - layout.align.abi.bytes(), - variant.align.abi.bytes(), - ) - } - // Skip empty variants. - if variant.size == Size::ZERO || variant.fields.count() == 0 || variant.is_uninhabited() + } + Variants::Multiple { variants, tag, tag_encoding, .. } => { + if let TagEncoding::Niche { niche_start, untagged_variant, niche_variants } = + tag_encoding { - // These are never actually accessed anyway, so we can skip the coherence check - // for them. They also fail that check, since they have - // `Aggregate`/`Uninhabited` ABI even when the main type is - // `Scalar`/`ScalarPair`. (Note that sometimes, variants with fields have size - // 0, and sometimes, variants without fields have non-0 size.) - continue; + let niche_size = tag.size(cx); + assert!(*niche_start <= niche_size.unsigned_int_max()); + for (idx, variant) in variants.iter_enumerated() { + // Ensure all inhabited variants are accounted for. + if !variant.is_uninhabited() { + assert!(idx == *untagged_variant || niche_variants.contains(&idx)); + } + } } - // The top-level ABI and the ABI of the variants should be coherent. - let scalar_coherent = - |s1: Scalar, s2: Scalar| s1.size(cx) == s2.size(cx) && s1.align(cx) == s2.align(cx); - let abi_coherent = match (layout.backend_repr, variant.backend_repr) { - (BackendRepr::Scalar(s1), BackendRepr::Scalar(s2)) => scalar_coherent(s1, s2), - (BackendRepr::ScalarPair(a1, b1), BackendRepr::ScalarPair(a2, b2)) => { - scalar_coherent(a1, a2) && scalar_coherent(b1, b2) + for variant in variants.iter() { + // No nested "multiple". + assert_matches!(variant.variants, Variants::Single { .. }); + // Variants should have the same or a smaller size as the full thing, + // and same for alignment. + if variant.size > layout.size { + bug!( + "Type with size {} bytes has variant with size {} bytes: {layout:#?}", + layout.size.bytes(), + variant.size.bytes(), + ) + } + if variant.align.abi > layout.align.abi { + bug!( + "Type with alignment {} bytes has variant with alignment {} bytes: {layout:#?}", + layout.align.abi.bytes(), + variant.align.abi.bytes(), + ) + } + // Skip empty variants. + if variant.size == Size::ZERO + || variant.fields.count() == 0 + || variant.is_uninhabited() + { + // These are never actually accessed anyway, so we can skip the coherence check + // for them. They also fail that check, since they have + // `Aggregate`/`Uninhabited` ABI even when the main type is + // `Scalar`/`ScalarPair`. (Note that sometimes, variants with fields have size + // 0, and sometimes, variants without fields have non-0 size.) + continue; + } + // The top-level ABI and the ABI of the variants should be coherent. + let scalar_coherent = |s1: Scalar, s2: Scalar| { + s1.size(cx) == s2.size(cx) && s1.align(cx) == s2.align(cx) + }; + let abi_coherent = match (layout.backend_repr, variant.backend_repr) { + (BackendRepr::Scalar(s1), BackendRepr::Scalar(s2)) => scalar_coherent(s1, s2), + (BackendRepr::ScalarPair(a1, b1), BackendRepr::ScalarPair(a2, b2)) => { + scalar_coherent(a1, a2) && scalar_coherent(b1, b2) + } + (BackendRepr::Uninhabited, _) => true, + (BackendRepr::Memory { .. }, _) => true, + _ => false, + }; + if !abi_coherent { + bug!( + "Variant ABI is incompatible with top-level ABI:\nvariant={:#?}\nTop-level: {layout:#?}", + variant + ); } - (BackendRepr::Uninhabited, _) => true, - (BackendRepr::Memory { .. }, _) => true, - _ => false, - }; - if !abi_coherent { - bug!( - "Variant ABI is incompatible with top-level ABI:\nvariant={:#?}\nTop-level: {layout:#?}", - variant - ); } } } diff --git a/compiler/rustc_type_ir/src/elaborate.rs b/compiler/rustc_type_ir/src/elaborate.rs index 0d574445df8..923b74abdfd 100644 --- a/compiler/rustc_type_ir/src/elaborate.rs +++ b/compiler/rustc_type_ir/src/elaborate.rs @@ -72,7 +72,7 @@ impl<I: Interner> Elaboratable<I> for ClauseWithSupertraitSpan<I> { _parent_trait_pred: crate::Binder<I, crate::TraitPredicate<I>>, _index: usize, ) -> Self { - ClauseWithSupertraitSpan { pred: clause.as_predicate(), supertrait_span: supertrait_span } + ClauseWithSupertraitSpan { pred: clause.as_predicate(), supertrait_span } } } diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index f45c94127bd..2db40accda3 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -543,7 +543,7 @@ pub trait AdtDef<I: Interner>: Copy + Debug + Hash + Eq { } pub trait ParamEnv<I: Interner>: Copy + Debug + Hash + Eq + TypeFoldable<I> { - fn caller_bounds(self) -> impl IntoIterator<Item = I::Clause>; + fn caller_bounds(self) -> impl SliceLike<Item = I::Clause>; } pub trait Features<I: Interner>: Copy { diff --git a/compiler/stable_mir/src/abi.rs b/compiler/stable_mir/src/abi.rs index 2ac30b5aff1..17e6a852022 100644 --- a/compiler/stable_mir/src/abi.rs +++ b/compiler/stable_mir/src/abi.rs @@ -180,6 +180,9 @@ impl FieldsShape { #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] pub enum VariantsShape { + /// A type with no valid variants. Must be uninhabited. + Empty, + /// Single enum variants, structs/tuples, unions, and all non-ADTs. Single { index: VariantIdx }, |
