diff options
Diffstat (limited to 'compiler')
560 files changed, 11735 insertions, 8245 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 2f55a9eaeda..c0b96f50b60 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::*; @@ -627,9 +627,11 @@ impl Pat { | PatKind::Or(s) => s.iter().for_each(|p| p.walk(it)), // Trivial wrappers over inner patterns. - PatKind::Box(s) | PatKind::Deref(s) | PatKind::Ref(s, _) | PatKind::Paren(s) => { - s.walk(it) - } + PatKind::Box(s) + | PatKind::Deref(s) + | PatKind::Ref(s, _) + | PatKind::Paren(s) + | PatKind::Guard(s, _) => s.walk(it), // These patterns do not contain subpatterns, skip. PatKind::Wild @@ -839,6 +841,9 @@ pub enum PatKind { // A never pattern `!`. Never, + /// A guard pattern (e.g., `x if guard(x)`). + Guard(P<Pat>, P<Expr>), + /// Parentheses in patterns used for grouping (i.e., `(PAT)`). Paren(P<Pat>), @@ -854,6 +859,8 @@ pub enum PatKind { pub enum PatFieldsRest { /// `module::StructName { field, ..}` Rest, + /// `module::StructName { field, syntax error }` + Recovered(ErrorGuaranteed), /// `module::StructName { field }` None, } @@ -1377,6 +1384,7 @@ impl Expr { | ExprKind::Tup(_) | ExprKind::Type(..) | ExprKind::Underscore + | ExprKind::UnsafeBinderCast(..) | ExprKind::While(..) | ExprKind::Err(_) | ExprKind::Dummy => ExprPrecedence::Unambiguous, @@ -1504,7 +1512,13 @@ pub enum ExprKind { /// `'label: for await? pat in iter { block }` /// /// This is desugared to a combination of `loop` and `match` expressions. - ForLoop { pat: P<Pat>, iter: P<Expr>, body: P<Block>, label: Option<Label>, kind: ForLoopKind }, + ForLoop { + pat: P<Pat>, + iter: P<Expr>, + body: P<Block>, + label: Option<Label>, + kind: ForLoopKind, + }, /// Conditionless loop (can be exited with `break`, `continue`, or `return`). /// /// `'label: loop { block }` @@ -1609,6 +1623,8 @@ pub enum ExprKind { /// A `format_args!()` expression. FormatArgs(P<FormatArgs>), + UnsafeBinderCast(UnsafeBinderCastKind, P<Expr>, Option<P<Ty>>), + /// Placeholder for an expression that wasn't syntactically well formed in some way. Err(ErrorGuaranteed), @@ -1647,6 +1663,16 @@ impl GenBlockKind { } } +/// Whether we're unwrapping or wrapping an unsafe binder +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Encodable, Decodable, HashStable_Generic)] +pub enum UnsafeBinderCastKind { + // e.g. `&i32` -> `unsafe<'a> &'a i32` + Wrap, + // e.g. `unsafe<'a> &'a i32` -> `&i32` + Unwrap, +} + /// The explicit `Self` type in a "qualified path". The actual /// path, including the trait and the associated item, is stored /// separately. `position` represents the index of the associated @@ -1734,53 +1760,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)), } } @@ -1790,27 +1779,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), } } } @@ -2218,6 +2187,12 @@ pub struct BareFnTy { pub decl_span: Span, } +#[derive(Clone, Encodable, Decodable, Debug)] +pub struct UnsafeBinderTy { + pub generic_params: ThinVec<GenericParam>, + pub inner_ty: P<Ty>, +} + /// The various kinds of type recognized by the compiler. // // Adding a new variant? Please update `test_ty` in `tests/ui/macros/stringify.rs`. @@ -2237,6 +2212,8 @@ pub enum TyKind { PinnedRef(Option<Lifetime>, MutTy), /// A bare function (e.g., `fn(usize) -> bool`). BareFn(P<BareFnTy>), + /// An unsafe existential lifetime binder (e.g., `unsafe<'a> &'a ()`). + UnsafeBinder(P<UnsafeBinderTy>), /// The never type (`!`). Never, /// A tuple (`(A, B, C, D,...)`). @@ -2872,7 +2849,7 @@ pub enum ModKind { /// or with definition outlined to a separate file `mod foo;` and already loaded from it. /// The inner span is from the first token past `{` to the last token until `}`, /// or from the first to the last token in the loaded file. - Loaded(ThinVec<P<Item>>, Inline, ModSpans), + Loaded(ThinVec<P<Item>>, Inline, ModSpans, Result<(), ErrorGuaranteed>), /// Module with definition outlined to a separate file `mod foo;` but not yet loaded from it. Unloaded, } @@ -3019,7 +2996,7 @@ impl NormalAttr { } } -#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct AttrItem { pub unsafety: Safety, pub path: Path, @@ -3114,6 +3091,7 @@ pub struct FieldDef { pub ident: Option<Ident>, pub ty: P<Ty>, + pub default: Option<AnonConst>, pub is_placeholder: bool, } diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 0d79cadef34..8ee3d4d590c 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -1,5 +1,6 @@ //! Functions dealing with attributes and meta items. +use std::fmt::Debug; use std::iter; use std::sync::atomic::{AtomicU32, Ordering}; @@ -10,9 +11,9 @@ 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}; @@ -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> { @@ -439,7 +437,8 @@ impl MetaItem { } impl MetaItemKind { - fn list_from_tokens(tokens: TokenStream) -> Option<ThinVec<MetaItemInner>> { + // public because it can be called in the hir + pub fn list_from_tokens(tokens: TokenStream) -> Option<ThinVec<MetaItemInner>> { let mut tokens = tokens.trees().peekable(); let mut result = ThinVec::new(); while tokens.peek().is_some() { @@ -492,7 +491,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 +500,6 @@ impl MetaItemKind { } _ => None, }, - AttrArgs::Eq { value: AttrArgsEq::Hir(lit), .. } => { - Some(MetaItemKind::NameValue(lit.clone())) - } } } } @@ -704,26 +700,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..fffcb3ef988 100644 --- a/compiler/rustc_ast/src/entry.rs +++ b/compiler/rustc_ast/src/entry.rs @@ -1,7 +1,7 @@ use rustc_span::Symbol; use rustc_span::symbol::sym; -use crate::{Attribute, attr}; +use crate::attr::{self, AttributeExt}; #[derive(Debug)] pub enum EntryPointType { @@ -37,7 +37,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/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 622c260868e..f07266a3e2d 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -451,8 +451,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); } } @@ -558,6 +558,11 @@ pub fn walk_ty<T: MutVisitor>(vis: &mut T, ty: &mut P<Ty>) { vis.visit_fn_decl(decl); vis.visit_span(decl_span); } + TyKind::UnsafeBinder(binder) => { + let UnsafeBinderTy { generic_params, inner_ty } = binder.deref_mut(); + generic_params.flat_map_in_place(|param| vis.flat_map_generic_param(param)); + vis.visit_ty(inner_ty); + } TyKind::Tup(tys) => visit_thin_vec(tys, |ty| vis.visit_ty(ty)), TyKind::Paren(ty) => vis.visit_ty(ty), TyKind::Pat(ty, pat) => { @@ -1120,13 +1125,14 @@ fn walk_poly_trait_ref<T: MutVisitor>(vis: &mut T, p: &mut PolyTraitRef) { } pub fn walk_field_def<T: MutVisitor>(visitor: &mut T, fd: &mut FieldDef) { - let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _, safety } = fd; + let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _, safety, default } = fd; visitor.visit_id(id); visit_attrs(visitor, attrs); visitor.visit_vis(vis); visit_safety(visitor, safety); visit_opt(ident, |ident| visitor.visit_ident(ident)); visitor.visit_ty(ty); + visit_opt(default, |default| visitor.visit_anon_const(default)); visitor.visit_span(span); } @@ -1211,7 +1217,12 @@ impl WalkItemKind for ItemKind { ItemKind::Mod(safety, mod_kind) => { visit_safety(vis, safety); match mod_kind { - ModKind::Loaded(items, _inline, ModSpans { inner_span, inject_use_span }) => { + ModKind::Loaded( + items, + _inline, + ModSpans { inner_span, inject_use_span }, + _, + ) => { items.flat_map_in_place(|item| vis.flat_map_item(item)); vis.visit_span(inner_span); vis.visit_span(inject_use_span); @@ -1525,6 +1536,10 @@ pub fn walk_pat<T: MutVisitor>(vis: &mut T, pat: &mut P<Pat>) { visit_opt(e2, |e| vis.visit_expr(e)); vis.visit_span(span); } + PatKind::Guard(p, e) => { + vis.visit_pat(p); + vis.visit_expr(e); + } PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => { visit_thin_vec(elems, |elem| vis.visit_pat(elem)) } @@ -1770,6 +1785,12 @@ pub fn walk_expr<T: MutVisitor>(vis: &mut T, Expr { kind, id, span, attrs, token ExprKind::TryBlock(body) => vis.visit_block(body), ExprKind::Lit(_token) => {} ExprKind::IncludedBytes(_bytes) => {} + ExprKind::UnsafeBinderCast(_kind, expr, ty) => { + vis.visit_expr(expr); + if let Some(ty) = ty { + vis.visit_ty(ty); + } + } ExprKind::Err(_guar) => {} ExprKind::Dummy => {} } diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs index ae1ca36a3ba..64f2a98b8a6 100644 --- a/compiler/rustc_ast/src/util/classify.rs +++ b/compiler/rustc_ast/src/util/classify.rs @@ -152,6 +152,7 @@ pub fn leading_labeled_expr(mut expr: &ast::Expr) -> bool { | Underscore | Yeet(..) | Yield(..) + | UnsafeBinderCast(..) | Err(..) | Dummy => return false, } @@ -232,6 +233,7 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<TrailingBrace<'_>> { | Paren(_) | Try(_) | Yeet(None) + | UnsafeBinderCast(..) | Err(_) | Dummy => break None, } @@ -253,6 +255,10 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> { ty = &mut_ty.ty; } + ast::TyKind::UnsafeBinder(binder) => { + ty = &binder.inner_ty; + } + ast::TyKind::BareFn(fn_ty) => match &fn_ty.decl.output { ast::FnRetTy::Default(_) => break None, ast::FnRetTy::Ty(ret) => ty = ret, diff --git a/compiler/rustc_ast/src/util/comments/tests.rs b/compiler/rustc_ast/src/util/comments/tests.rs index 61bb2468e79..f88b534a0c1 100644 --- a/compiler/rustc_ast/src/util/comments/tests.rs +++ b/compiler/rustc_ast/src/util/comments/tests.rs @@ -1,3 +1,5 @@ +#![cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] + use rustc_span::create_default_session_globals_then; use super::*; diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 2f6998783fa..211d13659ee 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -380,7 +380,7 @@ impl WalkItemKind for ItemKind { try_visit!(visitor.visit_fn(kind, span, id)); } ItemKind::Mod(_unsafety, mod_kind) => match mod_kind { - ModKind::Loaded(items, _inline, _inner_span) => { + ModKind::Loaded(items, _inline, _inner_span, _) => { walk_list!(visitor, visit_item, items); } ModKind::Unloaded => {} @@ -522,6 +522,10 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) -> V::Result { walk_list!(visitor, visit_generic_param, generic_params); try_visit!(visitor.visit_fn_decl(decl)); } + TyKind::UnsafeBinder(binder) => { + walk_list!(visitor, visit_generic_param, &binder.generic_params); + try_visit!(visitor.visit_ty(&binder.inner_ty)); + } TyKind::Path(maybe_qself, path) => { try_visit!(visitor.visit_qself(maybe_qself)); try_visit!(visitor.visit_path(path, *id)); @@ -682,6 +686,10 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) -> V::Res visit_opt!(visitor, visit_expr, lower_bound); visit_opt!(visitor, visit_expr, upper_bound); } + PatKind::Guard(subpattern, guard_condition) => { + try_visit!(visitor.visit_pat(subpattern)); + try_visit!(visitor.visit_expr(guard_condition)); + } PatKind::Wild | PatKind::Rest | PatKind::Never => {} PatKind::Err(_guar) => {} PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => { @@ -971,11 +979,13 @@ pub fn walk_struct_def<'a, V: Visitor<'a>>( } pub fn walk_field_def<'a, V: Visitor<'a>>(visitor: &mut V, field: &'a FieldDef) -> V::Result { - let FieldDef { attrs, id: _, span: _, vis, ident, ty, is_placeholder: _, safety: _ } = field; + let FieldDef { attrs, id: _, span: _, vis, ident, ty, is_placeholder: _, safety: _, default } = + field; walk_list!(visitor, visit_attribute, attrs); try_visit!(visitor.visit_vis(vis)); visit_opt!(visitor, visit_ident, ident); try_visit!(visitor.visit_ty(ty)); + visit_opt!(visitor, visit_anon_const, &*default); V::Result::output() } @@ -1220,6 +1230,10 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V ExprKind::TryBlock(body) => try_visit!(visitor.visit_block(body)), ExprKind::Lit(_token) => {} ExprKind::IncludedBytes(_bytes) => {} + ExprKind::UnsafeBinderCast(_kind, expr, ty) => { + try_visit!(visitor.visit_expr(expr)); + visit_opt!(visitor, visit_ty, ty); + } ExprKind::Err(_guar) => {} ExprKind::Dummy => {} } @@ -1273,7 +1287,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/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl index a4dbf981115..f96c9fe8e32 100644 --- a/compiler/rustc_ast_lowering/messages.ftl +++ b/compiler/rustc_ast_lowering/messages.ftl @@ -45,10 +45,6 @@ ast_lowering_bad_return_type_notation_output = ast_lowering_bad_return_type_notation_position = return type notation not allowed in this position yet -ast_lowering_base_expression_double_dot = - base expression required after `..` - .suggestion = add a base expression here - ast_lowering_clobber_abi_not_supported = `clobber_abi` is not supported on this target @@ -57,6 +53,9 @@ ast_lowering_closure_cannot_be_static = closures cannot be static ast_lowering_coroutine_too_many_parameters = too many parameters for a coroutine (expected 0 or 1 parameters) +ast_lowering_default_field_in_tuple = default fields are not supported in tuple structs + .label = default fields are only supported on structs + ast_lowering_does_not_support_modifiers = the `{$class_name}` register class does not support template modifiers diff --git a/compiler/rustc_ast_lowering/src/block.rs b/compiler/rustc_ast_lowering/src/block.rs index 20d3ce65fac..88ce6f80e10 100644 --- a/compiler/rustc_ast_lowering/src/block.rs +++ b/compiler/rustc_ast_lowering/src/block.rs @@ -1,5 +1,6 @@ use rustc_ast::{Block, BlockCheckMode, Local, LocalKind, Stmt, StmtKind}; use rustc_hir as hir; +use rustc_span::sym; use smallvec::SmallVec; use crate::{ImplTraitContext, ImplTraitPosition, LoweringContext}; @@ -82,11 +83,21 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { (self.arena.alloc_from_iter(stmts), expr) } + /// Return an `ImplTraitContext` that allows impl trait in bindings if + /// the feature gate is enabled, or issues a feature error if it is not. + fn impl_trait_in_bindings_ctxt(&self, position: ImplTraitPosition) -> ImplTraitContext { + if self.tcx.features().impl_trait_in_bindings() { + ImplTraitContext::InBinding + } else { + ImplTraitContext::FeatureGated(position, sym::impl_trait_in_bindings) + } + } + fn lower_local(&mut self, l: &Local) -> &'hir hir::LetStmt<'hir> { - let ty = l - .ty - .as_ref() - .map(|t| self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Variable))); + // Let statements are allowed to have impl trait in bindings. + let ty = l.ty.as_ref().map(|t| { + self.lower_ty(t, self.impl_trait_in_bindings_ctxt(ImplTraitPosition::Variable)) + }); let init = l.kind.init().map(|init| self.lower_expr(init)); let hir_id = self.lower_node_id(l.id); let pat = self.lower_pat(&l.pat); diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs index 447af57354f..2564d4e2772 100644 --- a/compiler/rustc_ast_lowering/src/errors.rs +++ b/compiler/rustc_ast_lowering/src/errors.rs @@ -38,6 +38,14 @@ pub(crate) struct InvalidAbi { pub suggestion: Option<InvalidAbiSuggestion>, } +#[derive(Diagnostic)] +#[diag(ast_lowering_default_field_in_tuple)] +pub(crate) struct TupleStructWithDefault { + #[primary_span] + #[label] + pub span: Span, +} + pub(crate) struct InvalidAbiReason(pub &'static str); impl Subdiagnostic for InvalidAbiReason { @@ -115,14 +123,6 @@ pub(crate) struct UnderscoreExprLhsAssign { } #[derive(Diagnostic)] -#[diag(ast_lowering_base_expression_double_dot, code = E0797)] -pub(crate) struct BaseExpressionDoubleDot { - #[primary_span] - #[suggestion(code = "/* expr */", applicability = "has-placeholders", style = "verbose")] - pub span: Span, -} - -#[derive(Diagnostic)] #[diag(ast_lowering_await_only_in_async_fn_and_blocks, code = E0728)] pub(crate) struct AwaitOnlyInAsyncFnAndBlocks { #[primary_span] diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 84e648f4923..32905806343 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -19,10 +19,10 @@ use thin_vec::{ThinVec, thin_vec}; use visit::{Visitor, walk_expr}; use super::errors::{ - AsyncCoroutinesNotSupported, AwaitOnlyInAsyncFnAndBlocks, BaseExpressionDoubleDot, - ClosureCannotBeStatic, CoroutineTooManyParameters, - FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd, MatchArmWithNoBody, - NeverPatternWithBody, NeverPatternWithGuard, UnderscoreExprLhsAssign, + AsyncCoroutinesNotSupported, AwaitOnlyInAsyncFnAndBlocks, ClosureCannotBeStatic, + CoroutineTooManyParameters, FunctionalRecordUpdateDestructuringAssignment, + InclusiveRangeWithNoEnd, MatchArmWithNoBody, NeverPatternWithBody, NeverPatternWithGuard, + UnderscoreExprLhsAssign, }; use super::{ GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode, ResolverAstLoweringExt, @@ -357,12 +357,9 @@ impl<'hir> LoweringContext<'_, 'hir> { ), ExprKind::Struct(se) => { let rest = match &se.rest { - StructRest::Base(e) => Some(self.lower_expr(e)), - StructRest::Rest(sp) => { - let guar = self.dcx().emit_err(BaseExpressionDoubleDot { span: *sp }); - Some(&*self.arena.alloc(self.expr_err(*sp, guar))) - } - StructRest::None => None, + StructRest::Base(e) => hir::StructTailExpr::Base(self.lower_expr(e)), + StructRest::Rest(sp) => hir::StructTailExpr::DefaultFields(*sp), + StructRest::None => hir::StructTailExpr::None, }; hir::ExprKind::Struct( self.arena.alloc(self.lower_qpath( @@ -382,6 +379,14 @@ impl<'hir> LoweringContext<'_, 'hir> { ExprKind::Yield(opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()), ExprKind::Err(guar) => hir::ExprKind::Err(*guar), + ExprKind::UnsafeBinderCast(kind, expr, ty) => hir::ExprKind::UnsafeBinderCast( + *kind, + self.lower_expr(expr), + ty.as_ref().map(|ty| { + self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Cast)) + }), + ), + ExprKind::Dummy => { span_bug!(e.span, "lowered ExprKind::Dummy") } @@ -1526,7 +1531,7 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ExprKind::Struct( self.arena.alloc(hir::QPath::LangItem(lang_item, self.lower_span(span))), fields, - None, + hir::StructTailExpr::None, ) } diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index fb09f1c7fee..ad4410c57dd 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -17,7 +17,10 @@ use smallvec::{SmallVec, smallvec}; use thin_vec::ThinVec; use tracing::instrument; -use super::errors::{InvalidAbi, InvalidAbiReason, InvalidAbiSuggestion, MisplacedRelaxTraitBound}; +use super::errors::{ + InvalidAbi, InvalidAbiReason, InvalidAbiSuggestion, MisplacedRelaxTraitBound, + TupleStructWithDefault, +}; use super::{ AstOwner, FnDeclKind, ImplTraitContext, ImplTraitPosition, LoweringContext, ParamMode, ResolverAstLoweringExt, @@ -173,7 +176,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> { @@ -235,7 +238,7 @@ impl<'hir> LoweringContext<'_, 'hir> { }) } ItemKind::Mod(_, mod_kind) => match mod_kind { - ModKind::Loaded(items, _, spans) => { + ModKind::Loaded(items, _, spans, _) => { hir::ItemKind::Mod(self.lower_mod(items, spans)) } ModKind::Unloaded => panic!("`mod` items should have been loaded by now"), @@ -464,7 +467,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(); @@ -690,13 +693,27 @@ impl<'hir> LoweringContext<'_, 'hir> { VariantData::Tuple(fields, id) => { let ctor_id = self.lower_node_id(*id); self.alias_attrs(ctor_id, parent_id); - hir::VariantData::Tuple( - self.arena.alloc_from_iter( - fields.iter().enumerate().map(|f| self.lower_field_def(f)), - ), - ctor_id, - self.local_def_id(*id), - ) + let fields = self + .arena + .alloc_from_iter(fields.iter().enumerate().map(|f| self.lower_field_def(f))); + for field in &fields[..] { + if let Some(default) = field.default { + // Default values in tuple struct and tuple variants are not allowed by the + // RFC due to concerns about the syntax, both in the item definition and the + // expression. We could in the future allow `struct S(i32 = 0);` and force + // users to construct the value with `let _ = S { .. };`. + if self.tcx.features().default_field_values() { + self.dcx().emit_err(TupleStructWithDefault { span: default.span }); + } else { + let _ = self.dcx().span_delayed_bug( + default.span, + "expected `default values on `struct` fields aren't supported` \ + feature-gate error but none was produced", + ); + } + } + } + hir::VariantData::Tuple(fields, ctor_id, self.local_def_id(*id)) } VariantData::Unit(id) => { let ctor_id = self.lower_node_id(*id); @@ -723,6 +740,7 @@ impl<'hir> LoweringContext<'_, 'hir> { None => Ident::new(sym::integer(index), self.lower_span(f.span)), }, vis_span: self.lower_span(f.vis.span), + default: f.default.as_ref().map(|v| self.lower_anon_const_to_anon_const(v)), ty, safety: self.lower_safety(f.safety, hir::Safety::Safe), } @@ -1374,7 +1392,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 bac3f974cca..41fa4c13442 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; @@ -96,7 +95,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>)>, @@ -260,6 +259,13 @@ enum ImplTraitContext { /// equivalent to a new opaque type like `type T = impl Debug; fn foo() -> T`. /// OpaqueTy { origin: hir::OpaqueTyOrigin<LocalDefId> }, + + /// Treat `impl Trait` as a "trait ascription", which is like a type + /// variable but that also enforces that a set of trait goals hold. + /// + /// This is useful to guide inference for unnameable types. + InBinding, + /// `impl Trait` is unstably accepted in this position. FeatureGated(ImplTraitPosition, Symbol), /// `impl Trait` is not accepted in this position. @@ -840,7 +846,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 { @@ -852,25 +858,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) { @@ -882,15 +896,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 @@ -906,7 +919,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 } } } } @@ -1228,6 +1241,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { param_names: self.lower_fn_params_to_names(&f.decl), })) } + TyKind::UnsafeBinder(f) => { + let generic_params = self.lower_lifetime_binder(t.id, &f.generic_params); + hir::TyKind::UnsafeBinder(self.arena.alloc(hir::UnsafeBinderTy { + generic_params, + inner_ty: self.lower_ty(&f.inner_ty, itctx), + })) + } TyKind::Never => hir::TyKind::Never, TyKind::Tup(tys) => hir::TyKind::Tup( self.arena.alloc_from_iter(tys.iter().map(|ty| self.lower_ty_direct(ty, itctx))), @@ -1320,6 +1340,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } path } + ImplTraitContext::InBinding => { + hir::TyKind::TraitAscription(self.lower_param_bounds(bounds, itctx)) + } ImplTraitContext::FeatureGated(position, feature) => { let guar = self .tcx @@ -2184,7 +2207,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 ace7bfb5c73..60660548590 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -92,7 +92,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"); @@ -114,6 +121,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.lower_range_end(end, e2.is_some()), ); } + // FIXME(guard_patterns): lower pattern guards to HIR + PatKind::Guard(inner, _) => pattern = inner, PatKind::Slice(pats) => break self.lower_pat_slice(pats), PatKind::Rest => { // If we reach here the `..` pattern is not semantically allowed. 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 64e91c91e2c..3f071f5ecb5 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -342,7 +342,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() { @@ -1029,7 +1029,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.dcx().emit_err(errors::UnsafeItem { span, kind: "module" }); } // Ensure that `path` attributes on modules are recorded as used (cf. issue #35584). - if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _)) + if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _, _)) && !attr::contains_name(&item.attrs, sym::path) { self.check_mod_file_item_asciionly(item.ident); diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 89311516081..c10b3296497 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -512,11 +512,6 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { ); gate_all!(let_chains, "`let` expressions in this position are unstable"); gate_all!( - async_closure, - "async closures are unstable", - "to use an async block, remove the `||`: `async {`" - ); - gate_all!( async_trait_bounds, "`async` trait bounds are unstable", "use the desugared name of the async trait, such as `AsyncFn`" @@ -556,6 +551,8 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(builtin_syntax, "`builtin #` syntax is unstable"); gate_all!(explicit_tail_calls, "`become` expression is experimental"); gate_all!(generic_const_items, "generic const items are experimental"); + gate_all!(guard_patterns, "guard patterns are experimental", "consider using match arm guards"); + gate_all!(default_field_values, "default values on fields are experimental"); gate_all!(fn_delegation, "functions delegation is not yet fully implemented"); gate_all!(postfix_match, "postfix match is experimental"); gate_all!(mut_ref, "mutable by-reference bindings are experimental"); @@ -563,6 +560,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(return_type_notation, "return type notation is experimental"); gate_all!(pin_ergonomics, "pinned reference syntax is experimental"); gate_all!(unsafe_fields, "`unsafe` fields are experimental"); + gate_all!(unsafe_binders, "unsafe binder types are experimental"); if !visitor.features.never_patterns() { if let Some(spans) = spans.get(&sym::never_patterns) { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 479677b0a5a..cbb92c8c30f 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -17,9 +17,9 @@ 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; @@ -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(), @@ -1198,6 +1191,14 @@ impl<'a> State<'a> { ast::TyKind::BareFn(f) => { self.print_ty_fn(f.ext, f.safety, &f.decl, None, &f.generic_params); } + ast::TyKind::UnsafeBinder(f) => { + self.ibox(INDENT_UNIT); + self.word("unsafe"); + self.print_generic_params(&f.generic_params); + self.nbsp(); + self.print_type(&f.inner_ty); + self.end(); + } ast::TyKind::Path(None, path) => { self.print_path(path, false, 0); } @@ -1653,11 +1654,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(); @@ -1709,6 +1713,14 @@ impl<'a> State<'a> { self.print_expr(e, FixupContext::default()); } } + PatKind::Guard(subpat, condition) => { + self.popen(); + self.print_pat(subpat); + self.space(); + self.word_space("if"); + self.print_expr(condition, FixupContext::default()); + self.pclose(); + } PatKind::Slice(elts) => { self.word("["); self.commasep(Inconsistent, elts, |s, p| s.print_pat(p)); diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index c239cb249c3..dce76fb1e77 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -772,6 +772,25 @@ impl<'a> State<'a> { self.word_nbsp("try"); self.print_block_with_attrs(blk, attrs) } + ast::ExprKind::UnsafeBinderCast(kind, expr, ty) => { + self.word("builtin # "); + match kind { + ast::UnsafeBinderCastKind::Wrap => self.word("wrap_binder"), + ast::UnsafeBinderCastKind::Unwrap => self.word("unwrap_binder"), + } + self.popen(); + self.ibox(0); + self.print_expr(expr, FixupContext::default()); + + if let Some(ty) = ty { + self.word(","); + self.space(); + self.print_type(ty); + } + + self.end(); + self.pclose(); + } ast::ExprKind::Err(_) => { self.popen(); self.word("/*ERROR*/"); 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..b9f841800ab --- /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::{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..8cf8e86100f --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -0,0 +1,254 @@ +//! 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; +use rustc_span::symbol::{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..672fcf05eda --- /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::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..2f9872cc311 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -0,0 +1,149 @@ +//! 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; +use rustc_span::symbol::{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..008edfe6366 --- /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::{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..0d5bd105f05 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -0,0 +1,385 @@ +//! 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; +use rustc_span::symbol::{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..c59d2fec1dc --- /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::{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/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs index 16b3d901956..ff838fbbb88 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs @@ -34,6 +34,25 @@ pub struct BorrowSet<'tcx> { pub(crate) locals_state_at_exit: LocalsStateAtExit, } +// These methods are public to support borrowck consumers. +impl<'tcx> BorrowSet<'tcx> { + pub fn location_map(&self) -> &FxIndexMap<Location, BorrowData<'tcx>> { + &self.location_map + } + + pub fn activation_map(&self) -> &FxIndexMap<Location, Vec<BorrowIndex>> { + &self.activation_map + } + + pub fn local_map(&self) -> &FxIndexMap<mir::Local, FxIndexSet<BorrowIndex>> { + &self.local_map + } + + pub fn locals_state_at_exit(&self) -> &LocalsStateAtExit { + &self.locals_state_at_exit + } +} + impl<'tcx> Index<BorrowIndex> for BorrowSet<'tcx> { type Output = BorrowData<'tcx>; @@ -45,7 +64,7 @@ impl<'tcx> Index<BorrowIndex> for BorrowSet<'tcx> { /// Location where a two-phase borrow is activated, if a borrow /// is in fact a two-phase borrow. #[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub(crate) enum TwoPhaseActivation { +pub enum TwoPhaseActivation { NotTwoPhase, NotActivated, ActivatedAt(Location), @@ -68,6 +87,33 @@ pub struct BorrowData<'tcx> { pub(crate) assigned_place: mir::Place<'tcx>, } +// These methods are public to support borrowck consumers. +impl<'tcx> BorrowData<'tcx> { + pub fn reserve_location(&self) -> Location { + self.reserve_location + } + + pub fn activation_location(&self) -> TwoPhaseActivation { + self.activation_location + } + + pub fn kind(&self) -> mir::BorrowKind { + self.kind + } + + pub fn region(&self) -> RegionVid { + self.region + } + + pub fn borrowed_place(&self) -> mir::Place<'tcx> { + self.borrowed_place + } + + pub fn assigned_place(&self) -> mir::Place<'tcx> { + self.assigned_place + } +} + impl<'tcx> fmt::Display for BorrowData<'tcx> { fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result { let kind = match self.kind { @@ -120,7 +166,7 @@ impl LocalsStateAtExit { } impl<'tcx> BorrowSet<'tcx> { - pub(crate) fn build( + pub fn build( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, locals_are_invalidated_at_exit: bool, diff --git a/compiler/rustc_borrowck/src/consumers.rs b/compiler/rustc_borrowck/src/consumers.rs index 7ace38c3e85..74de766ba23 100644 --- a/compiler/rustc_borrowck/src/consumers.rs +++ b/compiler/rustc_borrowck/src/consumers.rs @@ -5,15 +5,15 @@ use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::{Body, Promoted}; use rustc_middle::ty::TyCtxt; +pub use super::borrow_set::{BorrowData, BorrowSet, TwoPhaseActivation}; pub use super::constraints::OutlivesConstraint; pub use super::dataflow::{BorrowIndex, Borrows, calculate_borrows_out_of_scope_at_location}; -pub use super::facts::{AllFacts as PoloniusInput, RustcFacts}; +pub use super::facts::{AllFacts as PoloniusInput, PoloniusRegionVid, RustcFacts}; pub use super::location::{LocationTable, RichLocation}; pub use super::nll::PoloniusOutput; pub use super::place_ext::PlaceExt; pub use super::places_conflict::{PlaceConflictBias, places_conflict}; pub use super::region_infer::RegionInferenceContext; -use crate::borrow_set::BorrowSet; /// Options determining the output behavior of [`get_body_with_borrowck_facts`]. /// diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 452038bc328..dc4eab766c9 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -8,7 +8,10 @@ use rustc_middle::mir::{ }; use rustc_middle::ty::{RegionVid, TyCtxt}; use rustc_mir_dataflow::fmt::DebugWithContext; -use rustc_mir_dataflow::impls::{EverInitializedPlaces, MaybeUninitializedPlaces}; +use rustc_mir_dataflow::impls::{ + EverInitializedPlaces, EverInitializedPlacesDomain, MaybeUninitializedPlaces, + MaybeUninitializedPlacesDomain, +}; use rustc_mir_dataflow::{Analysis, GenKill, JoinSemiLattice, SwitchIntEdgeEffects}; use tracing::debug; @@ -24,7 +27,7 @@ pub(crate) struct Borrowck<'a, 'tcx> { } impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> { - type Domain = BorrowckDomain<'a, 'tcx>; + type Domain = BorrowckDomain; const NAME: &'static str = "borrowck"; @@ -41,48 +44,48 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> { unreachable!(); } - fn apply_before_statement_effect( + fn apply_early_statement_effect( &mut self, state: &mut Self::Domain, stmt: &mir::Statement<'tcx>, loc: Location, ) { - self.borrows.apply_before_statement_effect(&mut state.borrows, stmt, loc); - self.uninits.apply_before_statement_effect(&mut state.uninits, stmt, loc); - self.ever_inits.apply_before_statement_effect(&mut state.ever_inits, stmt, loc); + self.borrows.apply_early_statement_effect(&mut state.borrows, stmt, loc); + self.uninits.apply_early_statement_effect(&mut state.uninits, stmt, loc); + self.ever_inits.apply_early_statement_effect(&mut state.ever_inits, stmt, loc); } - fn apply_statement_effect( + fn apply_primary_statement_effect( &mut self, state: &mut Self::Domain, stmt: &mir::Statement<'tcx>, loc: Location, ) { - self.borrows.apply_statement_effect(&mut state.borrows, stmt, loc); - self.uninits.apply_statement_effect(&mut state.uninits, stmt, loc); - self.ever_inits.apply_statement_effect(&mut state.ever_inits, stmt, loc); + self.borrows.apply_primary_statement_effect(&mut state.borrows, stmt, loc); + self.uninits.apply_primary_statement_effect(&mut state.uninits, stmt, loc); + self.ever_inits.apply_primary_statement_effect(&mut state.ever_inits, stmt, loc); } - fn apply_before_terminator_effect( + fn apply_early_terminator_effect( &mut self, state: &mut Self::Domain, term: &mir::Terminator<'tcx>, loc: Location, ) { - self.borrows.apply_before_terminator_effect(&mut state.borrows, term, loc); - self.uninits.apply_before_terminator_effect(&mut state.uninits, term, loc); - self.ever_inits.apply_before_terminator_effect(&mut state.ever_inits, term, loc); + self.borrows.apply_early_terminator_effect(&mut state.borrows, term, loc); + self.uninits.apply_early_terminator_effect(&mut state.uninits, term, loc); + self.ever_inits.apply_early_terminator_effect(&mut state.ever_inits, term, loc); } - fn apply_terminator_effect<'mir>( + fn apply_primary_terminator_effect<'mir>( &mut self, state: &mut Self::Domain, term: &'mir mir::Terminator<'tcx>, loc: Location, ) -> TerminatorEdges<'mir, 'tcx> { - self.borrows.apply_terminator_effect(&mut state.borrows, term, loc); - self.uninits.apply_terminator_effect(&mut state.uninits, term, loc); - self.ever_inits.apply_terminator_effect(&mut state.ever_inits, term, loc); + self.borrows.apply_primary_terminator_effect(&mut state.borrows, term, loc); + self.uninits.apply_primary_terminator_effect(&mut state.uninits, term, loc); + self.ever_inits.apply_primary_terminator_effect(&mut state.ever_inits, term, loc); // This return value doesn't matter. It's only used by `iterate_to_fixpoint`, which this // analysis doesn't use. @@ -110,14 +113,14 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> { } } -impl JoinSemiLattice for BorrowckDomain<'_, '_> { +impl JoinSemiLattice for BorrowckDomain { fn join(&mut self, _other: &Self) -> bool { // This is only reachable from `iterate_to_fixpoint`, which this analysis doesn't use. unreachable!(); } } -impl<'tcx, C> DebugWithContext<C> for BorrowckDomain<'_, 'tcx> +impl<'tcx, C> DebugWithContext<C> for BorrowckDomain where C: rustc_mir_dataflow::move_paths::HasMoveData<'tcx>, { @@ -160,10 +163,10 @@ where /// The transient state of the dataflow analyses used by the borrow checker. #[derive(Clone, Debug, PartialEq, Eq)] -pub(crate) struct BorrowckDomain<'a, 'tcx> { - pub(crate) borrows: <Borrows<'a, 'tcx> as Analysis<'tcx>>::Domain, - pub(crate) uninits: <MaybeUninitializedPlaces<'a, 'tcx> as Analysis<'tcx>>::Domain, - pub(crate) ever_inits: <EverInitializedPlaces<'a, 'tcx> as Analysis<'tcx>>::Domain, +pub(crate) struct BorrowckDomain { + pub(crate) borrows: BorrowsDomain, + pub(crate) uninits: MaybeUninitializedPlacesDomain, + pub(crate) ever_inits: EverInitializedPlacesDomain, } rustc_index::newtype_index! { @@ -503,7 +506,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> { /// That means they went out of a nonlexical scope fn kill_loans_out_of_scope_at_location( &self, - trans: &mut <Self as Analysis<'tcx>>::Domain, + state: &mut <Self as Analysis<'tcx>>::Domain, location: Location, ) { // NOTE: The state associated with a given `location` @@ -518,14 +521,14 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> { // region, then setting that gen-bit will override any // potential kill introduced here. if let Some(indices) = self.borrows_out_of_scope_at_location.get(&location) { - trans.kill_all(indices.iter().copied()); + state.kill_all(indices.iter().copied()); } } /// Kill any borrows that conflict with `place`. fn kill_borrows_on_place( &self, - trans: &mut <Self as Analysis<'tcx>>::Domain, + state: &mut <Self as Analysis<'tcx>>::Domain, place: Place<'tcx>, ) { debug!("kill_borrows_on_place: place={:?}", place); @@ -543,7 +546,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> { // `places_conflict` for every borrow. if place.projection.is_empty() { if !self.body.local_decls[place.local].is_ref_to_static() { - trans.kill_all(other_borrows_of_local); + state.kill_all(other_borrows_of_local); } return; } @@ -562,10 +565,12 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> { ) }); - trans.kill_all(definitely_conflicting_borrows); + state.kill_all(definitely_conflicting_borrows); } } +type BorrowsDomain = BitSet<BorrowIndex>; + /// Forward dataflow computation of the set of borrows that are in scope at a particular location. /// - we gen the introduced loans /// - we kill loans on locals going out of (regular) scope @@ -574,7 +579,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> { /// - we also kill loans of conflicting places when overwriting a shared path: e.g. borrows of /// `a.b.c` when `a` is overwritten. impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> { - type Domain = BitSet<BorrowIndex>; + type Domain = BorrowsDomain; const NAME: &'static str = "borrows"; @@ -588,18 +593,18 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> { // function execution, so this method has no effect. } - fn apply_before_statement_effect( + fn apply_early_statement_effect( &mut self, - trans: &mut Self::Domain, + state: &mut Self::Domain, _statement: &mir::Statement<'tcx>, location: Location, ) { - self.kill_loans_out_of_scope_at_location(trans, location); + self.kill_loans_out_of_scope_at_location(state, location); } - fn apply_statement_effect( + fn apply_primary_statement_effect( &mut self, - trans: &mut Self::Domain, + state: &mut Self::Domain, stmt: &mir::Statement<'tcx>, location: Location, ) { @@ -617,18 +622,18 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> { panic!("could not find BorrowIndex for location {location:?}"); }); - trans.gen_(index); + state.gen_(index); } // Make sure there are no remaining borrows for variables // that are assigned over. - self.kill_borrows_on_place(trans, *lhs); + self.kill_borrows_on_place(state, *lhs); } mir::StatementKind::StorageDead(local) => { // Make sure there are no remaining borrows for locals that // are gone out of scope. - self.kill_borrows_on_place(trans, Place::from(*local)); + self.kill_borrows_on_place(state, Place::from(*local)); } mir::StatementKind::FakeRead(..) @@ -646,18 +651,18 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> { } } - fn apply_before_terminator_effect( + fn apply_early_terminator_effect( &mut self, - trans: &mut Self::Domain, + state: &mut Self::Domain, _terminator: &mir::Terminator<'tcx>, location: Location, ) { - self.kill_loans_out_of_scope_at_location(trans, location); + self.kill_loans_out_of_scope_at_location(state, location); } - fn apply_terminator_effect<'mir>( + fn apply_primary_terminator_effect<'mir>( &mut self, - trans: &mut Self::Domain, + state: &mut Self::Domain, terminator: &'mir mir::Terminator<'tcx>, _location: Location, ) -> TerminatorEdges<'mir, 'tcx> { @@ -666,7 +671,7 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> { if let mir::InlineAsmOperand::Out { place: Some(place), .. } | mir::InlineAsmOperand::InOut { out_place: Some(place), .. } = *op { - self.kill_borrows_on_place(trans, place); + self.kill_borrows_on_place(state, place); } } } diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index c11103af476..8dcc5324fdf 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1151,7 +1151,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { expr: &hir::Expr<'_>, ) { let typeck_results = self.infcx.tcx.typeck(self.mir_def_id()); - let hir::ExprKind::Struct(struct_qpath, fields, Some(base)) = expr.kind else { return }; + let hir::ExprKind::Struct(struct_qpath, fields, hir::StructTailExpr::Base(base)) = + expr.kind + else { + return; + }; let hir::QPath::Resolved(_, path) = struct_qpath else { return }; let hir::def::Res::Def(_, def_id) = path.res else { return }; let Some(expr_ty) = typeck_results.node_type_opt(expr.hir_id) else { return }; @@ -1239,7 +1243,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { expr: &'tcx hir::Expr<'tcx>, use_spans: Option<UseSpans<'tcx>>, ) { - if let hir::ExprKind::Struct(_, _, Some(_)) = expr.kind { + if let hir::ExprKind::Struct(_, _, hir::StructTailExpr::Base(_)) = expr.kind { // We have `S { foo: val, ..base }`. In `check_aggregate_rvalue` we have a single // `Location` that covers both the `S { ... }` literal, all of its fields and the // `base`. If the move happens because of `S { foo: val, bar: base.bar }` the `expr` @@ -1450,6 +1454,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ty::Param(param_ty) => Ok(( generics.type_param(param_ty, tcx), predicate.trait_ref.print_trait_sugared().to_string(), + Some(predicate.trait_ref.def_id), )), _ => Err(()), } @@ -1463,9 +1468,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { tcx, hir_generics, err, - predicates - .iter() - .map(|(param, constraint)| (param.name.as_str(), &**constraint, None)), + predicates.iter().map(|(param, constraint, def_id)| { + (param.name.as_str(), &**constraint, *def_id) + }), None, ); } diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index c5ebf3c547e..4938875f7c4 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -1100,12 +1100,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } let decl_span = local_decl.source_info.span; - let label = match *local_decl.local_info() { + let amp_mut_sugg = match *local_decl.local_info() { LocalInfo::User(mir::BindingForm::ImplicitSelf(_)) => { let suggestion = suggest_ampmut_self(self.infcx.tcx, decl_span); let additional = local_trait.map(|span| (span, suggest_ampmut_self(self.infcx.tcx, span))); - Some((true, decl_span, suggestion, additional)) + Some(AmpMutSugg { has_sugg: true, span: decl_span, suggestion, additional }) } LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm { @@ -1150,7 +1150,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { None } None => { - let (has_sugg, decl_span, sugg) = if name != kw::SelfLower { + if name != kw::SelfLower { suggest_ampmut( self.infcx.tcx, local_decl.ty, @@ -1165,7 +1165,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { .. })) => { let sugg = suggest_ampmut_self(self.infcx.tcx, decl_span); - (true, decl_span, sugg) + Some(AmpMutSugg { + has_sugg: true, + span: decl_span, + suggestion: sugg, + additional: None, + }) } // explicit self (eg `self: &'a Self`) _ => suggest_ampmut( @@ -1176,8 +1181,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { opt_ty_info, ), } - }; - Some((has_sugg, decl_span, sugg, None)) + } } } } @@ -1187,15 +1191,24 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { .. })) => { let pattern_span: Span = local_decl.source_info.span; - suggest_ref_mut(self.infcx.tcx, pattern_span) - .map(|span| (true, span, "mut ".to_owned(), None)) + suggest_ref_mut(self.infcx.tcx, pattern_span).map(|span| AmpMutSugg { + has_sugg: true, + span, + suggestion: "mut ".to_owned(), + additional: None, + }) } _ => unreachable!(), }; - match label { - Some((true, err_help_span, suggested_code, additional)) => { + match amp_mut_sugg { + Some(AmpMutSugg { + has_sugg: true, + span: err_help_span, + suggestion: suggested_code, + additional, + }) => { let mut sugg = vec![(err_help_span, suggested_code)]; if let Some(s) = additional { sugg.push(s); @@ -1217,7 +1230,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ); } } - Some((false, err_label_span, message, _)) => { + Some(AmpMutSugg { + has_sugg: false, span: err_label_span, suggestion: message, .. + }) => { let def_id = self.body.source.def_id(); let hir_id = if let Some(local_def_id) = def_id.as_local() && let Some(body) = self.infcx.tcx.hir().maybe_body_owned_by(local_def_id) @@ -1422,6 +1437,13 @@ fn suggest_ampmut_self<'tcx>(tcx: TyCtxt<'tcx>, span: Span) -> String { } } +struct AmpMutSugg { + has_sugg: bool, + span: Span, + suggestion: String, + additional: Option<(Span, String)>, +} + // When we want to suggest a user change a local variable to be a `&mut`, there // are three potential "obvious" things to highlight: // @@ -1443,7 +1465,7 @@ fn suggest_ampmut<'tcx>( decl_span: Span, opt_assignment_rhs_span: Option<Span>, opt_ty_info: Option<Span>, -) -> (bool, Span, String) { +) -> Option<AmpMutSugg> { // if there is a RHS and it starts with a `&` from it, then check if it is // mutable, and if not, put suggest putting `mut ` to make it mutable. // we don't have to worry about lifetime annotations here because they are @@ -1452,11 +1474,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_mut = if let Some(rest) = stripped.trim_start().strip_prefix("mut") { + // 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, + }); + } + + // 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, @@ -1473,13 +1511,17 @@ 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. - return (true, span, "mut ".to_owned()); + return Some(AmpMutSugg { + has_sugg: true, + span, + suggestion: "mut ".to_owned(), + additional: None, + }); } } @@ -1504,18 +1546,23 @@ fn suggest_ampmut<'tcx>( && let Some(ws_pos) = src.find(char::is_whitespace) { let span = span.with_lo(span.lo() + BytePos(ws_pos as u32)).shrink_to_lo(); - (true, span, " mut".to_owned()) + Some(AmpMutSugg { has_sugg: true, span, suggestion: " mut".to_owned(), additional: None }) // if there is already a binding, we modify it to be `mut` } else if binding_exists { // shrink the span to just after the `&` in `&variable` let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo(); - (true, span, "mut ".to_owned()) + Some(AmpMutSugg { has_sugg: true, span, suggestion: "mut ".to_owned(), additional: None }) } else { // otherwise, suggest that the user annotates the binding; we provide the // type of the local. let ty = decl_ty.builtin_deref(true).unwrap(); - (false, span, format!("{}mut {}", if decl_ty.is_ref() { "&" } else { "*" }, ty)) + Some(AmpMutSugg { + has_sugg: false, + span, + suggestion: format!("{}mut {}", if decl_ty.is_ref() { "&" } else { "*" }, ty), + additional: None, + }) } } diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 6ea5c44dc01..9bf6767313f 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -848,7 +848,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 +875,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 +883,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 +1018,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..5dfc2658d2a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -459,11 +459,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 +871,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/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 9b7474c2208..63e20b16f7a 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -26,7 +26,7 @@ use rustc_data_structures::graph::dominators::Dominators; use rustc_errors::Diag; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; -use rustc_index::bit_set::{BitSet, ChunkedBitSet}; +use rustc_index::bit_set::{BitSet, MixedBitSet}; use rustc_index::{IndexSlice, IndexVec}; use rustc_infer::infer::{ InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, TyCtxtInferExt, @@ -43,7 +43,7 @@ use rustc_mir_dataflow::impls::{ use rustc_mir_dataflow::move_paths::{ InitIndex, InitLocation, LookupResult, MoveData, MoveOutIndex, MovePathIndex, }; -use rustc_mir_dataflow::{Analysis, EntrySets, Results, ResultsVisitor, visit_results}; +use rustc_mir_dataflow::{Analysis, EntryStates, Results, ResultsVisitor, visit_results}; use rustc_session::lint::builtin::UNUSED_MUT; use rustc_span::{Span, Symbol}; use smallvec::SmallVec; @@ -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 @@ -224,6 +220,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 = @@ -426,14 +423,14 @@ fn get_flow_results<'a, 'tcx>( ever_inits: ever_inits.analysis, }; - assert_eq!(borrows.entry_sets.len(), uninits.entry_sets.len()); - assert_eq!(borrows.entry_sets.len(), ever_inits.entry_sets.len()); - let entry_sets: EntrySets<'_, Borrowck<'_, '_>> = - itertools::izip!(borrows.entry_sets, uninits.entry_sets, ever_inits.entry_sets) + assert_eq!(borrows.entry_states.len(), uninits.entry_states.len()); + assert_eq!(borrows.entry_states.len(), ever_inits.entry_states.len()); + let entry_states: EntryStates<'_, Borrowck<'_, '_>> = + itertools::izip!(borrows.entry_states, uninits.entry_states, ever_inits.entry_states) .map(|(borrows, uninits, ever_inits)| BorrowckDomain { borrows, uninits, ever_inits }) .collect(); - Results { analysis, entry_sets } + Results { analysis, entry_states } } pub(crate) struct BorrowckInferCtxt<'tcx> { @@ -600,10 +597,10 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> { // 3. assignments do not affect things loaned out as immutable // 4. moves do not affect things loaned out in any way impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<'a, '_, 'tcx> { - fn visit_statement_before_primary_effect( + fn visit_after_early_statement_effect( &mut self, _results: &mut Results<'tcx, Borrowck<'a, 'tcx>>, - state: &BorrowckDomain<'a, 'tcx>, + state: &BorrowckDomain, stmt: &'a Statement<'tcx>, location: Location, ) { @@ -674,10 +671,10 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt< } } - fn visit_terminator_before_primary_effect( + fn visit_after_early_terminator_effect( &mut self, _results: &mut Results<'tcx, Borrowck<'a, 'tcx>>, - state: &BorrowckDomain<'a, 'tcx>, + state: &BorrowckDomain, term: &'a Terminator<'tcx>, loc: Location, ) { @@ -787,10 +784,10 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt< } } - fn visit_terminator_after_primary_effect( + fn visit_after_primary_terminator_effect( &mut self, _results: &mut Results<'tcx, Borrowck<'a, 'tcx>>, - state: &BorrowckDomain<'a, 'tcx>, + state: &BorrowckDomain, term: &'a Terminator<'tcx>, loc: Location, ) { @@ -983,7 +980,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { place_span: (Place<'tcx>, Span), kind: (AccessDepth, ReadOrWrite), is_local_mutation_allowed: LocalMutationIsAllowed, - state: &BorrowckDomain<'a, 'tcx>, + state: &BorrowckDomain, ) { let (sd, rw) = kind; @@ -1032,7 +1029,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { place_span: (Place<'tcx>, Span), sd: AccessDepth, rw: ReadOrWrite, - state: &BorrowckDomain<'a, 'tcx>, + state: &BorrowckDomain, ) -> bool { let mut error_reported = false; @@ -1172,7 +1169,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { location: Location, place_span: (Place<'tcx>, Span), kind: AccessDepth, - state: &BorrowckDomain<'a, 'tcx>, + state: &BorrowckDomain, ) { // Write of P[i] or *P requires P init'd. self.check_if_assigned_path_is_moved(location, place_span, state); @@ -1190,7 +1187,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { &mut self, location: Location, (rvalue, span): (&'a Rvalue<'tcx>, Span), - state: &BorrowckDomain<'a, 'tcx>, + state: &BorrowckDomain, ) { match rvalue { &Rvalue::Ref(_ /*rgn*/, bk, place) => { @@ -1448,7 +1445,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { &mut self, location: Location, (operand, span): (&'a Operand<'tcx>, Span), - state: &BorrowckDomain<'a, 'tcx>, + state: &BorrowckDomain, ) { match *operand { Operand::Copy(place) => { @@ -1568,12 +1565,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { } } - fn check_activations( - &mut self, - location: Location, - span: Span, - state: &BorrowckDomain<'a, 'tcx>, - ) { + fn check_activations(&mut self, location: Location, span: Span, state: &BorrowckDomain) { // Two-phase borrow support: For each activation that is newly // generated at this statement, check if it interferes with // another borrow. @@ -1731,7 +1723,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { location: Location, desired_action: InitializationRequiringAction, place_span: (PlaceRef<'tcx>, Span), - state: &BorrowckDomain<'a, 'tcx>, + state: &BorrowckDomain, ) { let maybe_uninits = &state.uninits; @@ -1797,7 +1789,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { location: Location, desired_action: InitializationRequiringAction, place_span: (PlaceRef<'tcx>, Span), - maybe_uninits: &ChunkedBitSet<MovePathIndex>, + maybe_uninits: &MixedBitSet<MovePathIndex>, from: u64, to: u64, ) { @@ -1836,7 +1828,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { location: Location, desired_action: InitializationRequiringAction, place_span: (PlaceRef<'tcx>, Span), - state: &BorrowckDomain<'a, 'tcx>, + state: &BorrowckDomain, ) { let maybe_uninits = &state.uninits; @@ -1935,7 +1927,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { &mut self, location: Location, (place, span): (Place<'tcx>, Span), - state: &BorrowckDomain<'a, 'tcx>, + state: &BorrowckDomain, ) { debug!("check_if_assigned_path_is_moved place: {:?}", place); @@ -2001,7 +1993,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { location: Location, base: PlaceRef<'tcx>, span: Span, - state: &BorrowckDomain<'a, 'tcx>, + state: &BorrowckDomain, ) { // rust-lang/rust#21232: Until Rust allows reads from the // initialized parts of partially initialized structs, we @@ -2092,7 +2084,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { (place, span): (Place<'tcx>, Span), kind: ReadOrWrite, is_local_mutation_allowed: LocalMutationIsAllowed, - state: &BorrowckDomain<'a, 'tcx>, + state: &BorrowckDomain, location: Location, ) -> bool { debug!( @@ -2206,18 +2198,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { } } - fn is_local_ever_initialized( - &self, - local: Local, - state: &BorrowckDomain<'a, 'tcx>, - ) -> Option<InitIndex> { + fn is_local_ever_initialized(&self, local: Local, state: &BorrowckDomain) -> Option<InitIndex> { let mpi = self.move_data.rev_lookup.find_local(local)?; let ii = &self.move_data.init_path_map[mpi]; ii.into_iter().find(|&&index| state.ever_inits.contains(index)).copied() } /// Adds the place into the used mutable variables set - fn add_used_mut(&mut self, root_place: RootPlace<'tcx>, state: &BorrowckDomain<'a, 'tcx>) { + fn add_used_mut(&mut self, root_place: RootPlace<'tcx>, state: &BorrowckDomain) { match root_place { RootPlace { place_local: local, place_projection: [], is_local_mutation_allowed } => { // If the local may have been initialized, and it is now currently being diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index be02e2f48df..d7ea8e1bcc2 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -116,7 +116,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, diff --git a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs index f646beeecf7..f646beeecf7 100644 --- a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs diff --git a/compiler/rustc_borrowck/src/polonius/loan_kills.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_kills.rs index 68e0865ab82..68e0865ab82 100644 --- a/compiler/rustc_borrowck/src/polonius/loan_kills.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_kills.rs 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..9fccc00bdaf --- /dev/null +++ b/compiler/rustc_borrowck/src/polonius/legacy/mod.rs @@ -0,0 +1,184 @@ +//! 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 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 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 +/// +/// 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); +} + +/// 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<'_>, +) { + 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))); + } + } 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<'_>, +) { + // 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())); + } + } +} + +/// 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>, +) { + loan_kills::emit_loan_kills(tcx, all_facts, location_table, body, borrow_set); +} diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs index 9fccc00bdaf..9c1583f1988 100644 --- a/compiler/rustc_borrowck/src/polonius/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/mod.rs @@ -1,184 +1 @@ -//! 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 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 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 -/// -/// 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); -} - -/// 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<'_>, -) { - 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))); - } - } 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<'_>, -) { - // 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())); - } - } -} - -/// 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>, -) { - loan_kills::emit_loan_kills(tcx, all_facts, location_table, body, borrow_set); -} +pub(crate) mod legacy; diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index 585d0eabf5b..3903c45fda5 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -275,7 +275,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { user_ty: ty::UserType<'tcx>, span: Span, ) { - let ty::UserType::Ty(user_ty) = user_ty else { bug!() }; + let ty::UserTypeKind::Ty(user_ty) = user_ty.kind else { bug!() }; // A fast path for a common case with closure input/output types. if let ty::Infer(_) = user_ty.kind() { diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs index bbe2b55d8c4..1f2ec168f03 100644 --- a/compiler/rustc_borrowck/src/type_check/input_output.rs +++ b/compiler/rustc_borrowck/src/type_check/input_output.rs @@ -110,7 +110,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ) { self.ascribe_user_type_skip_wf( arg_decl.ty, - ty::UserType::Ty(user_ty), + ty::UserType::new(ty::UserTypeKind::Ty(user_ty)), arg_decl.source_info.span, ); } @@ -119,7 +119,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let output_decl = &body.local_decls[RETURN_PLACE]; self.ascribe_user_type_skip_wf( output_decl.ty, - ty::UserType::Ty(user_provided_sig.output()), + ty::UserType::new(ty::UserTypeKind::Ty(user_provided_sig.output())), output_decl.source_info.span, ); } diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs index 20d19a53752..05e4a176a6d 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs @@ -45,7 +45,7 @@ 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); + polonius::emit_access_facts(typeck, body, move_data); trace::trace( typeck, diff --git a/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs b/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs index e8d8ae0850b..5ffba94ee68 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs @@ -11,88 +11,19 @@ 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>( +/// Emit polonius facts for variable defs, uses, drops, and path accesses. +pub(super) fn emit_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()"); + debug!("emit_access_facts()"); + + let _prof_timer = typeck.infcx.tcx.prof.generic_activity("polonius_fact_generation"); let location_table = typeck.location_table; - let mut extractor = UseFactsExtractor { + let mut extractor = AccessFactsExtractor { var_defined_at: &mut facts.var_defined_at, var_used_at: &mut facts.var_used_at, var_dropped_at: &mut facts.var_dropped_at, @@ -107,7 +38,6 @@ pub(super) fn populate_access_facts<'a, 'tcx>( "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); @@ -119,12 +49,12 @@ pub(super) fn populate_access_facts<'a, 'tcx>( /// 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>( +pub(super) fn emit_drop_facts<'tcx>( typeck: &mut TypeChecker<'_, 'tcx>, local: Local, kind: &GenericArg<'tcx>, ) { - debug!("add_drop_of_var_derefs_origin(local={:?}, kind={:?}", local, kind); + debug!("emit_drop_facts(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; @@ -134,3 +64,60 @@ pub(super) fn add_drop_of_var_derefs_origin<'tcx>( }); } } + +/// MIR visitor extracting point-wise facts about accesses. +struct AccessFactsExtractor<'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, +} + +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.var_defined_at.push((local, self.location_to_index(location))); + } + Some(DefUse::Use) => { + debug!("AccessFactsExtractor - emit use"); + self.var_used_at.push((local, self.location_to_index(location))); + } + Some(DefUse::Drop) => { + debug!("AccessFactsExtractor - emit drop"); + self.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.path_accessed_at_base.push((path, self.location_to_index(location))); + } + + _ => {} + } + } +} diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 3ec36c16cbf..539d3f97a63 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -590,7 +590,7 @@ 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::emit_drop_facts(self.typeck, dropped_local, &kind); } } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 89e683b8ae3..90d327b0ad2 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -31,7 +31,7 @@ use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{ self, Binder, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, CoroutineArgsExt, Dynamic, GenericArgsRef, OpaqueHiddenType, OpaqueTypeKey, RegionVid, Ty, TyCtxt, UserArgs, - UserType, UserTypeAnnotationIndex, + UserTypeAnnotationIndex, }; use rustc_middle::{bug, span_bug}; use rustc_mir_dataflow::ResultsCursor; @@ -370,7 +370,10 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { } else { self.cx.ascribe_user_type( constant.const_.ty(), - UserType::TypeOf(uv.def, UserArgs { args: uv.args, user_self_ty: None }), + ty::UserType::new(ty::UserTypeKind::TypeOf(uv.def, UserArgs { + args: uv.args, + user_self_ty: None, + })), locations.span(self.cx.body), ); } @@ -991,9 +994,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { for user_annotation in self.user_type_annotations { let CanonicalUserTypeAnnotation { span, ref user_ty, inferred_ty } = *user_annotation; let annotation = self.instantiate_canonical(span, user_ty); - if let ty::UserType::TypeOf(def, args) = annotation + if let ty::UserTypeKind::TypeOf(def, args) = annotation.kind && let DefKind::InlineConst = tcx.def_kind(def) { + assert!(annotation.bounds.is_empty()); self.check_inline_const(inferred_ty, def.expect_local(), args, span); } else { self.ascribe_user_type(inferred_ty, annotation, span); 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 c05d44cb452..87d3d288013 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -261,7 +261,7 @@ builtin_macros_non_exhaustive_default = default variant must be exhaustive builtin_macros_non_generic_pointee = the `#[pointee]` attribute may only be used on generic parameters -builtin_macros_non_unit_default = the `#[default]` attribute may only be used on unit enum variants +builtin_macros_non_unit_default = the `#[default]` attribute may only be used on unit enum variants{$post} .help = consider a manual implementation of `Default` builtin_macros_only_one_argument = {$name} takes 1 argument diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs index 70fa4d00c0f..eb07975d8af 100644 --- a/compiler/rustc_builtin_macros/src/assert/context.rs +++ b/compiler/rustc_builtin_macros/src/assert/context.rs @@ -323,7 +323,8 @@ impl<'cx, 'a> Context<'cx, 'a> { | ExprKind::While(_, _, _) | ExprKind::Yeet(_) | ExprKind::Become(_) - | ExprKind::Yield(_) => {} + | ExprKind::Yield(_) + | ExprKind::UnsafeBinderCast(..) => {} } } diff --git a/compiler/rustc_builtin_macros/src/cfg.rs b/compiler/rustc_builtin_macros/src/cfg.rs index 15993dbf5ec..6e90f1682e3 100644 --- a/compiler/rustc_builtin_macros/src/cfg.rs +++ b/compiler/rustc_builtin_macros/src/cfg.rs @@ -7,7 +7,7 @@ use rustc_ast::tokenstream::TokenStream; use rustc_errors::PResult; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult}; 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; diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs index 3bd8f899a4a..b91d6ced123 100644 --- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs +++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs @@ -6,7 +6,7 @@ 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; diff --git a/compiler/rustc_builtin_macros/src/deriving/decodable.rs b/compiler/rustc_builtin_macros/src/deriving/decodable.rs index 686424770fc..469092e7b1c 100644 --- a/compiler/rustc_builtin_macros/src/deriving/decodable.rs +++ b/compiler/rustc_builtin_macros/src/deriving/decodable.rs @@ -205,7 +205,7 @@ where let fields = fields .iter() .enumerate() - .map(|(i, &(ident, span))| { + .map(|(i, &(ident, span, _))| { let arg = getarg(cx, span, ident.name, i); cx.field_imm(span, ident, arg) }) diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs index d4befd12190..6b1a6effad7 100644 --- a/compiler/rustc_builtin_macros/src/deriving/default.rs +++ b/compiler/rustc_builtin_macros/src/deriving/default.rs @@ -54,26 +54,38 @@ pub(crate) fn expand_deriving_default( trait_def.expand(cx, mitem, item, push) } +fn default_call(cx: &ExtCtxt<'_>, span: Span) -> ast::ptr::P<ast::Expr> { + // Note that `kw::Default` is "default" and `sym::Default` is "Default"! + let default_ident = cx.std_path(&[kw::Default, sym::Default, kw::Default]); + cx.expr_call_global(span, default_ident, ThinVec::new()) +} + fn default_struct_substructure( cx: &ExtCtxt<'_>, trait_span: Span, substr: &Substructure<'_>, summary: &StaticFields, ) -> BlockOrExpr { - // Note that `kw::Default` is "default" and `sym::Default` is "Default"! - let default_ident = cx.std_path(&[kw::Default, sym::Default, kw::Default]); - let default_call = |span| cx.expr_call_global(span, default_ident.clone(), ThinVec::new()); - let expr = match summary { Unnamed(_, IsTuple::No) => cx.expr_ident(trait_span, substr.type_ident), Unnamed(fields, IsTuple::Yes) => { - let exprs = fields.iter().map(|sp| default_call(*sp)).collect(); + let exprs = fields.iter().map(|sp| default_call(cx, *sp)).collect(); cx.expr_call_ident(trait_span, substr.type_ident, exprs) } Named(fields) => { let default_fields = fields .iter() - .map(|&(ident, span)| cx.field_imm(span, ident, default_call(span))) + .map(|(ident, span, default_val)| { + let value = match default_val { + // We use `Default::default()`. + None => default_call(cx, *span), + // We use the field default const expression. + Some(val) => { + cx.expr(val.value.span, ast::ExprKind::ConstBlock(val.clone())) + } + }; + cx.field_imm(*span, *ident, value) + }) .collect(); cx.expr_struct_ident(trait_span, substr.type_ident, default_fields) } @@ -93,10 +105,38 @@ fn default_enum_substructure( } { Ok(default_variant) => { // We now know there is exactly one unit variant with exactly one `#[default]` attribute. - cx.expr_path(cx.path(default_variant.span, vec![ - Ident::new(kw::SelfUpper, default_variant.span), - default_variant.ident, - ])) + match &default_variant.data { + VariantData::Unit(_) => cx.expr_path(cx.path(default_variant.span, vec![ + Ident::new(kw::SelfUpper, default_variant.span), + default_variant.ident, + ])), + VariantData::Struct { fields, .. } => { + // This only happens if `#![feature(default_field_values)]`. We have validated + // all fields have default values in the definition. + let default_fields = fields + .iter() + .map(|field| { + cx.field_imm(field.span, field.ident.unwrap(), match &field.default { + // We use `Default::default()`. + None => default_call(cx, field.span), + // We use the field default const expression. + Some(val) => { + cx.expr(val.value.span, ast::ExprKind::ConstBlock(val.clone())) + } + }) + }) + .collect(); + let path = cx.path(default_variant.span, vec![ + Ident::new(kw::SelfUpper, default_variant.span), + default_variant.ident, + ]); + cx.expr_struct(default_variant.span, path, default_fields) + } + // Logic error in `extract_default_variant`. + VariantData::Tuple(..) => { + cx.dcx().bug("encountered tuple variant annotated with `#[default]`") + } + } } Err(guar) => DummyResult::raw_expr(trait_span, Some(guar)), }; @@ -156,8 +196,20 @@ fn extract_default_variant<'a>( } }; - if !matches!(variant.data, VariantData::Unit(..)) { - let guar = cx.dcx().emit_err(errors::NonUnitDefault { span: variant.ident.span }); + if cx.ecfg.features.default_field_values() + && let VariantData::Struct { fields, .. } = &variant.data + && fields.iter().all(|f| f.default.is_some()) + // Disallow `#[default] Variant {}` + && !fields.is_empty() + { + // Allowed + } else if !matches!(variant.data, VariantData::Unit(..)) { + let post = if cx.ecfg.features.default_field_values() { + " or variants where every field has a default value" + } else { + "" + }; + let guar = cx.dcx().emit_err(errors::NonUnitDefault { span: variant.ident.span, post }); return Err(guar); } @@ -216,7 +268,12 @@ struct DetectNonVariantDefaultAttr<'a, 'b> { impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, 'b> { fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) { if attr.has_name(kw::Default) { - self.cx.dcx().emit_err(errors::NonUnitDefault { span: attr.span }); + let post = if self.cx.ecfg.features.default_field_values() { + " or variants where every field has a default value" + } else { + "" + }; + self.cx.dcx().emit_err(errors::NonUnitDefault { span: attr.span, post }); } rustc_ast::visit::walk_attribute(self, attr); diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index f6eea0b21ca..1c5ff6c9ef2 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -182,10 +182,10 @@ pub(crate) use StaticFields::*; pub(crate) use SubstructureFields::*; use rustc_ast::ptr::P; use rustc_ast::{ - self as ast, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind, Generics, - Mutability, PatKind, VariantData, + 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}; @@ -296,7 +296,7 @@ pub(crate) enum StaticFields { /// Tuple and unit structs/enum variants like this. Unnamed(Vec<Span>, IsTuple), /// Normal structs/struct variants. - Named(Vec<(Ident, Span)>), + Named(Vec<(Ident, Span, Option<AnonConst>)>), } /// A summary of the possible sets of fields. @@ -1435,7 +1435,7 @@ impl<'a> TraitDef<'a> { for field in struct_def.fields() { let sp = field.span.with_ctxt(self.span.ctxt()); match field.ident { - Some(ident) => named_idents.push((ident, sp)), + Some(ident) => named_idents.push((ident, sp, field.default.clone())), _ => just_spans.push(sp), } } diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index f8e65661e52..c9bd3371e55 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -424,6 +424,7 @@ pub(crate) struct MultipleDefaultsSugg { pub(crate) struct NonUnitDefault { #[primary_span] pub(crate) span: Span, + pub(crate) post: &'static str, } #[derive(Diagnostic)] diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index b2048c534a4..e7ff65e08f9 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -141,8 +141,10 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> { // We don't want to recurse into anything other than mods, since // mods or tests inside of functions will break things - if let ast::ItemKind::Mod(_, ModKind::Loaded(.., ast::ModSpans { inner_span: span, .. })) = - item.kind + if let ast::ItemKind::Mod( + _, + ModKind::Loaded(.., ast::ModSpans { inner_span: span, .. }, _), + ) = item.kind { let prev_tests = mem::take(&mut self.tests); walk_item_kind( diff --git a/compiler/rustc_codegen_cranelift/Cargo.lock b/compiler/rustc_codegen_cranelift/Cargo.lock index e4f77472802..d81e7214961 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.lock +++ b/compiler/rustc_codegen_cranelift/Cargo.lock @@ -46,24 +46,24 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cranelift-bforest" -version = "0.113.0" +version = "0.114.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5e7afe85cadb55c4c1176268a2ac046fdff8dfaeca39e18581b9dc319ca9e" +checksum = "2ba4f80548f22dc9c43911907b5e322c5555544ee85f785115701e6a28c9abe1" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-bitset" -version = "0.113.0" +version = "0.114.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ab25ef3be935a80680e393183e1f94ef507e93a24a8369494d2c6818aedb3e3" +checksum = "005884e3649c3e5ff2dc79e8a94b138f11569cc08a91244a292714d2a86e9156" [[package]] name = "cranelift-codegen" -version = "0.113.0" +version = "0.114.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900a19b84545924f1851cbfe386962edfc4ecbc3366a254825cf1ecbcda8ba08" +checksum = "fe4036255ec33ce9a37495dfbcfc4e1118fd34e693eff9a1e106336b7cd16a9b" dependencies = [ "bumpalo", "cranelift-bforest", @@ -78,48 +78,49 @@ dependencies = [ "log", "regalloc2", "rustc-hash", + "serde", "smallvec", "target-lexicon", ] [[package]] name = "cranelift-codegen-meta" -version = "0.113.0" +version = "0.114.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08c73b2395ffe9e7b4fdf7e2ebc052e7e27af13f68a964985346be4da477a5fc" +checksum = "f7ca74f4b68319da11d39e894437cb6e20ec7c2e11fbbda823c3bf207beedff7" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.113.0" +version = "0.114.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d9ed0854e96a4ff0879bff39d078de8dea7f002721c9494c1fdb4e1baa86ccc" +checksum = "897e54f433a0269c4187871aa06d452214d5515d228d5bdc22219585e9eef895" [[package]] name = "cranelift-control" -version = "0.113.0" +version = "0.114.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4aca921dd422e781409de0129c255768fec5dec1dae83239b497fb9138abb89" +checksum = "29cb4018f5bf59fb53f515fa9d80e6f8c5ce19f198dc538984ebd23ecf8965ec" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.113.0" +version = "0.114.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d770e6605eccee15b49decdd82cd26f2b6404767802471459ea49c57379a98" +checksum = "305399fd781a2953ac78c1396f02ff53144f39c33eb7fc7789cf4e8936d13a96" dependencies = [ "cranelift-bitset", ] [[package]] name = "cranelift-frontend" -version = "0.113.0" +version = "0.114.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29268711cb889cb39215b10faf88b9087d4c9e1d2633581e4f722a2bf4bb4ef9" +checksum = "9230b460a128d53653456137751d27baf567947a3ab8c0c4d6e31fd08036d81e" dependencies = [ "cranelift-codegen", "log", @@ -129,15 +130,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.113.0" +version = "0.114.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc65156f010aed1985767ad1bff0eb8d186743b7b03e23d0c17604a253e3f356" +checksum = "b961e24ae3ec9813a24a15ae64bbd2a42e4de4d79a7f3225a412e3b94e78d1c8" [[package]] name = "cranelift-jit" -version = "0.113.0" +version = "0.114.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ba6b46367a4f466cfb1abe32793fa1a0f96d862251491b01a44726b8ed9445" +checksum = "62699329d4ced20fe281fbaef45e11b473b7ab310491b4bdebcd8b818a8ef7fe" dependencies = [ "anyhow", "cranelift-codegen", @@ -155,9 +156,9 @@ dependencies = [ [[package]] name = "cranelift-module" -version = "0.113.0" +version = "0.114.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "007607022a4883ebdffc46c0925e2e10babf2a565ae78518034ade722aa825d2" +checksum = "2f20b0b51ba962dac30fc7e812b86e4390d908acd4f59bcc8ac7610a8f3e0977" dependencies = [ "anyhow", "cranelift-codegen", @@ -166,9 +167,9 @@ dependencies = [ [[package]] name = "cranelift-native" -version = "0.113.0" +version = "0.114.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8bf9b361eaf5a7627647270fabf1dc910d993edbeaf272a652c107861ebe9c2" +checksum = "4d5bd76df6c9151188dfa428c863b33da5b34561b67f43c0cf3f24a794f9fa1f" dependencies = [ "cranelift-codegen", "libc", @@ -177,9 +178,9 @@ dependencies = [ [[package]] name = "cranelift-object" -version = "0.113.0" +version = "0.114.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ca5c38fa00c0cd943035391bdcc84ed00748f17c66c682e410f5a62f234d44" +checksum = "ee231640a7ecceedd0f1f2782d9288db6a6908cc70675ed9427e3bf0ea6daacd" dependencies = [ "anyhow", "cranelift-codegen", @@ -364,6 +365,26 @@ dependencies = [ ] [[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "slice-group-by" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -412,9 +433,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasmtime-jit-icache-coherence" -version = "26.0.0" +version = "27.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e458e6a1a010a53f86ac8d75837c0c6b2ce3e54b7503b2f1dc5629a4a541f5a" +checksum = "91b218a92866f74f35162f5d03a4e0f62cd0e1cc624285b1014275e5d4575fad" dependencies = [ "anyhow", "cfg-if", diff --git a/compiler/rustc_codegen_cranelift/Cargo.toml b/compiler/rustc_codegen_cranelift/Cargo.toml index f352ef72cb0..b2fed3c490e 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.toml +++ b/compiler/rustc_codegen_cranelift/Cargo.toml @@ -8,12 +8,12 @@ crate-type = ["dylib"] [dependencies] # These have to be in sync with each other -cranelift-codegen = { version = "0.113.0", default-features = false, features = ["std", "unwind", "all-native-arch"] } -cranelift-frontend = { version = "0.113.0" } -cranelift-module = { version = "0.113.0" } -cranelift-native = { version = "0.113.0" } -cranelift-jit = { version = "0.113.0", optional = true } -cranelift-object = { version = "0.113.0" } +cranelift-codegen = { version = "0.114.0", default-features = false, features = ["std", "unwind", "all-native-arch"] } +cranelift-frontend = { version = "0.114.0" } +cranelift-module = { version = "0.114.0" } +cranelift-native = { version = "0.114.0" } +cranelift-jit = { version = "0.114.0", optional = true } +cranelift-object = { version = "0.114.0" } target-lexicon = "0.12.0" gimli = { version = "0.31", default-features = false, features = ["write"] } object = { version = "0.36", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } diff --git a/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs b/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs index 9292778806a..674acfbd309 100644 --- a/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs +++ b/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs @@ -1,4 +1,4 @@ -use crate::path::{Dirs, RelPath}; +use crate::path::Dirs; use crate::prepare::GitRepo; use crate::utils::{CargoProject, Compiler, spawn_and_wait}; use crate::{CodegenBackend, SysrootKind, build_sysroot}; @@ -20,7 +20,7 @@ pub(crate) fn run( rustup_toolchain_name: Option<&str>, bootstrap_host_compiler: &Compiler, ) { - RelPath::DOWNLOAD.ensure_exists(dirs); + std::fs::create_dir_all(&dirs.download_dir).unwrap(); ABI_CAFE_REPO.fetch(dirs); ABI_CAFE_REPO.patch(dirs); diff --git a/compiler/rustc_codegen_cranelift/build_system/bench.rs b/compiler/rustc_codegen_cranelift/build_system/bench.rs index ebeb6772250..73a0f325fc2 100644 --- a/compiler/rustc_codegen_cranelift/build_system/bench.rs +++ b/compiler/rustc_codegen_cranelift/build_system/bench.rs @@ -3,7 +3,7 @@ use std::io::Write; use std::path::Path; use std::process::Command; -use crate::path::{Dirs, RelPath}; +use crate::path::Dirs; use crate::prepare::GitRepo; use crate::rustc_info::get_file_name; use crate::utils::{Compiler, spawn_and_wait}; @@ -39,11 +39,11 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) { }; eprintln!("[BENCH COMPILE] ebobby/simple-raytracer"); - let cargo_clif = RelPath::DIST - .to_path(dirs) + let cargo_clif = dirs + .dist_dir .join(get_file_name(&bootstrap_host_compiler.rustc, "cargo_clif", "bin").replace('_', "-")); let manifest_path = SIMPLE_RAYTRACER_REPO.source_dir().to_path(dirs).join("Cargo.toml"); - let target_dir = RelPath::BUILD.join("simple_raytracer").to_path(dirs); + let target_dir = dirs.build_dir.join("simple_raytracer"); let clean_cmd = format!( "RUSTC=rustc cargo clean --manifest-path {manifest_path} --target-dir {target_dir}", @@ -68,7 +68,7 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) { target_dir = target_dir.display(), ); - let bench_compile_markdown = RelPath::DIST.to_path(dirs).join("bench_compile.md"); + let bench_compile_markdown = dirs.dist_dir.join("bench_compile.md"); let bench_compile = hyperfine_command( 1, @@ -92,7 +92,7 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) { eprintln!("[BENCH RUN] ebobby/simple-raytracer"); - let bench_run_markdown = RelPath::DIST.to_path(dirs).join("bench_run.md"); + let bench_run_markdown = dirs.dist_dir.join("bench_run.md"); let raytracer_cg_llvm = Path::new(".").join(get_file_name( &bootstrap_host_compiler.rustc, @@ -120,7 +120,7 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) { ], &bench_run_markdown, ); - bench_run.current_dir(RelPath::BUILD.to_path(dirs)); + bench_run.current_dir(&dirs.build_dir); spawn_and_wait(bench_run); if let Some(gha_step_summary) = gha_step_summary.as_mut() { diff --git a/compiler/rustc_codegen_cranelift/build_system/build_backend.rs b/compiler/rustc_codegen_cranelift/build_system/build_backend.rs index 02da89f737c..72bc422523d 100644 --- a/compiler/rustc_codegen_cranelift/build_system/build_backend.rs +++ b/compiler/rustc_codegen_cranelift/build_system/build_backend.rs @@ -6,7 +6,7 @@ use crate::rustc_info::get_file_name; use crate::shared_utils::{rustflags_from_env, rustflags_to_cmd_env}; use crate::utils::{CargoProject, Compiler, LogGroup}; -static CG_CLIF: CargoProject = CargoProject::new(&RelPath::SOURCE, "cg_clif"); +static CG_CLIF: CargoProject = CargoProject::new(&RelPath::source("."), "cg_clif"); pub(crate) fn build_backend( dirs: &Dirs, diff --git a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs index f1f4489bcbc..e47e9829916 100644 --- a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs +++ b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs @@ -22,9 +22,9 @@ pub(crate) fn build_sysroot( eprintln!("[BUILD] sysroot {:?}", sysroot_kind); - let dist_dir = RelPath::DIST.to_path(dirs); + let dist_dir = &dirs.dist_dir; - ensure_empty_dir(&dist_dir); + ensure_empty_dir(dist_dir); fs::create_dir_all(dist_dir.join("bin")).unwrap(); fs::create_dir_all(dist_dir.join("lib")).unwrap(); @@ -55,7 +55,7 @@ pub(crate) fn build_sysroot( let mut build_cargo_wrapper_cmd = Command::new(&bootstrap_host_compiler.rustc); let wrapper_path = dist_dir.join(&wrapper_name); build_cargo_wrapper_cmd - .arg(RelPath::SCRIPTS.to_path(dirs).join(&format!("{wrapper}.rs"))) + .arg(dirs.source_dir.join("scripts").join(&format!("{wrapper}.rs"))) .arg("-o") .arg(&wrapper_path) .arg("-Cstrip=debuginfo"); @@ -85,7 +85,7 @@ pub(crate) fn build_sysroot( &cg_clif_dylib_path, sysroot_kind, ); - host.install_into_sysroot(&dist_dir); + host.install_into_sysroot(dist_dir); if !is_native { build_sysroot_for_triple( @@ -99,7 +99,7 @@ pub(crate) fn build_sysroot( &cg_clif_dylib_path, sysroot_kind, ) - .install_into_sysroot(&dist_dir); + .install_into_sysroot(dist_dir); } let mut target_compiler = { @@ -143,10 +143,10 @@ impl SysrootTarget { } } -static STDLIB_SRC: RelPath = RelPath::BUILD.join("stdlib"); +static STDLIB_SRC: RelPath = RelPath::build("stdlib"); static STANDARD_LIBRARY: CargoProject = - CargoProject::new(&STDLIB_SRC.join("library/sysroot"), "stdlib_target"); -static RTSTARTUP_SYSROOT: RelPath = RelPath::BUILD.join("rtstartup"); + CargoProject::new(&RelPath::build("stdlib/library/sysroot"), "stdlib_target"); +static RTSTARTUP_SYSROOT: RelPath = RelPath::build("rtstartup"); fn build_sysroot_for_triple( dirs: &Dirs, @@ -247,6 +247,7 @@ fn build_clif_sysroot_for_triple( let mut build_cmd = STANDARD_LIBRARY.build(&compiler, dirs); build_cmd.arg("--release"); build_cmd.arg("--features").arg("backtrace panic-unwind compiler-builtins-no-f16-f128"); + build_cmd.arg(format!("-Zroot-dir={}", STDLIB_SRC.to_path(dirs).display())); build_cmd.env("CARGO_PROFILE_RELEASE_DEBUG", "true"); build_cmd.env("__CARGO_DEFAULT_LIB_METADATA", "cg_clif"); if compiler.triple.contains("apple") { @@ -281,13 +282,14 @@ fn build_rtstartup(dirs: &Dirs, compiler: &Compiler) -> Option<SysrootTarget> { return None; } - RTSTARTUP_SYSROOT.ensure_fresh(dirs); + let rtstartup_sysroot = RTSTARTUP_SYSROOT.to_path(dirs); + ensure_empty_dir(&rtstartup_sysroot); let rtstartup_src = STDLIB_SRC.to_path(dirs).join("library").join("rtstartup"); let mut target_libs = SysrootTarget { triple: compiler.triple.clone(), libs: vec![] }; for file in ["rsbegin", "rsend"] { - let obj = RTSTARTUP_SYSROOT.to_path(dirs).join(format!("{file}.o")); + let obj = rtstartup_sysroot.join(format!("{file}.o")); let mut build_rtstartup_cmd = Command::new(&compiler.rustc); build_rtstartup_cmd .arg("--target") diff --git a/compiler/rustc_codegen_cranelift/build_system/main.rs b/compiler/rustc_codegen_cranelift/build_system/main.rs index b68ac7c0926..99e6146657f 100644 --- a/compiler/rustc_codegen_cranelift/build_system/main.rs +++ b/compiler/rustc_codegen_cranelift/build_system/main.rs @@ -185,12 +185,11 @@ fn main() { frozen, }; - path::RelPath::BUILD.ensure_exists(&dirs); + std::fs::create_dir_all(&dirs.build_dir).unwrap(); { // Make sure we always explicitly specify the target dir - let target = - path::RelPath::BUILD.join("target_dir_should_be_set_explicitly").to_path(&dirs); + let target = dirs.build_dir.join("target_dir_should_be_set_explicitly"); env::set_var("CARGO_TARGET_DIR", &target); let _ = std::fs::remove_file(&target); std::fs::File::create(target).unwrap(); diff --git a/compiler/rustc_codegen_cranelift/build_system/path.rs b/compiler/rustc_codegen_cranelift/build_system/path.rs index 35e7e81c528..20a81156b71 100644 --- a/compiler/rustc_codegen_cranelift/build_system/path.rs +++ b/compiler/rustc_codegen_cranelift/build_system/path.rs @@ -1,8 +1,5 @@ -use std::fs; use std::path::PathBuf; -use crate::utils::ensure_empty_dir; - #[derive(Debug, Clone)] pub(crate) struct Dirs { pub(crate) source_dir: PathBuf, @@ -16,54 +13,34 @@ pub(crate) struct Dirs { #[derive(Debug, Copy, Clone)] pub(crate) enum PathBase { Source, - Download, Build, - Dist, } impl PathBase { fn to_path(self, dirs: &Dirs) -> PathBuf { match self { PathBase::Source => dirs.source_dir.clone(), - PathBase::Download => dirs.download_dir.clone(), PathBase::Build => dirs.build_dir.clone(), - PathBase::Dist => dirs.dist_dir.clone(), } } } #[derive(Debug, Copy, Clone)] -pub(crate) enum RelPath { - Base(PathBase), - Join(&'static RelPath, &'static str), +pub(crate) struct RelPath { + base: PathBase, + suffix: &'static str, } impl RelPath { - pub(crate) const SOURCE: RelPath = RelPath::Base(PathBase::Source); - pub(crate) const DOWNLOAD: RelPath = RelPath::Base(PathBase::Download); - pub(crate) const BUILD: RelPath = RelPath::Base(PathBase::Build); - pub(crate) const DIST: RelPath = RelPath::Base(PathBase::Dist); - - pub(crate) const SCRIPTS: RelPath = RelPath::SOURCE.join("scripts"); - pub(crate) const PATCHES: RelPath = RelPath::SOURCE.join("patches"); - - pub(crate) const fn join(&'static self, suffix: &'static str) -> RelPath { - RelPath::Join(self, suffix) + pub(crate) const fn source(suffix: &'static str) -> RelPath { + RelPath { base: PathBase::Source, suffix } } - pub(crate) fn to_path(&self, dirs: &Dirs) -> PathBuf { - match self { - RelPath::Base(base) => base.to_path(dirs), - RelPath::Join(base, suffix) => base.to_path(dirs).join(suffix), - } - } - - pub(crate) fn ensure_exists(&self, dirs: &Dirs) { - fs::create_dir_all(self.to_path(dirs)).unwrap(); + pub(crate) const fn build(suffix: &'static str) -> RelPath { + RelPath { base: PathBase::Build, suffix } } - pub(crate) fn ensure_fresh(&self, dirs: &Dirs) { - let path = self.to_path(dirs); - ensure_empty_dir(&path); + pub(crate) fn to_path(&self, dirs: &Dirs) -> PathBuf { + self.base.to_path(dirs).join(self.suffix) } } diff --git a/compiler/rustc_codegen_cranelift/build_system/prepare.rs b/compiler/rustc_codegen_cranelift/build_system/prepare.rs index c6f979f0278..a4e9cb5f5c8 100644 --- a/compiler/rustc_codegen_cranelift/build_system/prepare.rs +++ b/compiler/rustc_codegen_cranelift/build_system/prepare.rs @@ -8,7 +8,7 @@ use crate::path::{Dirs, RelPath}; use crate::utils::{copy_dir_recursively, ensure_empty_dir, spawn_and_wait}; pub(crate) fn prepare(dirs: &Dirs) { - RelPath::DOWNLOAD.ensure_exists(dirs); + std::fs::create_dir_all(&dirs.download_dir).unwrap(); crate::tests::RAND_REPO.fetch(dirs); crate::tests::REGEX_REPO.fetch(dirs); } @@ -79,13 +79,13 @@ impl GitRepo { fn download_dir(&self, dirs: &Dirs) -> PathBuf { match self.url { - GitRepoUrl::Github { user: _, repo } => RelPath::DOWNLOAD.join(repo).to_path(dirs), + GitRepoUrl::Github { user: _, repo } => dirs.download_dir.join(repo), } } pub(crate) const fn source_dir(&self) -> RelPath { match self.url { - GitRepoUrl::Github { user: _, repo } => RelPath::BUILD.join(repo), + GitRepoUrl::Github { user: _, repo } => RelPath::build(repo), } } @@ -130,7 +130,7 @@ impl GitRepo { } let source_lockfile = - RelPath::PATCHES.to_path(dirs).join(format!("{}-lock.toml", self.patch_name)); + dirs.source_dir.join("patches").join(format!("{}-lock.toml", self.patch_name)); let target_lockfile = download_dir.join("Cargo.lock"); if source_lockfile.exists() { assert!(!target_lockfile.exists()); @@ -191,7 +191,7 @@ fn init_git_repo(repo_dir: &Path) { } fn get_patches(dirs: &Dirs, crate_name: &str) -> Vec<PathBuf> { - let mut patches: Vec<_> = fs::read_dir(RelPath::PATCHES.to_path(dirs)) + let mut patches: Vec<_> = fs::read_dir(dirs.source_dir.join("patches")) .unwrap() .map(|entry| entry.unwrap().path()) .filter(|path| path.extension() == Some(OsStr::new("patch"))) diff --git a/compiler/rustc_codegen_cranelift/build_system/tests.rs b/compiler/rustc_codegen_cranelift/build_system/tests.rs index fd94e80f631..08736db8ba0 100644 --- a/compiler/rustc_codegen_cranelift/build_system/tests.rs +++ b/compiler/rustc_codegen_cranelift/build_system/tests.rs @@ -7,10 +7,10 @@ use crate::path::{Dirs, RelPath}; use crate::prepare::{GitRepo, apply_patches}; use crate::rustc_info::get_default_sysroot; use crate::shared_utils::rustflags_from_env; -use crate::utils::{CargoProject, Compiler, LogGroup, spawn_and_wait}; +use crate::utils::{CargoProject, Compiler, LogGroup, ensure_empty_dir, spawn_and_wait}; use crate::{CodegenBackend, SysrootKind, build_sysroot, config}; -static BUILD_EXAMPLE_OUT_DIR: RelPath = RelPath::BUILD.join("example"); +static BUILD_EXAMPLE_OUT_DIR: RelPath = RelPath::build("example"); struct TestCase { config: &'static str, @@ -92,10 +92,6 @@ const BASE_SYSROOT_SUITE: &[TestCase] = &[ TestCase::build_bin_and_run("aot.mod_bench", "example/mod_bench.rs", &[]), TestCase::build_bin_and_run("aot.issue-72793", "example/issue-72793.rs", &[]), TestCase::build_bin("aot.issue-59326", "example/issue-59326.rs"), - TestCase::custom("aot.polymorphize_coroutine", &|runner| { - runner.run_rustc(&["example/polymorphize_coroutine.rs", "-Zpolymorphize"]); - runner.run_out_command("polymorphize_coroutine", &[]); - }), TestCase::build_bin_and_run("aot.neon", "example/neon.rs", &[]), TestCase::custom("aot.gen_block_iterate", &|runner| { runner.run_rustc([ @@ -129,11 +125,11 @@ pub(crate) static REGEX_REPO: GitRepo = GitRepo::github( static REGEX: CargoProject = CargoProject::new(®EX_REPO.source_dir(), "regex_target"); -static PORTABLE_SIMD_SRC: RelPath = RelPath::BUILD.join("portable-simd"); +static PORTABLE_SIMD_SRC: RelPath = RelPath::build("portable-simd"); static PORTABLE_SIMD: CargoProject = CargoProject::new(&PORTABLE_SIMD_SRC, "portable-simd_target"); -static LIBCORE_TESTS_SRC: RelPath = RelPath::BUILD.join("coretests"); +static LIBCORE_TESTS_SRC: RelPath = RelPath::build("coretests"); static LIBCORE_TESTS: CargoProject = CargoProject::new(&LIBCORE_TESTS_SRC, "coretests_target"); @@ -162,7 +158,7 @@ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[ &LIBCORE_TESTS_SRC.to_path(&runner.dirs), ); - let source_lockfile = RelPath::PATCHES.to_path(&runner.dirs).join("coretests-lock.toml"); + let source_lockfile = runner.dirs.source_dir.join("patches/coretests-lock.toml"); let target_lockfile = LIBCORE_TESTS_SRC.to_path(&runner.dirs).join("Cargo.lock"); fs::copy(source_lockfile, target_lockfile).unwrap(); @@ -267,7 +263,9 @@ pub(crate) fn run_tests( stdlib_source.clone(), ); - BUILD_EXAMPLE_OUT_DIR.ensure_fresh(dirs); + let path = BUILD_EXAMPLE_OUT_DIR.to_path(dirs); + ensure_empty_dir(&path); + runner.run_testsuite(NO_SYSROOT_SUITE); } else { eprintln!("[SKIP] no_sysroot tests"); diff --git a/compiler/rustc_codegen_cranelift/build_system/utils.rs b/compiler/rustc_codegen_cranelift/build_system/utils.rs index 22a9487a202..c2114caf869 100644 --- a/compiler/rustc_codegen_cranelift/build_system/utils.rs +++ b/compiler/rustc_codegen_cranelift/build_system/utils.rs @@ -93,7 +93,7 @@ impl CargoProject { } pub(crate) fn target_dir(&self, dirs: &Dirs) -> PathBuf { - RelPath::BUILD.join(self.target).to_path(dirs) + dirs.build_dir.join(self.target) } #[must_use] diff --git a/compiler/rustc_codegen_cranelift/config.txt b/compiler/rustc_codegen_cranelift/config.txt index 527ec5303b6..b63597f60fc 100644 --- a/compiler/rustc_codegen_cranelift/config.txt +++ b/compiler/rustc_codegen_cranelift/config.txt @@ -42,7 +42,6 @@ aot.float-minmax-pass aot.mod_bench aot.issue-72793 aot.issue-59326 -aot.polymorphize_coroutine aot.neon aot.gen_block_iterate aot.raw-dylib diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs index 3da215fe6c0..a0a381638c0 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs @@ -55,26 +55,26 @@ impl<T: ?Sized> LegacyReceiver for &mut T {} impl<T: ?Sized> LegacyReceiver for Box<T> {} #[lang = "copy"] -pub unsafe trait Copy {} - -unsafe impl Copy for bool {} -unsafe impl Copy for u8 {} -unsafe impl Copy for u16 {} -unsafe impl Copy for u32 {} -unsafe impl Copy for u64 {} -unsafe impl Copy for u128 {} -unsafe impl Copy for usize {} -unsafe impl Copy for i8 {} -unsafe impl Copy for i16 {} -unsafe impl Copy for i32 {} -unsafe impl Copy for isize {} -unsafe impl Copy for f32 {} -unsafe impl Copy for f64 {} -unsafe impl Copy for char {} -unsafe impl<'a, T: ?Sized> Copy for &'a T {} -unsafe impl<T: ?Sized> Copy for *const T {} -unsafe impl<T: ?Sized> Copy for *mut T {} -unsafe impl<T: Copy> Copy for Option<T> {} +pub trait Copy {} + +impl Copy for bool {} +impl Copy for u8 {} +impl Copy for u16 {} +impl Copy for u32 {} +impl Copy for u64 {} +impl Copy for u128 {} +impl Copy for usize {} +impl Copy for i8 {} +impl Copy for i16 {} +impl Copy for i32 {} +impl Copy for isize {} +impl Copy for f32 {} +impl Copy for f64 {} +impl Copy for char {} +impl<'a, T: ?Sized> Copy for &'a T {} +impl<T: ?Sized> Copy for *const T {} +impl<T: ?Sized> Copy for *mut T {} +impl<T: Copy> Copy for Option<T> {} #[lang = "sync"] pub unsafe trait Sync {} diff --git a/compiler/rustc_codegen_cranelift/example/polymorphize_coroutine.rs b/compiler/rustc_codegen_cranelift/example/polymorphize_coroutine.rs deleted file mode 100644 index 407da94c0f0..00000000000 --- a/compiler/rustc_codegen_cranelift/example/polymorphize_coroutine.rs +++ /dev/null @@ -1,17 +0,0 @@ -#![feature(coroutines, coroutine_trait, stmt_expr_attributes)] - -use std::ops::Coroutine; -use std::pin::Pin; - -fn main() { - run_coroutine::<i32>(); -} - -fn run_coroutine<T>() { - let mut coroutine = #[coroutine] - || { - yield; - return; - }; - Pin::new(&mut coroutine).resume(()); -} diff --git a/compiler/rustc_codegen_cranelift/example/std_example.rs b/compiler/rustc_codegen_cranelift/example/std_example.rs index 3078288c286..0b1d83c5630 100644 --- a/compiler/rustc_codegen_cranelift/example/std_example.rs +++ b/compiler/rustc_codegen_cranelift/example/std_example.rs @@ -8,6 +8,9 @@ unboxed_closures )] #![allow(internal_features)] +// FIXME once abi_unsupported_vector_types is a hard error disable the foo test when the respective +// target feature is not enabled. +#![allow(abi_unsupported_vector_types)] #[cfg(target_arch = "x86_64")] use std::arch::x86_64::*; diff --git a/compiler/rustc_codegen_cranelift/patches/0022-coretests-Disable-not-compiling-tests.patch b/compiler/rustc_codegen_cranelift/patches/0022-coretests-Disable-not-compiling-tests.patch index 1860810e7f3..161173d4765 100644 --- a/compiler/rustc_codegen_cranelift/patches/0022-coretests-Disable-not-compiling-tests.patch +++ b/compiler/rustc_codegen_cranelift/patches/0022-coretests-Disable-not-compiling-tests.patch @@ -38,7 +38,7 @@ index 42a26ae..5ac1042 100644 @@ -1,3 +1,4 @@ +#![cfg(test)] // tidy-alphabetical-start - #![cfg_attr(bootstrap, feature(const_three_way_compare))] - #![cfg_attr(bootstrap, feature(strict_provenance))] + #![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] + #![cfg_attr(test, feature(cfg_match))] -- 2.21.0 (Apple Git-122) diff --git a/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch index 59653c6e875..06840624cef 100644 --- a/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch +++ b/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch @@ -14,10 +14,9 @@ diff --git a/lib.rs b/lib.rs index 1e336bf..35e6f54 100644 --- a/lib.rs +++ b/lib.rs -@@ -2,7 +2,6 @@ - #![cfg_attr(bootstrap, feature(const_three_way_compare))] - #![cfg_attr(bootstrap, feature(strict_provenance))] - #![cfg_attr(not(bootstrap), feature(strict_provenance_lints))] +@@ -2,6 +2,5 @@ + #![cfg(test)] + // tidy-alphabetical-start -#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] #![cfg_attr(test, feature(cfg_match))] #![feature(alloc_layout_extra)] diff --git a/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch b/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch index ada35145e29..6012af6a8dd 100644 --- a/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch +++ b/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch @@ -4,22 +4,23 @@ Date: Fri, 9 Aug 2024 15:44:51 +0000 Subject: [PATCH] Disable f16 and f128 in compiler-builtins --- - library/sysroot/Cargo.toml | 2 +- + library/liballoc/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) -diff --git a/library/sysroot/Cargo.toml b/library/sysroot/Cargo.toml +diff --git a/library/liballoc/Cargo.toml b/library/liballoc/Cargo.toml index 7165c3e48af..968552ad435 100644 ---- a/library/sysroot/Cargo.toml -+++ b/library/sysroot/Cargo.toml +--- a/library/alloc/Cargo.toml ++++ b/library/alloc/Cargo.toml @@ -11,7 +11,7 @@ test = { path = "../test" } + edition = "2021" - # Forward features to the `std` crate as necessary - [features] --default = ["std_detect_file_io", "std_detect_dlsym_getauxval", "panic-unwind"] -+default = ["std_detect_file_io", "std_detect_dlsym_getauxval", "panic-unwind", "compiler-builtins-no-f16-f128"] - backtrace = ["std/backtrace"] - compiler-builtins-c = ["std/compiler-builtins-c"] - compiler-builtins-mem = ["std/compiler-builtins-mem"] + [dependencies] + core = { path = "../core" } +-compiler_builtins = { version = "=0.1.138", features = ['rustc-dep-of-std'] } ++compiler_builtins = { version = "=0.1.138", features = ['rustc-dep-of-std', 'no-f16-f128'] } + + [dev-dependencies] + rand = { version = "0.8.5", default-features = false, features = ["alloc"] } -- 2.34.1 diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain index a223cd7dbb8..8d935df4d1f 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain +++ b/compiler/rustc_codegen_cranelift/rust-toolchain @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2024-11-09" +channel = "nightly-2024-12-06" components = ["rust-src", "rustc-dev", "llvm-tools"] profile = "minimal" diff --git a/compiler/rustc_codegen_cranelift/scripts/rustc-clif.rs b/compiler/rustc_codegen_cranelift/scripts/rustc-clif.rs index 92defd21cd9..a27b9983bf1 100644 --- a/compiler/rustc_codegen_cranelift/scripts/rustc-clif.rs +++ b/compiler/rustc_codegen_cranelift/scripts/rustc-clif.rs @@ -33,6 +33,11 @@ fn main() { args.push(OsString::from("--sysroot")); args.push(OsString::from(sysroot.to_str().unwrap())); } + if passed_args.is_empty() { + // Don't pass any arguments when the user didn't pass any arguments + // either to ensure the help message is shown. + args.clear(); + } args.extend(passed_args); let rustc = if let Some(rustc) = option_env!("RUSTC") { diff --git a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh index 5b3f2a91207..54f6baff4fe 100644 --- a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh +++ b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh @@ -35,13 +35,14 @@ full-bootstrap = true local-rebuild = true [rust] +download-rustc = false codegen-backends = ["cranelift"] deny-warnings = false verbose-tests = false # The cg_clif sysroot doesn't contain llvm tools and unless llvm_tools is # disabled bootstrap will crash trying to copy llvm tools for the bootstrap # compiler. -llvm_tools = false +llvm-tools = false EOF popd diff --git a/compiler/rustc_codegen_cranelift/scripts/test_bootstrap.sh b/compiler/rustc_codegen_cranelift/scripts/test_bootstrap.sh index 770f2b6df6c..791d457993d 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_bootstrap.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_bootstrap.sh @@ -11,22 +11,5 @@ rm -r compiler/rustc_codegen_cranelift/{Cargo.*,src} cp ../Cargo.* compiler/rustc_codegen_cranelift/ cp -r ../src compiler/rustc_codegen_cranelift/src -# FIXME(rust-lang/rust#132719) remove once it doesn't break without this patch -cat <<EOF | git apply - -diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs -index 3394f2a84a0..cb980dd4d7c 100644 ---- a/src/bootstrap/src/core/build_steps/compile.rs -+++ b/src/bootstrap/src/core/build_steps/compile.rs -@@ -1976,7 +1976,7 @@ fn run(self, builder: &Builder<'_>) -> Compiler { - } - } - -- { -+ if builder.config.llvm_enabled(target_compiler.host) && builder.config.llvm_tools_enabled { - // \`llvm-strip\` is used by rustc, which is actually just a symlink to \`llvm-objcopy\`, - // so copy and rename \`llvm-objcopy\`. - let src_exe = exe("llvm-objcopy", target_compiler.host); -EOF - ./x.py build --stage 1 library/std popd diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index a820da286f5..e291ec20464 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -57,6 +57,7 @@ rm tests/ui/asm/x86_64/issue-96797.rs # const and sym inline asm operands don't rm tests/ui/asm/x86_64/goto.rs # inline asm labels not supported rm tests/ui/simd/simd-bitmask-notpow2.rs # non-pow-of-2 simd vector sizes rm -r tests/run-make/embed-source-dwarf # embedding sources in debuginfo +rm tests/ui/simd-abi-checks.rs # vector types >128bits not yet supported # requires LTO rm -r tests/run-make/cdylib @@ -75,6 +76,8 @@ rm -r tests/ui/instrument-coverage/ rm tests/ui/half-open-range-patterns/half-open-range-pats-semantics.rs rm tests/ui/asm/aarch64/type-f16.rs rm tests/ui/float/conv-bits-runtime-const.rs +rm tests/ui/consts/const-eval/float_methods.rs +rm tests/ui/match/match-float.rs # optimization tests # ================== diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index cab5b35c18d..2466bfe60c7 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -125,8 +125,9 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { returns: Vec<AbiParam>, args: &[Value], ) -> Cow<'_, [Value]> { - if self.tcx.sess.target.is_like_windows { - let (mut params, mut args): (Vec<_>, Vec<_>) = params + // Pass i128 arguments by-ref on Windows. + let (params, args): (Vec<_>, Cow<'_, [_]>) = if self.tcx.sess.target.is_like_windows { + let (params, args): (Vec<_>, Vec<_>) = params .into_iter() .zip(args) .map(|(param, &arg)| { @@ -140,29 +141,42 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { }) .unzip(); - let indirect_ret_val = returns.len() == 1 && returns[0].value_type == types::I128; + (params, args.into()) + } else { + (params, args.into()) + }; - if indirect_ret_val { - params.insert(0, AbiParam::new(self.pointer_type)); - let ret_ptr = self.create_stack_slot(16, 16); - args.insert(0, ret_ptr.get_addr(self)); - self.lib_call_unadjusted(name, params, vec![], &args); - return Cow::Owned(vec![ret_ptr.load(self, types::I128, MemFlags::trusted())]); + // Return i128 using a return area pointer on Windows and s390x. + let adjust_ret_param = + if self.tcx.sess.target.is_like_windows || self.tcx.sess.target.arch == "s390x" { + returns.len() == 1 && returns[0].value_type == types::I128 } else { - return self.lib_call_unadjusted(name, params, returns, &args); - } - } + false + }; + + if adjust_ret_param { + let mut params = params; + let mut args = args.to_vec(); + + params.insert(0, AbiParam::new(self.pointer_type)); + let ret_ptr = self.create_stack_slot(16, 16); + args.insert(0, ret_ptr.get_addr(self)); - self.lib_call_unadjusted(name, params, returns, args) + self.lib_call_unadjusted(name, params, vec![], &args); + + Cow::Owned(vec![ret_ptr.load(self, types::I128, MemFlags::trusted())]) + } else { + Cow::Borrowed(self.lib_call_unadjusted(name, params, returns, &args)) + } } - pub(crate) fn lib_call_unadjusted( + fn lib_call_unadjusted( &mut self, name: &str, params: Vec<AbiParam>, returns: Vec<AbiParam>, args: &[Value], - ) -> Cow<'_, [Value]> { + ) -> &[Value] { let sig = Signature { params, returns, call_conv: self.target_config.default_call_conv }; let func_id = self.module.declare_function(name, Linkage::Import, &sig).unwrap(); let func_ref = self.module.declare_func_in_func(func_id, &mut self.bcx.func); @@ -175,7 +189,7 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { } let results = self.bcx.inst_results(call_inst); assert!(results.len() <= 2, "{}", results.len()); - Cow::Borrowed(results) + results } } @@ -380,8 +394,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( def_id, fn_args, source_info.span, - ) - .polymorphize(fx.tcx); + ); if is_call_from_compiler_builtins_to_upstream_monomorphization(fx.tcx, instance) { if target.is_some() { @@ -684,7 +697,7 @@ pub(crate) fn codegen_drop<'tcx>( target: BasicBlock, ) { let ty = drop_place.layout().ty; - let drop_instance = Instance::resolve_drop_in_place(fx.tcx, ty).polymorphize(fx.tcx); + let drop_instance = Instance::resolve_drop_in_place(fx.tcx, ty); if let ty::InstanceKind::DropGlue(_, None) | ty::InstanceKind::AsyncDropGlueCtorShim(_, None) = drop_instance.def diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 06cc5754894..34066eb83fc 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -6,6 +6,7 @@ use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use cranelift_module::ModuleError; use rustc_ast::InlineAsmOptions; use rustc_codegen_ssa::base::is_call_from_compiler_builtins_to_upstream_monomorphization; +use rustc_data_structures::profiling::SelfProfilerRef; use rustc_index::IndexVec; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::InlineAsmMacro; @@ -16,6 +17,7 @@ use rustc_middle::ty::print::with_no_trimmed_paths; use crate::constant::ConstantCx; use crate::debuginfo::{FunctionDebugContext, TypeDebugContext}; +use crate::enable_verifier; use crate::inline_asm::codegen_naked_asm; use crate::prelude::*; use crate::pretty_clif::CommentWriter; @@ -169,12 +171,13 @@ pub(crate) fn codegen_fn<'tcx>( pub(crate) fn compile_fn( cx: &mut crate::CodegenCx, + profiler: &SelfProfilerRef, cached_context: &mut Context, module: &mut dyn Module, codegened_func: CodegenedFunction, ) { let _timer = - cx.profiler.generic_activity_with_arg("compile function", &*codegened_func.symbol_name); + profiler.generic_activity_with_arg("compile function", &*codegened_func.symbol_name); let clif_comments = codegened_func.clif_comments; @@ -212,7 +215,7 @@ pub(crate) fn compile_fn( }; // Define function - cx.profiler.generic_activity("define function").run(|| { + profiler.generic_activity("define function").run(|| { context.want_disasm = cx.should_write_ir; match module.define_function(codegened_func.func_id, context) { Ok(()) => {} @@ -253,7 +256,7 @@ pub(crate) fn compile_fn( // Define debuginfo for function let debug_context = &mut cx.debug_context; - cx.profiler.generic_activity("generate debug info").run(|| { + profiler.generic_activity("generate debug info").run(|| { if let Some(debug_context) = debug_context { codegened_func.func_debug_cx.unwrap().finalize( debug_context, @@ -264,11 +267,11 @@ pub(crate) fn compile_fn( }); } -pub(crate) fn verify_func( - tcx: TyCtxt<'_>, - writer: &crate::pretty_clif::CommentWriter, - func: &Function, -) { +fn verify_func(tcx: TyCtxt<'_>, writer: &crate::pretty_clif::CommentWriter, func: &Function) { + if !enable_verifier(tcx.sess) { + return; + } + tcx.prof.generic_activity("verify clif ir").run(|| { let flags = cranelift_codegen::settings::Flags::new(cranelift_codegen::settings::builder()); match cranelift_codegen::verify_function(&func, &flags) { @@ -670,8 +673,7 @@ fn codegen_stmt<'tcx>( def_id, args, ) - .unwrap() - .polymorphize(fx.tcx), + .unwrap(), ); let func_addr = fx.bcx.ins().func_addr(fx.pointer_type, func_ref); lval.write_cvalue(fx, CValue::by_val(func_addr, to_layout)); @@ -757,8 +759,7 @@ fn codegen_stmt<'tcx>( def_id, args, ty::ClosureKind::FnOnce, - ) - .polymorphize(fx.tcx); + ); let func_ref = fx.get_function_ref(instance); let func_addr = fx.bcx.ins().func_addr(fx.pointer_type, func_ref); lval.write_cvalue(fx, CValue::by_val(func_addr, lval.layout())); @@ -1084,7 +1085,7 @@ fn codegen_panic_inner<'tcx>( let def_id = fx.tcx.require_lang_item(lang_item, span); - let instance = Instance::mono(fx.tcx, def_id).polymorphize(fx.tcx); + let instance = Instance::mono(fx.tcx, def_id); if is_call_from_compiler_builtins_to_upstream_monomorphization(fx.tcx, instance) { fx.bcx.ins().trap(TrapCode::user(2).unwrap()); diff --git a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs index b6a4769e031..734574338d0 100644 --- a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs +++ b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs @@ -81,26 +81,6 @@ pub(crate) fn maybe_codegen_checked<'tcx>( match bin_op { BinOp::BitAnd | BinOp::BitOr | BinOp::BitXor => unreachable!(), BinOp::Add | BinOp::Sub => None, - BinOp::Mul if is_signed => { - let out_ty = Ty::new_tup(fx.tcx, &[lhs.layout().ty, fx.tcx.types.bool]); - let oflow = CPlace::new_stack_slot(fx, fx.layout_of(fx.tcx.types.i32)); - let lhs = lhs.load_scalar(fx); - let rhs = rhs.load_scalar(fx); - let oflow_ptr = oflow.to_ptr().get_addr(fx); - let res = fx.lib_call_unadjusted( - "__muloti4", - vec![ - AbiParam::new(types::I128), - AbiParam::new(types::I128), - AbiParam::new(fx.pointer_type), - ], - vec![AbiParam::new(types::I128)], - &[lhs, rhs, oflow_ptr], - )[0]; - let oflow = oflow.to_cvalue(fx).load_scalar(fx); - let oflow = fx.bcx.ins().ireduce(types::I8, oflow); - Some(CValue::by_val_pair(res, oflow, fx.layout_of(out_ty))) - } BinOp::Mul => { let out_ty = Ty::new_tup(fx.tcx, &[lhs.layout().ty, fx.tcx.types.bool]); let out_place = CPlace::new_stack_slot(fx, fx.layout_of(out_ty)); @@ -110,7 +90,12 @@ pub(crate) fn maybe_codegen_checked<'tcx>( AbiParam::new(types::I128), ]; let args = [out_place.to_ptr().get_addr(fx), lhs.load_scalar(fx), rhs.load_scalar(fx)]; - fx.lib_call("__rust_u128_mulo", param_types, vec![], &args); + fx.lib_call( + if is_signed { "__rust_i128_mulo" } else { "__rust_u128_mulo" }, + param_types, + vec![], + &args, + ); Some(out_place.to_cvalue(fx)) } BinOp::AddUnchecked | BinOp::SubUnchecked | BinOp::MulUnchecked => unreachable!(), diff --git a/compiler/rustc_codegen_cranelift/src/concurrency_limiter.rs b/compiler/rustc_codegen_cranelift/src/concurrency_limiter.rs index 2093b49ff31..b5a81fc11d5 100644 --- a/compiler/rustc_codegen_cranelift/src/concurrency_limiter.rs +++ b/compiler/rustc_codegen_cranelift/src/concurrency_limiter.rs @@ -1,8 +1,7 @@ use std::sync::{Arc, Condvar, Mutex}; -use jobserver::HelperThread; +use rustc_data_structures::jobserver::{self, HelperThread}; use rustc_errors::DiagCtxtHandle; -use rustc_session::Session; // FIXME don't panic when a worker thread panics @@ -14,14 +13,13 @@ pub(super) struct ConcurrencyLimiter { } impl ConcurrencyLimiter { - pub(super) fn new(sess: &Session, pending_jobs: usize) -> Self { + pub(super) fn new(pending_jobs: usize) -> Self { let state = Arc::new(Mutex::new(state::ConcurrencyLimiterState::new(pending_jobs))); let available_token_condvar = Arc::new(Condvar::new()); let state_helper = state.clone(); let available_token_condvar_helper = available_token_condvar.clone(); - let helper_thread = sess - .jobserver + let helper_thread = jobserver::client() .clone() .into_helper_thread(move |token| { let mut state = state_helper.lock().unwrap(); @@ -113,7 +111,7 @@ impl Drop for ConcurrencyLimiterToken { } mod state { - use jobserver::Acquired; + use rustc_data_structures::jobserver::Acquired; #[derive(Debug)] pub(super) struct ConcurrencyLimiterState { diff --git a/compiler/rustc_codegen_cranelift/src/config.rs b/compiler/rustc_codegen_cranelift/src/config.rs index 12bce680d9e..d784f6e9d9e 100644 --- a/compiler/rustc_codegen_cranelift/src/config.rs +++ b/compiler/rustc_codegen_cranelift/src/config.rs @@ -1,10 +1,3 @@ -use std::env; -use std::str::FromStr; - -fn bool_env_var(key: &str) -> bool { - env::var(key).as_deref() == Ok("1") -} - /// The mode to use for compilation. #[derive(Copy, Clone, Debug)] pub enum CodegenMode { @@ -16,19 +9,6 @@ pub enum CodegenMode { JitLazy, } -impl FromStr for CodegenMode { - type Err = String; - - fn from_str(s: &str) -> Result<Self, Self::Err> { - match s { - "aot" => Ok(CodegenMode::Aot), - "jit" => Ok(CodegenMode::Jit), - "jit-lazy" => Ok(CodegenMode::JitLazy), - _ => Err(format!("Unknown codegen mode `{}`", s)), - } - } -} - /// Configuration of cg_clif as passed in through `-Cllvm-args` and various env vars. #[derive(Clone, Debug)] pub struct BackendConfig { @@ -41,51 +21,22 @@ pub struct BackendConfig { /// /// Defaults to the value of `CG_CLIF_JIT_ARGS`. pub jit_args: Vec<String>, - - /// Enable the Cranelift ir verifier for all compilation passes. If not set it will only run - /// once before passing the clif ir to Cranelift for compilation. - /// - /// Defaults to true when the `CG_CLIF_ENABLE_VERIFIER` env var is set to 1 or when cg_clif is - /// compiled with debug assertions enabled or false otherwise. Can be set using - /// `-Cllvm-args=enable_verifier=...`. - pub enable_verifier: bool, - - /// Don't cache object files in the incremental cache. Useful during development of cg_clif - /// to make it possible to use incremental mode for all analyses performed by rustc without - /// caching object files when their content should have been changed by a change to cg_clif. - /// - /// Defaults to true when the `CG_CLIF_DISABLE_INCR_CACHE` env var is set to 1 or false - /// otherwise. Can be set using `-Cllvm-args=disable_incr_cache=...`. - pub disable_incr_cache: bool, -} - -impl Default for BackendConfig { - fn default() -> Self { - BackendConfig { - codegen_mode: CodegenMode::Aot, - jit_args: { - match std::env::var("CG_CLIF_JIT_ARGS") { - Ok(args) => args.split(' ').map(|arg| arg.to_string()).collect(), - Err(std::env::VarError::NotPresent) => vec![], - Err(std::env::VarError::NotUnicode(s)) => { - panic!("CG_CLIF_JIT_ARGS not unicode: {:?}", s); - } - } - }, - enable_verifier: cfg!(debug_assertions) || bool_env_var("CG_CLIF_ENABLE_VERIFIER"), - disable_incr_cache: bool_env_var("CG_CLIF_DISABLE_INCR_CACHE"), - } - } } impl BackendConfig { /// Parse the configuration passed in using `-Cllvm-args`. pub fn from_opts(opts: &[String]) -> Result<Self, String> { - fn parse_bool(name: &str, value: &str) -> Result<bool, String> { - value.parse().map_err(|_| format!("failed to parse value `{}` for {}", value, name)) - } + let mut config = BackendConfig { + codegen_mode: CodegenMode::Aot, + jit_args: match std::env::var("CG_CLIF_JIT_ARGS") { + Ok(args) => args.split(' ').map(|arg| arg.to_string()).collect(), + Err(std::env::VarError::NotPresent) => vec![], + Err(std::env::VarError::NotUnicode(s)) => { + panic!("CG_CLIF_JIT_ARGS not unicode: {:?}", s); + } + }, + }; - let mut config = BackendConfig::default(); for opt in opts { if opt.starts_with("-import-instr-limit") { // Silently ignore -import-instr-limit. It is set by rust's build system even when @@ -94,9 +45,14 @@ impl BackendConfig { } if let Some((name, value)) = opt.split_once('=') { match name { - "mode" => config.codegen_mode = value.parse()?, - "enable_verifier" => config.enable_verifier = parse_bool(name, value)?, - "disable_incr_cache" => config.disable_incr_cache = parse_bool(name, value)?, + "mode" => { + config.codegen_mode = match value { + "aot" => CodegenMode::Aot, + "jit" => CodegenMode::Jit, + "jit-lazy" => CodegenMode::JitLazy, + _ => return Err(format!("Unknown codegen mode `{}`", value)), + }; + } _ => return Err(format!("Unknown option `{}`", name)), } } else { diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index abe6085b04f..3e7b81a96b6 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -452,8 +452,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant let data_id = match reloc_target_alloc { GlobalAlloc::Function { instance, .. } => { assert_eq!(addend, 0); - let func_id = - crate::abi::import_function(tcx, module, instance.polymorphize(tcx)); + let func_id = crate::abi::import_function(tcx, module, instance); let local_func_id = module.declare_func_in_data(func_id, &mut data); data.write_function_addr(offset.bytes() as u32, local_func_id); continue; diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index 8eab73ad5f9..4fc30b69123 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -1,6 +1,7 @@ //! The AOT driver uses [`cranelift_object`] to write object files suitable for linking into a //! standalone executable. +use std::env; use std::fs::{self, File}; use std::io::BufWriter; use std::path::{Path, PathBuf}; @@ -25,13 +26,18 @@ use rustc_middle::mir::mono::{CodegenUnit, MonoItem}; use rustc_session::Session; use rustc_session::config::{DebugInfo, OutFileName, OutputFilenames, OutputType}; -use crate::BackendConfig; +use crate::CodegenCx; +use crate::base::CodegenedFunction; use crate::concurrency_limiter::{ConcurrencyLimiter, ConcurrencyLimiterToken}; use crate::debuginfo::TypeDebugContext; use crate::global_asm::GlobalAsmConfig; use crate::prelude::*; use crate::unwind_module::UnwindModule; +fn disable_incr_cache() -> bool { + env::var("CG_CLIF_DISABLE_INCR_CACHE").as_deref() == Ok("1") +} + struct ModuleCodegenResult { module_regular: CompiledModule, module_global_asm: Option<CompiledModule>, @@ -63,10 +69,10 @@ impl OngoingCodegen { self, sess: &Session, outputs: &OutputFilenames, - backend_config: &BackendConfig, ) -> (CodegenResults, FxIndexMap<WorkProductId, WorkProduct>) { let mut work_products = FxIndexMap::default(); let mut modules = vec![]; + let disable_incr_cache = disable_incr_cache(); for module_codegen in self.modules { let module_codegen_result = match module_codegen { @@ -87,7 +93,7 @@ impl OngoingCodegen { if let Some((work_product_id, work_product)) = existing_work_product { work_products.insert(work_product_id, work_product); } else { - let work_product = if backend_config.disable_incr_cache { + let work_product = if disable_incr_cache { None } else if let Some(module_global_asm) = &module_global_asm { rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir( @@ -322,12 +328,8 @@ fn produce_final_output_artifacts( // These are used in linking steps and will be cleaned up afterward. } -fn make_module( - sess: &Session, - backend_config: &BackendConfig, - name: String, -) -> UnwindModule<ObjectModule> { - let isa = crate::build_isa(sess, backend_config); +fn make_module(sess: &Session, name: String) -> UnwindModule<ObjectModule> { + let isa = crate::build_isa(sess); let mut builder = ObjectBuilder::new(isa, name + ".o", cranelift_module::default_libcall_names()).unwrap(); @@ -412,7 +414,13 @@ fn emit_module( Err(err) => return Err(format!("error writing object file: {}", err)), }; - prof.artifact_size("object_file", &*name, file.metadata().unwrap().len()); + if prof.enabled() { + prof.artifact_size( + "object_file", + tmp_file.file_name().unwrap().to_string_lossy(), + file.metadata().unwrap().len(), + ); + } Ok(CompiledModule { name, @@ -486,91 +494,101 @@ fn reuse_workproduct_for_cgu( }) } +fn codegen_cgu_content( + tcx: TyCtxt<'_>, + module: &mut dyn Module, + cgu_name: rustc_span::Symbol, +) -> (CodegenCx, Vec<CodegenedFunction>) { + let _timer = tcx.prof.generic_activity_with_arg("codegen cgu", cgu_name.as_str()); + + let cgu = tcx.codegen_unit(cgu_name); + let mono_items = cgu.items_in_deterministic_order(tcx); + + let mut cx = crate::CodegenCx::new( + tcx, + module.isa(), + tcx.sess.opts.debuginfo != DebugInfo::None, + cgu_name, + ); + let mut type_dbg = TypeDebugContext::default(); + super::predefine_mono_items(tcx, module, &mono_items); + let mut codegened_functions = vec![]; + for (mono_item, _) in mono_items { + match mono_item { + MonoItem::Fn(inst) => { + if let Some(codegened_function) = crate::base::codegen_fn( + tcx, + &mut cx, + &mut type_dbg, + Function::new(), + module, + inst, + ) { + codegened_functions.push(codegened_function); + } + } + MonoItem::Static(def_id) => { + let data_id = crate::constant::codegen_static(tcx, module, def_id); + if let Some(debug_context) = &mut cx.debug_context { + debug_context.define_static(tcx, &mut type_dbg, def_id, data_id); + } + } + MonoItem::GlobalAsm(item_id) => { + crate::global_asm::codegen_global_asm_item(tcx, &mut cx.global_asm, item_id); + } + } + } + crate::main_shim::maybe_create_entry_wrapper(tcx, module, false, cgu.is_primary()); + + (cx, codegened_functions) +} + fn module_codegen( tcx: TyCtxt<'_>, - (backend_config, global_asm_config, cgu_name, token): ( - BackendConfig, + (global_asm_config, cgu_name, token): ( Arc<GlobalAsmConfig>, rustc_span::Symbol, ConcurrencyLimiterToken, ), ) -> OngoingModuleCodegen { - let (cgu_name, mut cx, mut module, codegened_functions) = - tcx.prof.generic_activity_with_arg("codegen cgu", cgu_name.as_str()).run(|| { - let cgu = tcx.codegen_unit(cgu_name); - let mono_items = cgu.items_in_deterministic_order(tcx); - - let mut module = make_module(tcx.sess, &backend_config, cgu_name.as_str().to_string()); - - let mut cx = crate::CodegenCx::new( - tcx, - module.isa(), - tcx.sess.opts.debuginfo != DebugInfo::None, - cgu_name, - ); - let mut type_dbg = TypeDebugContext::default(); - super::predefine_mono_items(tcx, &mut module, &mono_items); - let mut codegened_functions = vec![]; - for (mono_item, _) in mono_items { - match mono_item { - MonoItem::Fn(inst) => { - if let Some(codegened_function) = crate::base::codegen_fn( - tcx, - &mut cx, - &mut type_dbg, - Function::new(), - &mut module, - inst, - ) { - codegened_functions.push(codegened_function); - } - } - MonoItem::Static(def_id) => { - let data_id = crate::constant::codegen_static(tcx, &mut module, def_id); - if let Some(debug_context) = &mut cx.debug_context { - debug_context.define_static(tcx, &mut type_dbg, def_id, data_id); - } - } - MonoItem::GlobalAsm(item_id) => { - crate::global_asm::codegen_global_asm_item( - tcx, - &mut cx.global_asm, - item_id, - ); - } - } - } - crate::main_shim::maybe_create_entry_wrapper(tcx, &mut module, false, cgu.is_primary()); + let mut module = make_module(tcx.sess, cgu_name.as_str().to_string()); - let cgu_name = cgu.name().as_str().to_owned(); + let (mut cx, codegened_functions) = codegen_cgu_content(tcx, &mut module, cgu_name); - (cgu_name, cx, module, codegened_functions) - }); + let cgu_name = cgu_name.as_str().to_owned(); let producer = crate::debuginfo::producer(tcx.sess); + let profiler = tcx.prof.clone(); + OngoingModuleCodegen::Async(std::thread::spawn(move || { - cx.profiler.clone().generic_activity_with_arg("compile functions", &*cgu_name).run(|| { + profiler.clone().generic_activity_with_arg("compile functions", &*cgu_name).run(|| { cranelift_codegen::timing::set_thread_profiler(Box::new(super::MeasuremeProfiler( - cx.profiler.clone(), + profiler.clone(), ))); let mut cached_context = Context::new(); for codegened_func in codegened_functions { - crate::base::compile_fn(&mut cx, &mut cached_context, &mut module, codegened_func); + crate::base::compile_fn( + &mut cx, + &profiler, + &mut cached_context, + &mut module, + codegened_func, + ); } }); let global_asm_object_file = - cx.profiler.generic_activity_with_arg("compile assembly", &*cgu_name).run(|| { + profiler.generic_activity_with_arg("compile assembly", &*cgu_name).run(|| { crate::global_asm::compile_global_asm(&global_asm_config, &cgu_name, &cx.global_asm) })?; let codegen_result = - cx.profiler.generic_activity_with_arg("write object file", &*cgu_name).run(|| { + profiler.generic_activity_with_arg("write object file", &*cgu_name).run(|| { emit_cgu( &global_asm_config.output_filenames, - &cx.profiler, + &profiler, cgu_name, module, cx.debug_context, @@ -583,9 +601,63 @@ fn module_codegen( })) } +fn emit_metadata_module(tcx: TyCtxt<'_>, metadata: &EncodedMetadata) -> CompiledModule { + use rustc_middle::mir::mono::CodegenUnitNameBuilder; + + let _timer = tcx.sess.timer("write compressed metadata"); + + let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx); + let metadata_cgu_name = cgu_name_builder + .build_cgu_name(LOCAL_CRATE, ["crate"], Some("metadata")) + .as_str() + .to_string(); + + let tmp_file = + tcx.output_filenames(()).temp_path(OutputType::Metadata, Some(&metadata_cgu_name)); + + let symbol_name = rustc_middle::middle::exported_symbols::metadata_symbol_name(tcx); + let obj = create_compressed_metadata_file(tcx.sess, metadata, &symbol_name); + + if let Err(err) = std::fs::write(&tmp_file, obj) { + tcx.dcx().fatal(format!("error writing metadata object file: {}", err)); + } + + CompiledModule { + name: metadata_cgu_name, + kind: ModuleKind::Metadata, + object: Some(tmp_file), + dwarf_object: None, + bytecode: None, + assembly: None, + llvm_ir: None, + } +} + +fn emit_allocator_module(tcx: TyCtxt<'_>) -> Option<CompiledModule> { + let mut allocator_module = make_module(tcx.sess, "allocator_shim".to_string()); + let created_alloc_shim = crate::allocator::codegen(tcx, &mut allocator_module); + + if created_alloc_shim { + let product = allocator_module.finish(); + + match emit_module( + tcx.output_filenames(()), + &tcx.sess.prof, + product.object, + ModuleKind::Allocator, + "allocator_shim".to_owned(), + &crate::debuginfo::producer(tcx.sess), + ) { + Ok(allocator_module) => Some(allocator_module), + Err(err) => tcx.dcx().fatal(err), + } + } else { + None + } +} + pub(crate) fn run_aot( tcx: TyCtxt<'_>, - backend_config: BackendConfig, metadata: EncodedMetadata, need_metadata_module: bool, ) -> Box<OngoingCodegen> { @@ -607,7 +679,7 @@ pub(crate) fn run_aot( metadata_module: None, metadata, crate_info: CrateInfo::new(tcx, target_cpu), - concurrency_limiter: ConcurrencyLimiter::new(tcx.sess, 0), + concurrency_limiter: ConcurrencyLimiter::new(0), }); }; @@ -631,14 +703,15 @@ pub(crate) fn run_aot( let global_asm_config = Arc::new(crate::global_asm::GlobalAsmConfig::new(tcx)); + let disable_incr_cache = disable_incr_cache(); let (todo_cgus, done_cgus) = cgus.into_iter().enumerate().partition::<Vec<_>, _>(|&(i, _)| match cgu_reuse[i] { - _ if backend_config.disable_incr_cache => true, + _ if disable_incr_cache => true, CguReuse::No => true, CguReuse::PreLto | CguReuse::PostLto => false, }); - let concurrency_limiter = IntoDynSyncSend(ConcurrencyLimiter::new(tcx.sess, todo_cgus.len())); + let concurrency_limiter = IntoDynSyncSend(ConcurrencyLimiter::new(todo_cgus.len())); let modules = tcx.sess.time("codegen mono items", || { let mut modules: Vec<_> = par_map(todo_cgus, |(_, cgu)| { @@ -647,12 +720,7 @@ pub(crate) fn run_aot( .with_task( dep_node, tcx, - ( - backend_config.clone(), - global_asm_config.clone(), - cgu.name(), - concurrency_limiter.acquire(tcx.dcx()), - ), + (global_asm_config.clone(), cgu.name(), concurrency_limiter.acquire(tcx.dcx())), module_codegen, Some(rustc_middle::dep_graph::hash_result), ) @@ -666,62 +734,10 @@ pub(crate) fn run_aot( modules }); - let mut allocator_module = make_module(tcx.sess, &backend_config, "allocator_shim".to_string()); - let created_alloc_shim = crate::allocator::codegen(tcx, &mut allocator_module); - - let allocator_module = if created_alloc_shim { - let product = allocator_module.finish(); + let allocator_module = emit_allocator_module(tcx); - match emit_module( - tcx.output_filenames(()), - &tcx.sess.prof, - product.object, - ModuleKind::Allocator, - "allocator_shim".to_owned(), - &crate::debuginfo::producer(tcx.sess), - ) { - Ok(allocator_module) => Some(allocator_module), - Err(err) => tcx.dcx().fatal(err), - } - } else { - None - }; - - let metadata_module = if need_metadata_module { - let (metadata_cgu_name, tmp_file) = tcx.sess.time("write compressed metadata", || { - use rustc_middle::mir::mono::CodegenUnitNameBuilder; - - let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx); - let metadata_cgu_name = cgu_name_builder - .build_cgu_name(LOCAL_CRATE, ["crate"], Some("metadata")) - .as_str() - .to_string(); - - let tmp_file = - tcx.output_filenames(()).temp_path(OutputType::Metadata, Some(&metadata_cgu_name)); - - let symbol_name = rustc_middle::middle::exported_symbols::metadata_symbol_name(tcx); - let obj = create_compressed_metadata_file(tcx.sess, &metadata, &symbol_name); - - if let Err(err) = std::fs::write(&tmp_file, obj) { - tcx.dcx().fatal(format!("error writing metadata object file: {}", err)); - } - - (metadata_cgu_name, tmp_file) - }); - - Some(CompiledModule { - name: metadata_cgu_name, - kind: ModuleKind::Metadata, - object: Some(tmp_file), - dwarf_object: None, - bytecode: None, - assembly: None, - llvm_ir: None, - }) - } else { - None - }; + let metadata_module = + if need_metadata_module { Some(emit_metadata_module(tcx, &metadata)) } else { None }; Box::new(OngoingCodegen { modules, diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs index ae9578eeffb..4be4291021d 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs @@ -11,12 +11,12 @@ use cranelift_jit::{JITBuilder, JITModule}; use rustc_codegen_ssa::CrateInfo; use rustc_middle::mir::mono::MonoItem; use rustc_session::Session; -use rustc_span::Symbol; +use rustc_span::sym; use crate::debuginfo::TypeDebugContext; use crate::prelude::*; use crate::unwind_module::UnwindModule; -use crate::{BackendConfig, CodegenCx, CodegenMode}; +use crate::{CodegenCx, CodegenMode}; struct JitState { jit_module: UnwindModule<JITModule>, @@ -59,14 +59,10 @@ impl UnsafeMessage { } } -fn create_jit_module( - tcx: TyCtxt<'_>, - backend_config: &BackendConfig, - hotswap: bool, -) -> (UnwindModule<JITModule>, CodegenCx) { +fn create_jit_module(tcx: TyCtxt<'_>, hotswap: bool) -> (UnwindModule<JITModule>, CodegenCx) { let crate_info = CrateInfo::new(tcx, "dummy_target_cpu".to_string()); - let isa = crate::build_isa(tcx.sess, backend_config); + let isa = crate::build_isa(tcx.sess); let mut jit_builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names()); jit_builder.hotswap(hotswap); crate::compiler_builtins::register_functions_for_jit(&mut jit_builder); @@ -81,7 +77,7 @@ fn create_jit_module( (jit_module, cx) } -pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! { +pub(crate) fn run_jit(tcx: TyCtxt<'_>, codegen_mode: CodegenMode, jit_args: Vec<String>) -> ! { if !tcx.sess.opts.output_types.should_codegen() { tcx.dcx().fatal("JIT mode doesn't work with `cargo check`"); } @@ -90,11 +86,8 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! { tcx.dcx().fatal("can't jit non-executable crate"); } - let (mut jit_module, mut cx) = create_jit_module( - tcx, - &backend_config, - matches!(backend_config.codegen_mode, CodegenMode::JitLazy), - ); + let (mut jit_module, mut cx) = + create_jit_module(tcx, matches!(codegen_mode, CodegenMode::JitLazy)); let mut cached_context = Context::new(); let (_, cgus) = tcx.collect_and_partition_mono_items(()); @@ -110,7 +103,7 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! { super::predefine_mono_items(tcx, &mut jit_module, &mono_items); for (mono_item, _) in mono_items { match mono_item { - MonoItem::Fn(inst) => match backend_config.codegen_mode { + MonoItem::Fn(inst) => match codegen_mode { CodegenMode::Aot => unreachable!(), CodegenMode::Jit => { codegen_and_compile_fn( @@ -151,7 +144,7 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! { ); let args = std::iter::once(&*tcx.crate_name(LOCAL_CRATE).as_str().to_string()) - .chain(backend_config.jit_args.iter().map(|arg| &**arg)) + .chain(jit_args.iter().map(|arg| &**arg)) .map(|arg| CString::new(arg).unwrap()) .collect::<Vec<_>>(); @@ -211,7 +204,7 @@ pub(crate) fn codegen_and_compile_fn<'tcx>( instance: Instance<'tcx>, ) { cranelift_codegen::timing::set_thread_profiler(Box::new(super::MeasuremeProfiler( - cx.profiler.clone(), + tcx.prof.clone(), ))); tcx.prof.generic_activity("codegen and compile fn").run(|| { @@ -227,7 +220,7 @@ pub(crate) fn codegen_and_compile_fn<'tcx>( module, instance, ) { - crate::base::compile_fn(cx, cached_context, module, codegened_func); + crate::base::compile_fn(cx, &tcx.prof, cached_context, module, codegened_func); } }); } @@ -294,12 +287,7 @@ fn dep_symbol_lookup_fn( let mut dylib_paths = Vec::new(); - let data = &crate_info - .dependency_formats - .iter() - .find(|(crate_type, _data)| *crate_type == rustc_session::config::CrateType::Executable) - .unwrap() - .1; + let data = &crate_info.dependency_formats[&rustc_session::config::CrateType::Executable].1; // `used_crates` is in reverse postorder in terms of dependencies. Reverse the order here to // get a postorder which ensures that all dependencies of a dylib are loaded before the dylib // itself. This helps the dynamic linker to find dylibs not in the regular dynamic library diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs index d74c366a87f..33726056cc1 100644 --- a/compiler/rustc_codegen_cranelift/src/inline_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs @@ -102,13 +102,12 @@ pub(crate) fn codegen_inline_asm_terminator<'tcx>( // Pass a wrapper rather than the function itself as the function itself may not // be exported from the main codegen unit and may thus be unreachable from the // object file created by an external assembler. - let inline_asm_index = fx.cx.inline_asm_index.get(); - fx.cx.inline_asm_index.set(inline_asm_index + 1); let wrapper_name = format!( "__inline_asm_{}_wrapper_n{}", fx.cx.cgu_name.as_str().replace('.', "__").replace('-', "_"), - inline_asm_index + fx.cx.inline_asm_index ); + fx.cx.inline_asm_index += 1; let sig = get_function_sig(fx.tcx, fx.target_config.default_call_conv, instance); create_wrapper_function(fx.module, sig, &wrapper_name, symbol.name); @@ -167,13 +166,12 @@ pub(crate) fn codegen_inline_asm_inner<'tcx>( asm_gen.allocate_registers(); asm_gen.allocate_stack_slots(); - let inline_asm_index = fx.cx.inline_asm_index.get(); - fx.cx.inline_asm_index.set(inline_asm_index + 1); let asm_name = format!( "__inline_asm_{}_n{}", fx.cx.cgu_name.as_str().replace('.', "__").replace('-', "_"), - inline_asm_index + fx.cx.inline_asm_index ); + fx.cx.inline_asm_index += 1; let generated_asm = asm_gen.generate_asm_wrapper(&asm_name); fx.cx.global_asm.push_str(&generated_asm); @@ -266,13 +264,12 @@ pub(crate) fn codegen_naked_asm<'tcx>( // Pass a wrapper rather than the function itself as the function itself may not // be exported from the main codegen unit and may thus be unreachable from the // object file created by an external assembler. - let inline_asm_index = cx.inline_asm_index.get(); - cx.inline_asm_index.set(inline_asm_index + 1); let wrapper_name = format!( "__inline_asm_{}_wrapper_n{}", cx.cgu_name.as_str().replace('.', "__").replace('-', "_"), - inline_asm_index + cx.inline_asm_index ); + cx.inline_asm_index += 1; let sig = get_function_sig(tcx, module.target_config().default_call_conv, instance); create_wrapper_function(module, sig, &wrapper_name, symbol.name); diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 3318c0797ec..5f1b71eff6b 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -1270,8 +1270,7 @@ fn codegen_regular_intrinsic_call<'tcx>( } sym::cold_path => { - // This is a no-op. The intrinsic is just a hint to the optimizer. - // We still have an impl here to avoid it being turned into a call. + fx.bcx.set_cold_block(fx.bcx.current_block().unwrap()); } // Unimplemented intrinsics must have a fallback body. The fallback body is obtained diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index cac9975f04c..c38ef82e5b8 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -12,7 +12,6 @@ #![warn(unused_lifetimes)] // tidy-alphabetical-end -extern crate jobserver; #[macro_use] extern crate rustc_middle; extern crate rustc_abi; @@ -34,7 +33,7 @@ extern crate rustc_target; extern crate rustc_driver; use std::any::Any; -use std::cell::{Cell, RefCell}; +use std::env; use std::sync::Arc; use cranelift_codegen::isa::TargetIsa; @@ -42,7 +41,6 @@ use cranelift_codegen::settings::{self, Configurable}; use rustc_codegen_ssa::CodegenResults; use rustc_codegen_ssa::back::versioned_llvm_target; use rustc_codegen_ssa::traits::CodegenBackend; -use rustc_data_structures::profiling::SelfProfilerRef; use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_session::Session; @@ -123,11 +121,10 @@ impl<F: Fn() -> String> Drop for PrintOnPanic<F> { /// The codegen context holds any information shared between the codegen of individual functions /// inside a single codegen unit with the exception of the Cranelift [`Module`](cranelift_module::Module). struct CodegenCx { - profiler: SelfProfilerRef, output_filenames: Arc<OutputFilenames>, should_write_ir: bool, global_asm: String, - inline_asm_index: Cell<usize>, + inline_asm_index: usize, debug_context: Option<DebugContext>, cgu_name: Symbol, } @@ -142,11 +139,10 @@ impl CodegenCx { None }; CodegenCx { - profiler: tcx.prof.clone(), output_filenames: tcx.output_filenames(()).clone(), should_write_ir: crate::pretty_clif::should_write_ir(tcx), global_asm: String::new(), - inline_asm_index: Cell::new(0), + inline_asm_index: 0, debug_context, cgu_name, } @@ -154,7 +150,7 @@ impl CodegenCx { } pub struct CraneliftCodegenBackend { - pub config: RefCell<Option<BackendConfig>>, + pub config: Option<BackendConfig>, } impl CodegenBackend for CraneliftCodegenBackend { @@ -176,16 +172,13 @@ impl CodegenBackend for CraneliftCodegenBackend { sess.dcx() .fatal("`-Cinstrument-coverage` is LLVM specific and not supported by Cranelift"); } - - let mut config = self.config.borrow_mut(); - if config.is_none() { - let new_config = BackendConfig::from_opts(&sess.opts.cg.llvm_args) - .unwrap_or_else(|err| sess.dcx().fatal(err)); - *config = Some(new_config); - } } - fn target_features(&self, sess: &Session, _allow_unstable: bool) -> Vec<rustc_span::Symbol> { + fn target_features_cfg( + &self, + sess: &Session, + _allow_unstable: bool, + ) -> Vec<rustc_span::Symbol> { // FIXME return the actually used target features. this is necessary for #[cfg(target_feature)] if sess.target.arch == "x86_64" && sess.target.os != "none" { // x86_64 mandates SSE2 support @@ -215,12 +208,15 @@ impl CodegenBackend for CraneliftCodegenBackend { need_metadata_module: bool, ) -> Box<dyn Any> { tcx.dcx().abort_if_errors(); - let config = self.config.borrow().clone().unwrap(); + let config = self.config.clone().unwrap_or_else(|| { + BackendConfig::from_opts(&tcx.sess.opts.cg.llvm_args) + .unwrap_or_else(|err| tcx.sess.dcx().fatal(err)) + }); match config.codegen_mode { - CodegenMode::Aot => driver::aot::run_aot(tcx, config, metadata, need_metadata_module), + CodegenMode::Aot => driver::aot::run_aot(tcx, metadata, need_metadata_module), CodegenMode::Jit | CodegenMode::JitLazy => { #[cfg(feature = "jit")] - driver::jit::run_jit(tcx, config); + driver::jit::run_jit(tcx, config.codegen_mode, config.jit_args); #[cfg(not(feature = "jit"))] tcx.dcx().fatal("jit support was disabled when compiling rustc_codegen_cranelift"); @@ -234,16 +230,20 @@ impl CodegenBackend for CraneliftCodegenBackend { sess: &Session, outputs: &OutputFilenames, ) -> (CodegenResults, FxIndexMap<WorkProductId, WorkProduct>) { - let _timer = sess.timer("finish_ongoing_codegen"); - - ongoing_codegen.downcast::<driver::aot::OngoingCodegen>().unwrap().join( - sess, - outputs, - self.config.borrow().as_ref().unwrap(), - ) + ongoing_codegen.downcast::<driver::aot::OngoingCodegen>().unwrap().join(sess, outputs) } } +/// Determine if the Cranelift ir verifier should run. +/// +/// Returns true when `-Zverify-llvm-ir` is passed, the `CG_CLIF_ENABLE_VERIFIER` env var is set to +/// 1 or when cg_clif is compiled with debug assertions enabled or false otherwise. +fn enable_verifier(sess: &Session) -> bool { + sess.verify_llvm_ir() + || cfg!(debug_assertions) + || env::var("CG_CLIF_ENABLE_VERIFIER").as_deref() == Ok("1") +} + fn target_triple(sess: &Session) -> target_lexicon::Triple { // FIXME(madsmtm): Use `sess.target.llvm_target` once target-lexicon supports unversioned macOS. // See <https://github.com/bytecodealliance/target-lexicon/pull/113> @@ -253,14 +253,14 @@ fn target_triple(sess: &Session) -> target_lexicon::Triple { } } -fn build_isa(sess: &Session, backend_config: &BackendConfig) -> Arc<dyn TargetIsa + 'static> { +fn build_isa(sess: &Session) -> Arc<dyn TargetIsa + 'static> { use target_lexicon::BinaryFormat; let target_triple = crate::target_triple(sess); let mut flags_builder = settings::builder(); flags_builder.enable("is_pic").unwrap(); - let enable_verifier = if backend_config.enable_verifier { "true" } else { "false" }; + let enable_verifier = if enable_verifier(sess) { "true" } else { "false" }; flags_builder.set("enable_verifier", enable_verifier).unwrap(); flags_builder.set("regalloc_checker", enable_verifier).unwrap(); @@ -295,6 +295,16 @@ fn build_isa(sess: &Session, backend_config: &BackendConfig) -> Arc<dyn TargetIs } } + if let target_lexicon::OperatingSystem::Windows = target_triple.operating_system { + // FIXME remove dependency on this from the Rust ABI. cc bytecodealliance/wasmtime#9510 + flags_builder.enable("enable_multi_ret_implicit_sret").unwrap(); + } + + if let target_lexicon::Architecture::S390x = target_triple.architecture { + // FIXME remove dependency on this from the Rust ABI. cc bytecodealliance/wasmtime#9510 + flags_builder.enable("enable_multi_ret_implicit_sret").unwrap(); + } + if let target_lexicon::Architecture::Aarch64(_) | target_lexicon::Architecture::Riscv64(_) | target_lexicon::Architecture::X86_64 = target_triple.architecture @@ -347,5 +357,5 @@ fn build_isa(sess: &Session, backend_config: &BackendConfig) -> Arc<dyn TargetIs /// This is the entrypoint for a hot plugged rustc_codegen_cranelift #[no_mangle] pub fn __rustc_codegen_backend() -> Box<dyn CodegenBackend> { - Box::new(CraneliftCodegenBackend { config: RefCell::new(None) }) + Box::new(CraneliftCodegenBackend { config: None }) } diff --git a/compiler/rustc_codegen_cranelift/src/main_shim.rs b/compiler/rustc_codegen_cranelift/src/main_shim.rs index 2ee4ff5cec7..e480f21b9df 100644 --- a/compiler/rustc_codegen_cranelift/src/main_shim.rs +++ b/compiler/rustc_codegen_cranelift/src/main_shim.rs @@ -24,7 +24,7 @@ pub(crate) fn maybe_create_entry_wrapper( }; if main_def_id.is_local() { - let instance = Instance::mono(tcx, main_def_id).polymorphize(tcx); + let instance = Instance::mono(tcx, main_def_id); if module.get_name(tcx.symbol_name(instance).name).is_none() { return; } @@ -75,7 +75,7 @@ pub(crate) fn maybe_create_entry_wrapper( } }; - let instance = Instance::mono(tcx, rust_main_def_id).polymorphize(tcx); + let instance = Instance::mono(tcx, rust_main_def_id); let main_name = tcx.symbol_name(instance).name; let main_sig = get_function_sig(tcx, m.target_config().default_call_conv, instance); @@ -117,8 +117,7 @@ pub(crate) fn maybe_create_entry_wrapper( report.def_id, tcx.mk_args(&[GenericArg::from(main_ret_ty)]), DUMMY_SP, - ) - .polymorphize(tcx); + ); let report_name = tcx.symbol_name(report).name; let report_sig = get_function_sig(tcx, m.target_config().default_call_conv, report); @@ -143,8 +142,7 @@ pub(crate) fn maybe_create_entry_wrapper( start_def_id, tcx.mk_args(&[main_ret_ty.into()]), DUMMY_SP, - ) - .polymorphize(tcx); + ); let start_func_id = import_function(tcx, m, start_instance); let main_val = bcx.ins().func_addr(m.target_config().pointer_type(), main_func_ref); diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index 6676e684ca0..c17d1f30fbe 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -1005,9 +1005,6 @@ pub(crate) fn assert_assignable<'tcx>( } } } - (ty::Param(_), _) | (_, ty::Param(_)) if fx.tcx.sess.opts.unstable_opts.polymorphize => { - // No way to check if it is correct or not with polymorphization enabled - } _ => { assert_eq!( from_ty, diff --git a/compiler/rustc_codegen_gcc/example/mini_core.rs b/compiler/rustc_codegen_gcc/example/mini_core.rs index c887598f6e9..cdd151613df 100644 --- a/compiler/rustc_codegen_gcc/example/mini_core.rs +++ b/compiler/rustc_codegen_gcc/example/mini_core.rs @@ -52,24 +52,24 @@ impl<T: ?Sized> LegacyReceiver for &mut T {} impl<T: ?Sized, A: Allocator> LegacyReceiver for Box<T, A> {} #[lang = "copy"] -pub unsafe trait Copy {} - -unsafe impl Copy for bool {} -unsafe impl Copy for u8 {} -unsafe impl Copy for u16 {} -unsafe impl Copy for u32 {} -unsafe impl Copy for u64 {} -unsafe impl Copy for usize {} -unsafe impl Copy for i8 {} -unsafe impl Copy for i16 {} -unsafe impl Copy for i32 {} -unsafe impl Copy for isize {} -unsafe impl Copy for f32 {} -unsafe impl Copy for f64 {} -unsafe impl Copy for char {} -unsafe impl<'a, T: ?Sized> Copy for &'a T {} -unsafe impl<T: ?Sized> Copy for *const T {} -unsafe impl<T: ?Sized> Copy for *mut T {} +pub trait Copy {} + +impl Copy for bool {} +impl Copy for u8 {} +impl Copy for u16 {} +impl Copy for u32 {} +impl Copy for u64 {} +impl Copy for usize {} +impl Copy for i8 {} +impl Copy for i16 {} +impl Copy for i32 {} +impl Copy for isize {} +impl Copy for f32 {} +impl Copy for f64 {} +impl Copy for char {} +impl<'a, T: ?Sized> Copy for &'a T {} +impl<T: ?Sized> Copy for *const T {} +impl<T: ?Sized> Copy for *mut T {} #[lang = "sync"] pub unsafe trait Sync {} diff --git a/compiler/rustc_codegen_gcc/messages.ftl b/compiler/rustc_codegen_gcc/messages.ftl index 26ddc5732dd..85fa17a6ba5 100644 --- a/compiler/rustc_codegen_gcc/messages.ftl +++ b/compiler/rustc_codegen_gcc/messages.ftl @@ -9,7 +9,7 @@ codegen_gcc_lto_not_supported = LTO is not supported. You may get a linker error. codegen_gcc_forbidden_ctarget_feature = - target feature `{$feature}` cannot be toggled with `-Ctarget-feature` + target feature `{$feature}` cannot be toggled with `-Ctarget-feature`: {$reason} codegen_gcc_unwinding_inline_asm = GCC backend does not support unwinding from inline asm diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index a1f9eab10e7..415f8affab9 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -867,6 +867,13 @@ impl<'gcc, 'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { template_str.push_str("\n.popsection"); self.context.add_top_level_asm(None, &template_str); } + + fn mangled_name(&self, instance: Instance<'tcx>) -> String { + // TODO(@Amanieu): Additional mangling is needed on + // some targets to add a leading underscore (Mach-O) + // or byte count suffixes (x86 Windows). + self.tcx.symbol_name(instance).name.to_string() + } } fn modifier_to_gcc( 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/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 3846d025537..f67dcf0cb11 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -544,7 +544,10 @@ impl<'gcc, 'tcx> HasWasmCAbiOpt for CodegenCx<'gcc, 'tcx> { impl<'gcc, 'tcx> HasX86AbiOpt for CodegenCx<'gcc, 'tcx> { fn x86_abi_opt(&self) -> X86Abi { - X86Abi { regparm: self.tcx.sess.opts.unstable_opts.regparm } + X86Abi { + regparm: self.tcx.sess.opts.unstable_opts.regparm, + reg_struct_return: self.tcx.sess.opts.unstable_opts.reg_struct_return, + } } } diff --git a/compiler/rustc_codegen_gcc/src/errors.rs b/compiler/rustc_codegen_gcc/src/errors.rs index 7a586b5b04c..56849cc8610 100644 --- a/compiler/rustc_codegen_gcc/src/errors.rs +++ b/compiler/rustc_codegen_gcc/src/errors.rs @@ -28,6 +28,7 @@ pub(crate) struct UnstableCTargetFeature<'a> { #[diag(codegen_gcc_forbidden_ctarget_feature)] pub(crate) struct ForbiddenCTargetFeature<'a> { pub feature: &'a str, + pub reason: &'a str, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index 65279c9495a..058a874501b 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -5,7 +5,7 @@ use rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable; use rustc_data_structures::fx::FxHashMap; use rustc_middle::bug; use rustc_session::Session; -use rustc_target::target_features::{RUSTC_SPECIFIC_FEATURES, Stability}; +use rustc_target::target_features::RUSTC_SPECIFIC_FEATURES; use smallvec::{SmallVec, smallvec}; use crate::errors::{ @@ -94,13 +94,17 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri }; sess.dcx().emit_warn(unknown_feature); } - Some((_, Stability::Stable, _)) => {} - Some((_, Stability::Unstable(_), _)) => { - // An unstable feature. Warn about using it. - sess.dcx().emit_warn(UnstableCTargetFeature { feature }); - } - Some((_, Stability::Forbidden { .. }, _)) => { - sess.dcx().emit_err(ForbiddenCTargetFeature { feature }); + Some((_, stability, _)) => { + if let Err(reason) = + stability.toggle_allowed(&sess.target, enable_disable == '+') + { + sess.dcx().emit_warn(ForbiddenCTargetFeature { feature, reason }); + } else if stability.requires_nightly().is_some() { + // An unstable feature. Warn about using it. (It makes little sense + // to hard-error here since we just warn about fully unknown + // features above). + sess.dcx().emit_warn(UnstableCTargetFeature { feature }); + } } } diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 452e92bffa2..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; @@ -260,8 +260,8 @@ impl CodegenBackend for GccCodegenBackend { .join(sess) } - fn target_features(&self, sess: &Session, allow_unstable: bool) -> Vec<Symbol> { - target_features(sess, allow_unstable, &self.target_info) + fn target_features_cfg(&self, sess: &Session, allow_unstable: bool) -> Vec<Symbol> { + target_features_cfg(sess, allow_unstable, &self.target_info) } } @@ -472,7 +472,8 @@ fn to_gcc_opt_level(optlevel: Option<OptLevel>) -> OptimizationLevel { } } -pub fn target_features( +/// Returns the features that should be set in `cfg(target_feature)`. +fn target_features_cfg( sess: &Session, allow_unstable: bool, target_info: &LockedTargetInfo, @@ -481,10 +482,10 @@ pub fn target_features( sess.target .rust_target_features() .iter() - .filter(|(_, gate, _)| gate.is_supported()) - .filter_map(|&(feature, gate, _)| { - if sess.is_nightly_build() || allow_unstable || gate.is_stable() { - Some(feature) + .filter(|(_, gate, _)| gate.in_cfg()) + .filter_map(|(feature, gate, _)| { + if sess.is_nightly_build() || allow_unstable || gate.requires_nightly().is_none() { + Some(*feature) } else { None } diff --git a/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt b/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt index 56b51275a53..457072b1a5b 100644 --- a/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt +++ b/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt @@ -6,7 +6,6 @@ tests/ui/functions-closures/parallel-codegen-closures.rs tests/ui/linkage-attr/linkage1.rs tests/ui/lto/dylib-works.rs tests/ui/numbers-arithmetic/saturating-float-casts.rs -tests/ui/polymorphization/promoted-function.rs tests/ui/sepcomp/sepcomp-cci.rs tests/ui/sepcomp/sepcomp-extern.rs tests/ui/sepcomp/sepcomp-fns-backwards.rs 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/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index d1804cb49ad..3722d4350a2 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -442,6 +442,14 @@ impl<'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { ); } } + + fn mangled_name(&self, instance: Instance<'tcx>) -> String { + let llval = self.get_fn(instance); + llvm::build_string(|s| unsafe { + llvm::LLVMRustGetMangledName(llval, s); + }) + .expect("symbol is not valid UTF-8") + } } pub(crate) fn inline_asm_call<'ll>( @@ -504,14 +512,13 @@ pub(crate) fn inline_asm_call<'ll>( let key = "srcloc"; let kind = llvm::LLVMGetMDKindIDInContext( bx.llcx, - key.as_ptr() as *const c_char, + key.as_ptr().cast::<c_char>(), key.len() as c_uint, ); - // srcloc contains one integer for each line of assembly code. - // Unfortunately this isn't enough to encode a full span so instead - // we just encode the start position of each line. - // FIXME: Figure out a way to pass the entire line spans. + // `srcloc` contains one 64-bit integer for each line of assembly code, + // where the lower 32 bits hold the lo byte position and the upper 32 bits + // hold the hi byte position. let mut srcloc = vec![]; if dia == llvm::AsmDialect::Intel && line_spans.len() > 1 { // LLVM inserts an extra line to add the ".intel_syntax", so add @@ -521,13 +528,13 @@ pub(crate) fn inline_asm_call<'ll>( // due to the asm template string coming from a macro. LLVM will // default to the first srcloc for lines that don't have an // associated srcloc. - srcloc.push(llvm::LLVMValueAsMetadata(bx.const_i32(0))); + srcloc.push(llvm::LLVMValueAsMetadata(bx.const_u64(0))); } - srcloc.extend( - line_spans - .iter() - .map(|span| llvm::LLVMValueAsMetadata(bx.const_i32(span.lo().to_u32() as i32))), - ); + srcloc.extend(line_spans.iter().map(|span| { + llvm::LLVMValueAsMetadata(bx.const_u64( + u64::from(span.lo().to_u32()) | (u64::from(span.hi().to_u32()) << 32), + )) + })); let md = llvm::LLVMMDNodeInContext2(bx.llcx, srcloc.as_ptr(), srcloc.len()); let md = llvm::LLVMMetadataAsValue(&bx.llcx, md); llvm::LLVMSetMetadata(call, kind, md); diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index cb958c1d4d7..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}; @@ -395,17 +395,9 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( to_add.push(MemoryEffects::None.create_attr(cx.llcx)); } if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) { - to_add.push(AttributeKind::Naked.create_attr(cx.llcx)); - // HACK(jubilee): "indirect branch tracking" works by attaching prologues to functions. - // And it is a module-level attribute, so the alternative is pulling naked functions into - // new LLVM modules. Otherwise LLVM's "naked" functions come with endbr prefixes per - // https://github.com/rust-lang/rust/issues/98768 - to_add.push(AttributeKind::NoCfCheck.create_attr(cx.llcx)); - if llvm_util::get_version() < (19, 0, 0) { - // Prior to LLVM 19, branch-target-enforcement was disabled by setting the attribute to - // the string "false". Now it is disabled by absence of the attribute. - to_add.push(llvm::CreateAttrStringValue(cx.llcx, "branch-target-enforcement", "false")); - } + // do nothing; a naked function is converted into an extern function + // and a global assembly block. LLVM's support for naked functions is + // not used. } else { // Do not set sanitizer attributes for naked functions. to_add.extend(sanitize_attrs(cx, codegen_fn_attrs.no_sanitize)); diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index bde6668929c..66ca4e2b473 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -25,8 +25,8 @@ use rustc_session::Session; use rustc_session::config::{ self, Lto, OutputType, Passes, RemapPathScopeComponents, SplitDwarfKind, SwitchWithOptPath, }; -use rustc_span::InnerSpan; use rustc_span::symbol::sym; +use rustc_span::{BytePos, InnerSpan, Pos, SpanData, SyntaxContext}; use rustc_target::spec::{CodeModel, RelocModel, SanitizerSet, SplitDebuginfo, TlsModel}; use tracing::debug; @@ -415,21 +415,32 @@ fn report_inline_asm( cgcx: &CodegenContext<LlvmCodegenBackend>, msg: String, level: llvm::DiagnosticLevel, - mut cookie: u64, + cookie: u64, source: Option<(String, Vec<InnerSpan>)>, ) { // In LTO build we may get srcloc values from other crates which are invalid // since they use a different source map. To be safe we just suppress these // in LTO builds. - if matches!(cgcx.lto, Lto::Fat | Lto::Thin) { - cookie = 0; - } + let span = if cookie == 0 || matches!(cgcx.lto, Lto::Fat | Lto::Thin) { + SpanData::default() + } else { + let lo = BytePos::from_u32(cookie as u32); + let hi = BytePos::from_u32((cookie >> 32) as u32); + SpanData { + lo, + // LLVM version < 19 silently truncates the cookie to 32 bits in some situations. + hi: if hi.to_u32() != 0 { hi } else { lo }, + ctxt: SyntaxContext::root(), + parent: None, + } + }; let level = match level { llvm::DiagnosticLevel::Error => Level::Error, llvm::DiagnosticLevel::Warning => Level::Warning, llvm::DiagnosticLevel::Note | llvm::DiagnosticLevel::Remark => Level::Note, }; - cgcx.diag_emitter.inline_asm_error(cookie.try_into().unwrap(), msg, level, source); + let msg = msg.strip_prefix("error: ").unwrap_or(&msg).to_string(); + cgcx.diag_emitter.inline_asm_error(span, msg, level, source); } unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void) { 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/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 8852dec7d9f..adfe8aeb5c5 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -302,10 +302,9 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { (value, AddressSpace::DATA) } } - GlobalAlloc::Function { instance, .. } => ( - self.get_fn_addr(instance.polymorphize(self.tcx)), - self.data_layout().instruction_address_space, - ), + GlobalAlloc::Function { instance, .. } => { + (self.get_fn_addr(instance), self.data_layout().instruction_address_space) + } GlobalAlloc::VTable(ty, dyn_ty) => { let alloc = self .tcx diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 8218126ea29..c602d99ff9d 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -159,6 +159,16 @@ pub(crate) unsafe fn create_module<'ll>( // See https://github.com/llvm/llvm-project/pull/112084 target_data_layout = target_data_layout.replace("-i128:128", ""); } + if sess.target.arch.starts_with("powerpc64") { + // LLVM 20 updates the powerpc64 layout to correctly align 128 bit integers to 128 bit. + // See https://github.com/llvm/llvm-project/pull/118004 + target_data_layout = target_data_layout.replace("-i128:128", ""); + } + if sess.target.arch.starts_with("wasm32") || sess.target.arch.starts_with("wasm64") { + // LLVM 20 updates the wasm(32|64) layout to correctly align 128 bit integers to 128 bit. + // See https://github.com/llvm/llvm-project/pull/119204 + target_data_layout = target_data_layout.replace("-i128:128", ""); + } } // Ensure the data-layout values hardcoded remain the defaults. diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs index a6e07ea2a60..1f133141c18 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs @@ -152,6 +152,34 @@ impl CoverageSpan { } } +/// Holds tables of the various region types in one struct. +/// +/// Don't pass this struct across FFI; pass the individual region tables as +/// pointer/length pairs instead. +/// +/// Each field name has a `_regions` suffix for improved readability after +/// exhaustive destructing, which ensures that all region types are handled. +#[derive(Clone, Debug, Default)] +pub(crate) struct Regions { + pub(crate) code_regions: Vec<CodeRegion>, + pub(crate) branch_regions: Vec<BranchRegion>, + pub(crate) mcdc_branch_regions: Vec<MCDCBranchRegion>, + pub(crate) mcdc_decision_regions: Vec<MCDCDecisionRegion>, +} + +impl Regions { + /// Returns true if none of this structure's tables contain any regions. + pub(crate) fn has_no_regions(&self) -> bool { + let Self { code_regions, branch_regions, mcdc_branch_regions, mcdc_decision_regions } = + self; + + code_regions.is_empty() + && branch_regions.is_empty() + && mcdc_branch_regions.is_empty() + && mcdc_decision_regions.is_empty() + } +} + /// Must match the layout of `LLVMRustCoverageCodeRegion`. #[derive(Clone, Debug)] #[repr(C)] diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs index 99c2d12b261..086cf1f44a0 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs @@ -62,11 +62,10 @@ pub(crate) fn write_filenames_to_buffer<'a>( pub(crate) fn write_function_mappings_to_buffer( virtual_file_mapping: &[u32], expressions: &[ffi::CounterExpression], - code_regions: &[ffi::CodeRegion], - branch_regions: &[ffi::BranchRegion], - mcdc_branch_regions: &[ffi::MCDCBranchRegion], - mcdc_decision_regions: &[ffi::MCDCDecisionRegion], + regions: &ffi::Regions, ) -> Vec<u8> { + let ffi::Regions { code_regions, branch_regions, mcdc_branch_regions, mcdc_decision_regions } = + regions; llvm::build_byte_buffer(|buffer| unsafe { llvm::LLVMRustCoverageWriteFunctionMappingsToBuffer( virtual_file_mapping.as_ptr(), diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs index 0752c718c70..c5d1ebdfe7c 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs @@ -1,159 +1,38 @@ use rustc_data_structures::captures::Captures; -use rustc_data_structures::fx::FxIndexSet; -use rustc_index::bit_set::BitSet; -use rustc_middle::mir::CoverageIdsInfo; use rustc_middle::mir::coverage::{ - CounterId, CovTerm, Expression, ExpressionId, FunctionCoverageInfo, Mapping, MappingKind, Op, + CovTerm, CoverageIdsInfo, Expression, FunctionCoverageInfo, Mapping, MappingKind, Op, SourceRegion, }; -use rustc_middle::ty::Instance; -use tracing::debug; use crate::coverageinfo::ffi::{Counter, CounterExpression, ExprKind}; -/// Holds all of the coverage mapping data associated with a function instance, -/// collected during traversal of `Coverage` statements in the function's MIR. -#[derive(Debug)] -pub(crate) struct FunctionCoverageCollector<'tcx> { - /// Coverage info that was attached to this function by the instrumentor. - function_coverage_info: &'tcx FunctionCoverageInfo, - ids_info: &'tcx CoverageIdsInfo, - is_used: bool, +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> FunctionCoverageCollector<'tcx> { - /// Creates a new set of coverage data for a used (called) function. - pub(crate) fn new( - instance: Instance<'tcx>, - function_coverage_info: &'tcx FunctionCoverageInfo, - ids_info: &'tcx CoverageIdsInfo, - ) -> Self { - Self::create(instance, function_coverage_info, ids_info, true) - } - - /// Creates a new set of coverage data for an unused (never called) function. - pub(crate) fn unused( - instance: Instance<'tcx>, - function_coverage_info: &'tcx FunctionCoverageInfo, - ids_info: &'tcx CoverageIdsInfo, - ) -> Self { - Self::create(instance, function_coverage_info, ids_info, false) - } - - fn create( - instance: Instance<'tcx>, +impl<'tcx> FunctionCoverage<'tcx> { + pub(crate) fn new_used( function_coverage_info: &'tcx FunctionCoverageInfo, ids_info: &'tcx CoverageIdsInfo, - is_used: bool, ) -> Self { - let num_counters = function_coverage_info.num_counters; - let num_expressions = function_coverage_info.expressions.len(); - debug!( - "FunctionCoverage::create(instance={instance:?}) has \ - num_counters={num_counters}, num_expressions={num_expressions}, is_used={is_used}" - ); - - Self { function_coverage_info, ids_info, is_used } - } - - /// Identify expressions that will always have a value of zero, and note - /// their IDs in [`ZeroExpressions`]. Mappings that refer to a zero expression - /// can instead become mappings to a constant zero value. - /// - /// This method mainly exists to preserve the simplifications that were - /// already being performed by the Rust-side expression renumbering, so that - /// the resulting coverage mappings don't get worse. - fn identify_zero_expressions(&self) -> ZeroExpressions { - // The set of expressions that either were optimized out entirely, or - // have zero as both of their operands, and will therefore always have - // a value of zero. Other expressions that refer to these as operands - // can have those operands replaced with `CovTerm::Zero`. - let mut zero_expressions = ZeroExpressions::default(); - - // Simplify a copy of each expression based on lower-numbered expressions, - // and then update the set of always-zero expressions if necessary. - // (By construction, expressions can only refer to other expressions - // that have lower IDs, so one pass is sufficient.) - for (id, expression) in self.function_coverage_info.expressions.iter_enumerated() { - if !self.is_used || !self.ids_info.expressions_seen.contains(id) { - // If an expression was not seen, it must have been optimized away, - // so any operand that refers to it can be replaced with zero. - zero_expressions.insert(id); - continue; - } - - // We don't need to simplify the actual expression data in the - // expressions list; we can just simplify a temporary copy and then - // use that to update the set of always-zero expressions. - let Expression { mut lhs, op, mut rhs } = *expression; - - // If an expression has an operand that is also an expression, the - // operand's ID must be strictly lower. This is what lets us find - // all zero expressions in one pass. - let assert_operand_expression_is_lower = |operand_id: ExpressionId| { - assert!( - operand_id < id, - "Operand {operand_id:?} should be less than {id:?} in {expression:?}", - ) - }; - - // If an operand refers to a counter or expression that is always - // zero, then that operand can be replaced with `CovTerm::Zero`. - let maybe_set_operand_to_zero = |operand: &mut CovTerm| { - if let CovTerm::Expression(id) = *operand { - assert_operand_expression_is_lower(id); - } - - if is_zero_term(&self.ids_info.counters_seen, &zero_expressions, *operand) { - *operand = CovTerm::Zero; - } - }; - maybe_set_operand_to_zero(&mut lhs); - maybe_set_operand_to_zero(&mut rhs); - - // Coverage counter values cannot be negative, so if an expression - // involves subtraction from zero, assume that its RHS must also be zero. - // (Do this after simplifications that could set the LHS to zero.) - if lhs == CovTerm::Zero && op == Op::Subtract { - rhs = CovTerm::Zero; - } - - // After the above simplifications, if both operands are zero, then - // we know that this expression is always zero too. - if lhs == CovTerm::Zero && rhs == CovTerm::Zero { - zero_expressions.insert(id); - } - } - - zero_expressions + Self { function_coverage_info, ids_info: Some(ids_info) } } - pub(crate) fn into_finished(self) -> FunctionCoverage<'tcx> { - let zero_expressions = self.identify_zero_expressions(); - let FunctionCoverageCollector { function_coverage_info, ids_info, is_used, .. } = self; - - FunctionCoverage { function_coverage_info, ids_info, is_used, zero_expressions } + pub(crate) fn new_unused(function_coverage_info: &'tcx FunctionCoverageInfo) -> Self { + Self { function_coverage_info, ids_info: None } } -} - -pub(crate) struct FunctionCoverage<'tcx> { - pub(crate) function_coverage_info: &'tcx FunctionCoverageInfo, - ids_info: &'tcx CoverageIdsInfo, - is_used: bool, - - zero_expressions: ZeroExpressions, -} -impl<'tcx> FunctionCoverage<'tcx> { /// Returns true for a used (called) function, and false for an unused function. pub(crate) fn is_used(&self) -> bool { - self.is_used + 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 } + 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 @@ -196,37 +75,10 @@ impl<'tcx> FunctionCoverage<'tcx> { } fn is_zero_term(&self, term: CovTerm) -> bool { - !self.is_used || is_zero_term(&self.ids_info.counters_seen, &self.zero_expressions, term) - } -} - -/// Set of expression IDs that are known to always evaluate to zero. -/// Any mapping or expression operand that refers to these expressions can have -/// that reference replaced with a constant zero value. -#[derive(Default)] -struct ZeroExpressions(FxIndexSet<ExpressionId>); - -impl ZeroExpressions { - fn insert(&mut self, id: ExpressionId) { - self.0.insert(id); - } - - fn contains(&self, id: ExpressionId) -> bool { - self.0.contains(&id) - } -} - -/// Returns `true` if the given term is known to have a value of zero, taking -/// into account knowledge of which counters are unused and which expressions -/// are always zero. -fn is_zero_term( - counters_seen: &BitSet<CounterId>, - zero_expressions: &ZeroExpressions, - term: CovTerm, -) -> bool { - match term { - CovTerm::Zero => true, - CovTerm::Counter(id) => !counters_seen.contains(id), - CovTerm::Expression(id) => zero_expressions.contains(id), + 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 8c24579fa7c..4f2af732527 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -1,4 +1,3 @@ -use std::ffi::CString; use std::iter; use itertools::Itertools as _; @@ -9,21 +8,22 @@ use rustc_codegen_ssa::traits::{ use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_index::IndexVec; -use rustc_middle::mir::coverage::MappingKind; 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_target::spec::HasTargetSpec; use tracing::debug; use crate::common::CodegenCx; -use crate::coverageinfo::map_data::{FunctionCoverage, FunctionCoverageCollector}; -use crate::coverageinfo::{ffi, llvm_cov}; +use crate::coverageinfo::llvm_cov; +use crate::coverageinfo::map_data::FunctionCoverage; +use crate::coverageinfo::mapgen::covfun::prepare_covfun_record; use crate::llvm; +mod covfun; + /// Generates and exports the coverage map, which is embedded in special /// linker sections in the final binary. /// @@ -63,16 +63,11 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) { None => return, }; if function_coverage_map.is_empty() { - // This module has no functions with coverage instrumentation + // This CGU has no functions with coverage instrumentation. return; } - let function_coverage_entries = function_coverage_map - .into_iter() - .map(|(instance, function_coverage)| (instance, function_coverage.into_finished())) - .collect::<Vec<_>>(); - - let all_file_names = function_coverage_entries + 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)); @@ -80,52 +75,33 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) { // Encode all filenames referenced by coverage mappings in this CGU. let filenames_buffer = global_file_table.make_filenames_buffer(tcx); - - let filenames_size = filenames_buffer.len(); - let filenames_val = cx.const_bytes(&filenames_buffer); - let filenames_ref = llvm_cov::hash_bytes(&filenames_buffer); - - // Generate the coverage map header, which contains the filenames used by - // this CGU's coverage mappings, and store it in a well-known global. - generate_covmap_record(cx, covmap_version, filenames_size, filenames_val); + // 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(); - // Encode coverage mappings and generate function records - for (instance, function_coverage) in function_coverage_entries { - debug!("Generate function coverage for {}, {:?}", cx.codegen_unit.name(), instance); - - let mangled_function_name = tcx.symbol_name(instance).name; - let source_hash = function_coverage.source_hash(); - let is_used = function_coverage.is_used(); - - let coverage_mapping_buffer = - encode_mappings_for_function(tcx, &global_file_table, &function_coverage); + let covfun_records = function_coverage_map + .into_iter() + .filter_map(|(instance, function_coverage)| { + prepare_covfun_record(tcx, &global_file_table, instance, &function_coverage) + }) + .collect::<Vec<_>>(); - if coverage_mapping_buffer.is_empty() { - if function_coverage.is_used() { - bug!( - "A used function should have had coverage mapping data but did not: {}", - mangled_function_name - ); - } else { - debug!("unused function had no coverage mapping data: {}", mangled_function_name); - continue; - } - } + // 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 + // then the covmap record isn't useful anyway. + // This should prevent a repeat of <https://github.com/rust-lang/rust/issues/133606>. + if covfun_records.is_empty() { + return; + } - if !is_used { - unused_function_names.push(mangled_function_name); - } + for covfun in &covfun_records { + unused_function_names.extend(covfun.mangled_function_name_if_unused()); - generate_covfun_record( - cx, - mangled_function_name, - source_hash, - filenames_ref, - coverage_mapping_buffer, - is_used, - ); + covfun::generate_covfun_record(cx, filenames_hash, covfun) } // For unused functions, we need to take their mangled names and store them @@ -146,6 +122,11 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) { llvm::set_linkage(array, llvm::Linkage::InternalLinkage); llvm::set_initializer(array, initializer); } + + // Generate the coverage map header, which contains the filenames used by + // this CGU's coverage mappings, and store it in a well-known global. + // (This is skipped if we returned early due to having no covfun records.) + generate_covmap_record(cx, covmap_version, &filenames_buffer); } /// Maps "global" (per-CGU) file ID numbers to their underlying filenames. @@ -213,7 +194,7 @@ rustc_index::newtype_index! { /// Holds a mapping from "local" (per-function) file IDs to "global" (per-CGU) /// file IDs. -#[derive(Default)] +#[derive(Debug, Default)] struct VirtualFileMapping { local_to_global: IndexVec<LocalFileId, GlobalFileId>, global_to_local: FxIndexMap<GlobalFileId, LocalFileId>, @@ -227,10 +208,10 @@ impl VirtualFileMapping { .or_insert_with(|| self.local_to_global.push(global_file_id)) } - fn into_vec(self) -> Vec<u32> { - // This conversion should be optimized away to ~zero overhead. - // In any case, it's probably not hot enough to worry about. - self.local_to_global.into_iter().map(|global| global.as_u32()).collect() + fn to_vec(&self) -> Vec<u32> { + // This clone could be avoided by transmuting `&[GlobalFileId]` to `&[u32]`, + // but it isn't hot or expensive enough to justify the extra unsafety. + self.local_to_global.iter().map(|&global| GlobalFileId::as_u32(global)).collect() } } @@ -241,173 +222,38 @@ fn span_file_name(tcx: TyCtxt<'_>, span: Span) -> Symbol { Symbol::intern(&name) } -/// Using the expressions and counter regions collected for a single function, -/// generate the variable-sized payload of its corresponding `__llvm_covfun` -/// entry. The payload is returned as a vector of bytes. -/// -/// Newly-encountered filenames will be added to the global file table. -fn encode_mappings_for_function( - tcx: TyCtxt<'_>, - global_file_table: &GlobalFileTable, - function_coverage: &FunctionCoverage<'_>, -) -> Vec<u8> { - let counter_regions = function_coverage.counter_regions(); - if counter_regions.is_empty() { - return Vec::new(); - } - - let expressions = function_coverage.counter_expressions().collect::<Vec<_>>(); - - let mut virtual_file_mapping = VirtualFileMapping::default(); - let mut code_regions = vec![]; - let mut branch_regions = vec![]; - let mut mcdc_branch_regions = vec![]; - let mut mcdc_decision_regions = vec![]; - - // 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); - - // Look up the global file ID for that filename. - let global_file_id = global_file_table.global_file_id_for_file_name(file_name); - - // Associate that global file ID with a local file ID for this function. - let local_file_id = virtual_file_mapping.local_id_for_global(global_file_id); - debug!(" file id: {local_file_id:?} => {global_file_id:?} = '{file_name:?}'"); - - // 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 { - MappingKind::Code(term) => { - code_regions.push(ffi::CodeRegion { span, counter: ffi::Counter::from_term(term) }); - } - MappingKind::Branch { true_term, false_term } => { - branch_regions.push(ffi::BranchRegion { - 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, - 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), - }); - } - MappingKind::MCDCDecision(mcdc_decision_params) => { - mcdc_decision_regions.push(ffi::MCDCDecisionRegion { - span, - mcdc_decision_params: ffi::mcdc::DecisionParameters::from(mcdc_decision_params), - }); - } - } - } - - // Encode the function's coverage mappings into a buffer. - llvm_cov::write_function_mappings_to_buffer( - &virtual_file_mapping.into_vec(), - &expressions, - &code_regions, - &branch_regions, - &mcdc_branch_regions, - &mcdc_decision_regions, - ) -} - /// 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. -fn generate_covmap_record<'ll>( - cx: &CodegenCx<'ll, '_>, - version: u32, - filenames_size: usize, - filenames_val: &'ll llvm::Value, -) { - debug!("cov map: filenames_size = {}, 0-based version = {}", filenames_size, version); - - // Create the coverage data header (Note, fields 0 and 2 are now always zero, - // as of `llvm::coverage::CovMapVersion::Version4`.) - let zero_was_n_records_val = cx.const_u32(0); - let filenames_size_val = cx.const_u32(filenames_size as u32); - let zero_was_coverage_size_val = cx.const_u32(0); - let version_val = cx.const_u32(version); - let cov_data_header_val = cx.const_struct( - &[zero_was_n_records_val, filenames_size_val, zero_was_coverage_size_val, version_val], - /*packed=*/ false, - ); - - // Create the complete LLVM coverage data value to add to the LLVM IR - let covmap_data = - cx.const_struct(&[cov_data_header_val, filenames_val], /*packed=*/ false); - - let llglobal = llvm::add_global(cx.llmod, cx.val_ty(covmap_data), &llvm_cov::covmap_var_name()); - llvm::set_initializer(llglobal, covmap_data); - llvm::set_global_constant(llglobal, true); - llvm::set_linkage(llglobal, llvm::Linkage::PrivateLinkage); - llvm::set_section(llglobal, &llvm_cov::covmap_section_name(cx.llmod)); - // LLVM's coverage mapping format specifies 8-byte alignment for items in this section. - // <https://llvm.org/docs/CoverageMappingFormat.html> - llvm::set_alignment(llglobal, Align::EIGHT); - cx.add_used_global(llglobal); -} - -/// Generates the contents of the covfun record for this function, which -/// contains the function's coverage mapping data. The record is then stored -/// as a global variable in the `__llvm_covfun` section. -fn generate_covfun_record( - cx: &CodegenCx<'_, '_>, - mangled_function_name: &str, - source_hash: u64, - filenames_ref: u64, - coverage_mapping_buffer: Vec<u8>, - is_used: bool, -) { - // Concatenate the encoded coverage mappings - let coverage_mapping_size = coverage_mapping_buffer.len(); - let coverage_mapping_val = cx.const_bytes(&coverage_mapping_buffer); - - let func_name_hash = llvm_cov::hash_bytes(mangled_function_name.as_bytes()); - let func_name_hash_val = cx.const_u64(func_name_hash); - let coverage_mapping_size_val = cx.const_u32(coverage_mapping_size as u32); - let source_hash_val = cx.const_u64(source_hash); - let filenames_ref_val = cx.const_u64(filenames_ref); - let func_record_val = cx.const_struct( +fn generate_covmap_record<'ll>(cx: &CodegenCx<'ll, '_>, version: u32, filenames_buffer: &[u8]) { + // A covmap record consists of four target-endian u32 values, followed by + // the encoded filenames table. Two of the header fields are unused in + // modern versions of the LLVM coverage mapping format, and are always 0. + // <https://llvm.org/docs/CoverageMappingFormat.html#llvm-ir-representation> + // See also `src/llvm-project/clang/lib/CodeGen/CoverageMappingGen.cpp`. + let covmap_header = cx.const_struct( &[ - func_name_hash_val, - coverage_mapping_size_val, - source_hash_val, - filenames_ref_val, - coverage_mapping_val, + cx.const_u32(0), // (unused) + cx.const_u32(filenames_buffer.len() as u32), + cx.const_u32(0), // (unused) + cx.const_u32(version), ], - /*packed=*/ true, + /* packed */ false, ); - - // Choose a variable name to hold this function's covfun data. - // Functions that are used have a suffix ("u") to distinguish them from - // unused copies of the same function (from different CGUs), so that if a - // linker sees both it won't discard the used copy's data. - let func_record_var_name = - CString::new(format!("__covrec_{:X}{}", func_name_hash, if is_used { "u" } else { "" })) - .unwrap(); - debug!("function record var name: {:?}", func_record_var_name); - - let llglobal = llvm::add_global(cx.llmod, cx.val_ty(func_record_val), &func_record_var_name); - llvm::set_initializer(llglobal, func_record_val); - llvm::set_global_constant(llglobal, true); - llvm::set_linkage(llglobal, llvm::Linkage::LinkOnceODRLinkage); - llvm::set_visibility(llglobal, llvm::Visibility::Hidden); - llvm::set_section(llglobal, cx.covfun_section_name()); + let covmap_record = cx + .const_struct(&[covmap_header, cx.const_bytes(filenames_buffer)], /* packed */ false); + + let covmap_global = + llvm::add_global(cx.llmod, cx.val_ty(covmap_record), &llvm_cov::covmap_var_name()); + llvm::set_initializer(covmap_global, covmap_record); + llvm::set_global_constant(covmap_global, true); + llvm::set_linkage(covmap_global, llvm::Linkage::PrivateLinkage); + llvm::set_section(covmap_global, &llvm_cov::covmap_section_name(cx.llmod)); // LLVM's coverage mapping format specifies 8-byte alignment for items in this section. // <https://llvm.org/docs/CoverageMappingFormat.html> - llvm::set_alignment(llglobal, Align::EIGHT); - if cx.target_spec().supports_comdat() { - llvm::set_comdat(cx.llmod, llglobal, &func_record_var_name); - } - cx.add_used_global(llglobal); + llvm::set_alignment(covmap_global, Align::EIGHT); + + cx.add_used_global(covmap_global); } /// Each CGU will normally only emit coverage metadata for the functions that it actually generates. @@ -536,11 +382,6 @@ fn add_unused_function_coverage<'tcx>( ); // An unused function's mappings will all be rewritten to map to zero. - let function_coverage = FunctionCoverageCollector::unused( - instance, - function_coverage_info, - tcx.coverage_ids_info(instance.def), - ); - + 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 new file mode 100644 index 00000000000..33e7a0f2f20 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs @@ -0,0 +1,199 @@ +//! For each function that was instrumented for coverage, we need to embed its +//! corresponding coverage mapping metadata inside the `__llvm_covfun`[^win] +//! linker section of the final binary. +//! +//! [^win]: On Windows the section name is `.lcovfun`. + +use std::ffi::CString; + +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::ty::{Instance, TyCtxt}; +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::{ffi, llvm_cov}; +use crate::llvm; + +/// Intermediate coverage metadata for a single function, used to help build +/// the final record that will be embedded in the `__llvm_covfun` section. +#[derive(Debug)] +pub(crate) struct CovfunRecord<'tcx> { + mangled_function_name: &'tcx str, + source_hash: u64, + is_used: bool, + + virtual_file_mapping: VirtualFileMapping, + expressions: Vec<ffi::CounterExpression>, + regions: ffi::Regions, +} + +impl<'tcx> CovfunRecord<'tcx> { + /// FIXME(Zalathar): Make this the responsibility of the code that determines + /// which functions are unused. + pub(crate) fn mangled_function_name_if_unused(&self) -> Option<&'tcx str> { + (!self.is_used).then_some(self.mangled_function_name) + } +} + +pub(crate) fn prepare_covfun_record<'tcx>( + tcx: TyCtxt<'tcx>, + global_file_table: &GlobalFileTable, + instance: Instance<'tcx>, + function_coverage: &FunctionCoverage<'tcx>, +) -> Option<CovfunRecord<'tcx>> { + let mut covfun = CovfunRecord { + mangled_function_name: tcx.symbol_name(instance).name, + source_hash: function_coverage.source_hash(), + is_used: function_coverage.is_used(), + virtual_file_mapping: VirtualFileMapping::default(), + expressions: function_coverage.counter_expressions().collect::<Vec<_>>(), + regions: ffi::Regions::default(), + }; + + fill_region_tables(tcx, global_file_table, function_coverage, &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; + } + } + + Some(covfun) +} + +/// 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>, + 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); + + // Look up the global file ID for that filename. + let global_file_id = global_file_table.global_file_id_for_file_name(file_name); + + // 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; + + // 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 { + MappingKind::Code(term) => { + code_regions.push(ffi::CodeRegion { span, counter: ffi::Counter::from_term(term) }); + } + MappingKind::Branch { true_term, false_term } => { + branch_regions.push(ffi::BranchRegion { + 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, + 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), + }); + } + MappingKind::MCDCDecision(mcdc_decision_params) => { + mcdc_decision_regions.push(ffi::MCDCDecisionRegion { + span, + mcdc_decision_params: ffi::mcdc::DecisionParameters::from(mcdc_decision_params), + }); + } + } + } +} + +/// Generates the contents of the covfun record for this function, which +/// contains the function's coverage mapping data. The record is then stored +/// as a global variable in the `__llvm_covfun` section. +pub(crate) fn generate_covfun_record<'tcx>( + cx: &CodegenCx<'_, 'tcx>, + filenames_hash: u64, + covfun: &CovfunRecord<'tcx>, +) { + let &CovfunRecord { + mangled_function_name, + source_hash, + is_used, + ref virtual_file_mapping, + ref expressions, + ref regions, + } = covfun; + + // Encode the function's coverage mappings into a buffer. + let coverage_mapping_buffer = llvm_cov::write_function_mappings_to_buffer( + &virtual_file_mapping.to_vec(), + expressions, + regions, + ); + + // A covfun record consists of four target-endian integers, followed by the + // encoded mapping data in bytes. Note that the length field is 32 bits. + // <https://llvm.org/docs/CoverageMappingFormat.html#llvm-ir-representation> + // See also `src/llvm-project/clang/lib/CodeGen/CoverageMappingGen.cpp` and + // `COVMAP_V3` in `src/llvm-project/llvm/include/llvm/ProfileData/InstrProfData.inc`. + let func_name_hash = llvm_cov::hash_bytes(mangled_function_name.as_bytes()); + let covfun_record = cx.const_struct( + &[ + cx.const_u64(func_name_hash), + cx.const_u32(coverage_mapping_buffer.len() as u32), + cx.const_u64(source_hash), + cx.const_u64(filenames_hash), + cx.const_bytes(&coverage_mapping_buffer), + ], + // This struct needs to be packed, so that the 32-bit length field + // doesn't have unexpected padding. + true, + ); + + // Choose a variable name to hold this function's covfun data. + // Functions that are used have a suffix ("u") to distinguish them from + // unused copies of the same function (from different CGUs), so that if a + // linker sees both it won't discard the used copy's data. + let u = if is_used { "u" } else { "" }; + let covfun_var_name = CString::new(format!("__covrec_{func_name_hash:X}{u}")).unwrap(); + debug!("function record var name: {covfun_var_name:?}"); + + let covfun_global = llvm::add_global(cx.llmod, cx.val_ty(covfun_record), &covfun_var_name); + llvm::set_initializer(covfun_global, covfun_record); + llvm::set_global_constant(covfun_global, true); + llvm::set_linkage(covfun_global, llvm::Linkage::LinkOnceODRLinkage); + llvm::set_visibility(covfun_global, llvm::Visibility::Hidden); + llvm::set_section(covfun_global, cx.covfun_section_name()); + // LLVM's coverage mapping format specifies 8-byte alignment for items in this section. + // <https://llvm.org/docs/CoverageMappingFormat.html> + llvm::set_alignment(covfun_global, Align::EIGHT); + if cx.target_spec().supports_comdat() { + llvm::set_comdat(cx.llmod, covfun_global, &covfun_var_name); + } + + cx.add_used_global(covfun_global); +} diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index c2fcb33f98b..82b6677e203 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -13,7 +13,7 @@ use tracing::{debug, instrument}; use crate::builder::Builder; use crate::common::CodegenCx; -use crate::coverageinfo::map_data::FunctionCoverageCollector; +use crate::coverageinfo::map_data::FunctionCoverage; use crate::llvm; pub(crate) mod ffi; @@ -24,8 +24,7 @@ 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>, FunctionCoverageCollector<'tcx>>>, + pub(crate) function_coverage_map: RefCell<FxIndexMap<Instance<'tcx>, FunctionCoverage<'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>>>, @@ -42,9 +41,7 @@ impl<'ll, 'tcx> CguCoverageContext<'ll, 'tcx> { } } - fn take_function_coverage_map( - &self, - ) -> FxIndexMap<Instance<'tcx>, FunctionCoverageCollector<'tcx>> { + fn take_function_coverage_map(&self) -> FxIndexMap<Instance<'tcx>, FunctionCoverage<'tcx>> { self.function_coverage_map.replace(FxIndexMap::default()) } @@ -161,8 +158,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { // 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(|| { - FunctionCoverageCollector::new( - instance, + FunctionCoverage::new_used( function_coverage_info, bx.tcx.coverage_ids_info(instance.def), ) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index dee67baaee9..59275254022 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -471,8 +471,6 @@ pub(crate) fn type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> AdtKind::Enum => enums::build_enum_type_di_node(cx, unique_type_id), }, ty::Tuple(_) => build_tuple_type_di_node(cx, unique_type_id), - // Type parameters from polymorphized functions. - ty::Param(_) => build_param_type_di_node(cx, t), _ => bug!("debuginfo: unexpected type in type_di_node(): {:?}", t), }; @@ -871,26 +869,6 @@ fn build_foreign_type_di_node<'ll, 'tcx>( ) } -fn build_param_type_di_node<'ll, 'tcx>( - cx: &CodegenCx<'ll, 'tcx>, - t: Ty<'tcx>, -) -> DINodeCreationResult<'ll> { - debug!("build_param_type_di_node: {:?}", t); - let name = format!("{t:?}"); - DINodeCreationResult { - di_node: unsafe { - llvm::LLVMRustDIBuilderCreateBasicType( - DIB(cx), - name.as_c_char_ptr(), - name.len(), - Size::ZERO.bits(), - DW_ATE_unsigned, - ) - }, - already_stored_in_typemap: false, - } -} - pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>( tcx: TyCtxt<'tcx>, codegen_unit_name: &str, diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index d8b055137b3..c38c5d4c644 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -352,84 +352,84 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { | sym::saturating_add | sym::saturating_sub => { let ty = arg_tys[0]; - match int_type_width_signed(ty, self) { - Some((width, signed)) => match name { - sym::ctlz | sym::cttz => { - let y = self.const_bool(false); - let ret = self.call_intrinsic(&format!("llvm.{name}.i{width}"), &[ - args[0].immediate(), - y, - ]); - - self.intcast(ret, llret_ty, false) - } - sym::ctlz_nonzero => { - let y = self.const_bool(true); - let llvm_name = &format!("llvm.ctlz.i{width}"); - let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]); - self.intcast(ret, llret_ty, false) - } - sym::cttz_nonzero => { - let y = self.const_bool(true); - let llvm_name = &format!("llvm.cttz.i{width}"); - let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]); - self.intcast(ret, llret_ty, false) - } - sym::ctpop => { - let ret = self.call_intrinsic(&format!("llvm.ctpop.i{width}"), &[args - [0] - .immediate()]); - self.intcast(ret, llret_ty, false) - } - sym::bswap => { - if width == 8 { - args[0].immediate() // byte swap a u8/i8 is just a no-op - } else { - self.call_intrinsic(&format!("llvm.bswap.i{width}"), &[ - args[0].immediate() - ]) - } - } - sym::bitreverse => self - .call_intrinsic(&format!("llvm.bitreverse.i{width}"), &[ + if !ty.is_integral() { + tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType { + span, + name, + ty, + }); + return Ok(()); + } + let (size, signed) = ty.int_size_and_signed(self.tcx); + let width = size.bits(); + match name { + sym::ctlz | sym::cttz => { + let y = self.const_bool(false); + let ret = self.call_intrinsic(&format!("llvm.{name}.i{width}"), &[ + args[0].immediate(), + y, + ]); + + self.intcast(ret, llret_ty, false) + } + sym::ctlz_nonzero => { + let y = self.const_bool(true); + let llvm_name = &format!("llvm.ctlz.i{width}"); + let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]); + self.intcast(ret, llret_ty, false) + } + sym::cttz_nonzero => { + let y = self.const_bool(true); + let llvm_name = &format!("llvm.cttz.i{width}"); + let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]); + self.intcast(ret, llret_ty, false) + } + sym::ctpop => { + let ret = self.call_intrinsic(&format!("llvm.ctpop.i{width}"), &[ + args[0].immediate() + ]); + self.intcast(ret, llret_ty, false) + } + sym::bswap => { + if width == 8 { + args[0].immediate() // byte swap a u8/i8 is just a no-op + } else { + self.call_intrinsic(&format!("llvm.bswap.i{width}"), &[ args[0].immediate() - ]), - sym::rotate_left | sym::rotate_right => { - let is_left = name == sym::rotate_left; - let val = args[0].immediate(); - let raw_shift = args[1].immediate(); - // rotate = funnel shift with first two args the same - let llvm_name = - &format!("llvm.fsh{}.i{}", if is_left { 'l' } else { 'r' }, width); - - // llvm expects shift to be the same type as the values, but rust - // always uses `u32`. - let raw_shift = self.intcast(raw_shift, self.val_ty(val), false); - - self.call_intrinsic(llvm_name, &[val, val, raw_shift]) + ]) } - sym::saturating_add | sym::saturating_sub => { - let is_add = name == sym::saturating_add; - let lhs = args[0].immediate(); - let rhs = args[1].immediate(); - let llvm_name = &format!( - "llvm.{}{}.sat.i{}", - if signed { 's' } else { 'u' }, - if is_add { "add" } else { "sub" }, - width - ); - self.call_intrinsic(llvm_name, &[lhs, rhs]) - } - _ => bug!(), - }, - None => { - tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType { - span, - name, - ty, - }); - return Ok(()); } + sym::bitreverse => self + .call_intrinsic(&format!("llvm.bitreverse.i{width}"), &[ + args[0].immediate() + ]), + sym::rotate_left | sym::rotate_right => { + let is_left = name == sym::rotate_left; + let val = args[0].immediate(); + let raw_shift = args[1].immediate(); + // rotate = funnel shift with first two args the same + let llvm_name = + &format!("llvm.fsh{}.i{}", if is_left { 'l' } else { 'r' }, width); + + // llvm expects shift to be the same type as the values, but rust + // always uses `u32`. + let raw_shift = self.intcast(raw_shift, self.val_ty(val), false); + + self.call_intrinsic(llvm_name, &[val, val, raw_shift]) + } + sym::saturating_add | sym::saturating_sub => { + let is_add = name == sym::saturating_add; + let lhs = args[0].immediate(); + let rhs = args[1].immediate(); + let llvm_name = &format!( + "llvm.{}{}.sat.i{}", + if signed { 's' } else { 'u' }, + if is_add { "add" } else { "sub" }, + width + ); + self.call_intrinsic(llvm_name, &[lhs, rhs]) + } + _ => bug!(), } } @@ -2531,19 +2531,3 @@ fn generic_simd_intrinsic<'ll, 'tcx>( span_bug!(span, "unknown SIMD intrinsic"); } - -// Returns the width of an int Ty, and if it's signed or not -// Returns None if the type is not an integer -// FIXME: there’s multiple of this functions, investigate using some of the already existing -// stuffs. -fn int_type_width_signed(ty: Ty<'_>, cx: &CodegenCx<'_, '_>) -> Option<(u64, bool)> { - match ty.kind() { - ty::Int(t) => { - Some((t.bit_width().unwrap_or(u64::from(cx.tcx.sess.target.pointer_width)), true)) - } - ty::Uint(t) => { - Some((t.bit_width().unwrap_or(u64::from(cx.tcx.sess.target.pointer_width)), false)) - } - _ => None, - } -} diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 3dfb86d422d..af8562db054 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -27,7 +27,7 @@ use std::mem::ManuallyDrop; use back::owned_target_machine::OwnedTargetMachine; use back::write::{create_informational_target_machine, create_target_machine}; use errors::ParseTargetMachineConfig; -pub use llvm_util::target_features; +pub use llvm_util::target_features_cfg; use rustc_ast::expand::allocator::AllocatorKind; use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule}; use rustc_codegen_ssa::back::write::{ @@ -36,7 +36,7 @@ use rustc_codegen_ssa::back::write::{ use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen}; use rustc_data_structures::fx::FxIndexMap; -use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, FatalError}; +use rustc_errors::{DiagCtxtHandle, FatalError}; use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::ty::TyCtxt; @@ -330,8 +330,8 @@ impl CodegenBackend for LlvmCodegenBackend { llvm_util::print_version(); } - fn target_features(&self, sess: &Session, allow_unstable: bool) -> Vec<Symbol> { - target_features(sess, allow_unstable) + fn target_features_cfg(&self, sess: &Session, allow_unstable: bool) -> Vec<Symbol> { + target_features_cfg(sess, allow_unstable) } fn codegen_crate<'tcx>( @@ -370,19 +370,14 @@ impl CodegenBackend for LlvmCodegenBackend { (codegen_results, work_products) } - fn link( - &self, - sess: &Session, - codegen_results: CodegenResults, - outputs: &OutputFilenames, - ) -> Result<(), ErrorGuaranteed> { + fn link(&self, sess: &Session, codegen_results: CodegenResults, outputs: &OutputFilenames) { use rustc_codegen_ssa::back::link::link_binary; use crate::back::archive::LlvmArchiveBuilderBuilder; // Run the linker on any artifacts that resulted from the LLVM run. // This should produce either a finished executable or library. - link_binary(sess, &LlvmArchiveBuilderBuilder, codegen_results, outputs) + link_binary(sess, &LlvmArchiveBuilderBuilder, codegen_results, outputs); } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs b/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs index a4cb5a25d1b..11043b664f5 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs @@ -151,7 +151,7 @@ impl InlineAsmDiagnostic { unsafe { SrcMgrDiagnostic::unpack(super::LLVMRustGetSMDiagnostic(di, &mut cookie)) }; InlineAsmDiagnostic { level: smdiag.level, - cookie: cookie.into(), + cookie, message: smdiag.message, source: smdiag.source, } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index b1ace0033ba..d62632d1431 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -17,7 +17,9 @@ use super::debuginfo::{ DebugEmissionKind, DebugNameTableKind, }; -pub type Bool = c_uint; +/// In the LLVM-C API, boolean values are passed as `typedef int LLVMBool`, +/// which has a different ABI from Rust or C++ `bool`. +pub type Bool = c_int; pub const True: Bool = 1 as Bool; pub const False: Bool = 0 as Bool; @@ -2317,7 +2319,7 @@ unsafe extern "C" { pub fn LLVMRustGetSMDiagnostic<'a>( DI: &'a DiagnosticInfo, - cookie_out: &mut c_uint, + cookie_out: &mut u64, ) -> &'a SMDiagnostic; pub fn LLVMRustUnpackSMDiagnostic( diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index db2b03d9aed..194438af88c 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -17,7 +17,7 @@ use rustc_session::Session; use rustc_session::config::{PrintKind, PrintRequest}; use rustc_span::symbol::Symbol; use rustc_target::spec::{MergeFunctions, PanicStrategy, SmallDataThresholdSupport}; -use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATURES, Stability}; +use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATURES}; use crate::back::write::create_informational_target_machine; use crate::errors::{ @@ -230,6 +230,8 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea "aarch64" } else if sess.target.arch == "sparc64" { "sparc" + } else if sess.target.arch == "powerpc64" { + "powerpc" } else { &*sess.target.arch }; @@ -289,6 +291,7 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea // https://github.com/llvm/llvm-project/blob/llvmorg-18.1.0/llvm/lib/Target/Sparc/MCTargetDesc/SparcELFObjectWriter.cpp#L26 ("sparc", "v8plus") if get_version().0 == 19 => Some(LLVMFeature::new("v9")), ("sparc", "v8plus") if get_version().0 < 19 => None, + ("powerpc", "power8-crypto") => Some(LLVMFeature::new("crypto")), (_, s) => Some(LLVMFeature::new(s)), } } @@ -297,7 +300,7 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea /// Must express features in the way Rust understands them. /// /// We do not have to worry about RUSTC_SPECIFIC_FEATURES here, those are handled outside codegen. -pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> { +pub fn target_features_cfg(sess: &Session, allow_unstable: bool) -> Vec<Symbol> { let mut features: FxHashSet<Symbol> = Default::default(); // Add base features for the target. @@ -313,7 +316,7 @@ pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> { sess.target .rust_target_features() .iter() - .filter(|(_, gate, _)| gate.is_supported()) + .filter(|(_, gate, _)| gate.in_cfg()) .filter(|(feature, _, _)| { // skip checking special features, as LLVM may not understand them if RUSTC_SPECIAL_FEATURES.contains(feature) { @@ -369,10 +372,10 @@ pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> { sess.target .rust_target_features() .iter() - .filter(|(_, gate, _)| gate.is_supported()) - .filter_map(|&(feature, gate, _)| { - if sess.is_nightly_build() || allow_unstable || gate.is_stable() { - Some(feature) + .filter(|(_, gate, _)| gate.in_cfg()) + .filter_map(|(feature, gate, _)| { + if sess.is_nightly_build() || allow_unstable || gate.requires_nightly().is_none() { + Some(*feature) } else { None } @@ -490,7 +493,7 @@ fn print_target_features(sess: &Session, tm: &llvm::TargetMachine, out: &mut Str .rust_target_features() .iter() .filter_map(|(feature, gate, _implied)| { - if !gate.is_supported() { + if !gate.in_cfg() { // Only list (experimentally) supported features. return None; } @@ -713,13 +716,17 @@ pub(crate) fn global_llvm_features( }; sess.dcx().emit_warn(unknown_feature); } - Some((_, Stability::Stable, _)) => {} - Some((_, Stability::Unstable(_), _)) => { - // An unstable feature. Warn about using it. - sess.dcx().emit_warn(UnstableCTargetFeature { feature }); - } - Some((_, Stability::Forbidden { reason }, _)) => { - sess.dcx().emit_warn(ForbiddenCTargetFeature { feature, reason }); + Some((_, stability, _)) => { + if let Err(reason) = + stability.toggle_allowed(&sess.target, enable_disable == '+') + { + sess.dcx().emit_warn(ForbiddenCTargetFeature { feature, reason }); + } else if stability.requires_nightly().is_some() { + // An unstable feature. Warn about using it. It makes little sense + // to hard-error here since we just warn about fully unknown + // features above. + sess.dcx().emit_warn(UnstableCTargetFeature { feature }); + } } } diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index b898cfec796..628543443b3 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -11,18 +11,19 @@ bitflags = "2.4.1" cc = "1.1.23" either = "1.5.0" itertools = "0.12" -jobserver = "0.1.28" pathdiff = "0.2.0" regex = "1.4" rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } -rustc_attr = { path = "../rustc_attr" } +rustc_ast_pretty = { path = "../rustc_ast_pretty" } +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" } @@ -42,7 +43,7 @@ tempfile = "3.2" thin-vec = "0.2.12" thorin-dwp = "0.8" tracing = "0.1" -wasm-encoder = "0.216.0" +wasm-encoder = "0.219" # tidy-alphabetical-end [target.'cfg(unix)'.dependencies] diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 62db3d5a98c..56188714b44 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -201,6 +201,11 @@ codegen_ssa_missing_memory_ordering = Atomic intrinsic missing memory ordering codegen_ssa_missing_query_depgraph = found CGU-reuse attribute but `-Zquery-dep-graph` was not specified +codegen_ssa_mixed_export_name_and_no_mangle = `{$no_mangle_attr}` attribute may not be used in combination with `#[export_name]` + .label = `{$no_mangle_attr}` is ignored + .note = `#[export_name]` takes precedence + .suggestion = remove the `{$no_mangle_attr}` attribute + codegen_ssa_msvc_missing_linker = the msvc targets depend on the msvc linker but `link.exe` was not found codegen_ssa_multiple_external_func_decl = multiple declarations of external function `{$function}` from library `{$library_name}` have different calling conventions diff --git a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs index 11bcd727501..d1b1ff88b4a 100644 --- a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs +++ b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs @@ -26,9 +26,9 @@ 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; @@ -77,7 +77,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 +158,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 +177,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/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index ad81ff272c6..dee6dd7b262 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, ErrorGuaranteed, FatalError}; +use rustc_errors::{DiagCtxtHandle, FatalError}; 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}; @@ -71,7 +71,7 @@ pub fn link_binary( archive_builder_builder: &dyn ArchiveBuilderBuilder, codegen_results: CodegenResults, outputs: &OutputFilenames, -) -> Result<(), ErrorGuaranteed> { +) { let _timer = sess.timer("link_binary"); let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata); let mut tempfiles_for_stdout_output: Vec<PathBuf> = Vec::new(); @@ -119,7 +119,7 @@ pub fn link_binary( &codegen_results, RlibFlavor::Normal, &path, - )? + ) .build(&out_filename); } CrateType::Staticlib => { @@ -129,7 +129,7 @@ pub fn link_binary( &codegen_results, &out_filename, &path, - )?; + ); } _ => { link_natively( @@ -139,7 +139,7 @@ pub fn link_binary( &out_filename, &codegen_results, path.as_ref(), - )?; + ); } } if sess.opts.json_artifact_notifications { @@ -225,8 +225,6 @@ pub fn link_binary( maybe_remove_temps_from_module(preserve_objects, preserve_dwarf_objects, module); } }); - - Ok(()) } // Crate type is not passed when calculating the dylibs to include for LTO. In that case all @@ -238,7 +236,13 @@ pub fn each_linked_rlib( ) -> Result<(), errors::LinkRlibError> { let crates = info.used_crates.iter(); - let fmts = if crate_type.is_none() { + let fmts = if let Some(crate_type) = crate_type { + let Some(fmts) = info.dependency_formats.get(&crate_type) else { + return Err(errors::LinkRlibError::MissingFormat); + }; + + fmts + } else { for combination in info.dependency_formats.iter().combinations(2) { let (ty1, list1) = &combination[0]; let (ty2, list2) = &combination[1]; @@ -254,18 +258,7 @@ pub fn each_linked_rlib( if info.dependency_formats.is_empty() { return Err(errors::LinkRlibError::MissingFormat); } - &info.dependency_formats[0].1 - } else { - let fmts = info - .dependency_formats - .iter() - .find_map(|&(ty, ref list)| if Some(ty) == crate_type { Some(list) } else { None }); - - let Some(fmts) = fmts else { - return Err(errors::LinkRlibError::MissingFormat); - }; - - fmts + info.dependency_formats.first().unwrap().1 }; for &cnum in crates { @@ -298,7 +291,7 @@ fn link_rlib<'a>( codegen_results: &CodegenResults, flavor: RlibFlavor, tmpdir: &MaybeTempDir, -) -> Result<Box<dyn ArchiveBuilder + 'a>, ErrorGuaranteed> { +) -> Box<dyn ArchiveBuilder + 'a> { let mut ab = archive_builder_builder.new_archive_builder(sess); let trailing_metadata = match flavor { @@ -374,7 +367,7 @@ fn link_rlib<'a>( { let path = find_native_static_library(filename.as_str(), true, sess); let src = read(path) - .map_err(|e| sess.dcx().emit_fatal(errors::ReadFileError { message: e }))?; + .unwrap_or_else(|e| sess.dcx().emit_fatal(errors::ReadFileError { message: e })); let (data, _) = create_wrapper_file(sess, ".bundled_lib".to_string(), &src); let wrapper_file = emit_wrapper_file(sess, &data, tmpdir, filename.as_str()); packed_bundled_libs.push(wrapper_file); @@ -392,7 +385,7 @@ fn link_rlib<'a>( codegen_results.crate_info.used_libraries.iter(), tmpdir.as_ref(), true, - )? { + ) { ab.add_archive(&output_path, Box::new(|_| false)).unwrap_or_else(|error| { sess.dcx().emit_fatal(errors::AddNativeLibrary { library_path: output_path, error }); }); @@ -433,7 +426,7 @@ fn link_rlib<'a>( ab.add_file(&lib) } - Ok(ab) + ab } /// Extract all symbols defined in raw-dylib libraries, collated by library name. @@ -445,7 +438,7 @@ fn link_rlib<'a>( fn collate_raw_dylibs<'a>( sess: &Session, used_libraries: impl IntoIterator<Item = &'a NativeLib>, -) -> Result<Vec<(String, Vec<DllImport>)>, ErrorGuaranteed> { +) -> Vec<(String, Vec<DllImport>)> { // Use index maps to preserve original order of imports and libraries. let mut dylib_table = FxIndexMap::<String, FxIndexMap<Symbol, &DllImport>>::default(); @@ -469,15 +462,13 @@ fn collate_raw_dylibs<'a>( } } } - if let Some(guar) = sess.dcx().has_errors() { - return Err(guar); - } - Ok(dylib_table + sess.dcx().abort_if_errors(); + dylib_table .into_iter() .map(|(name, imports)| { (name, imports.into_iter().map(|(_, import)| import.clone()).collect()) }) - .collect()) + .collect() } fn create_dll_import_libs<'a>( @@ -486,8 +477,8 @@ fn create_dll_import_libs<'a>( used_libraries: impl IntoIterator<Item = &'a NativeLib>, tmpdir: &Path, is_direct_dependency: bool, -) -> Result<Vec<PathBuf>, ErrorGuaranteed> { - Ok(collate_raw_dylibs(sess, used_libraries)? +) -> Vec<PathBuf> { + collate_raw_dylibs(sess, used_libraries) .into_iter() .map(|(raw_dylib_name, raw_dylib_imports)| { let name_suffix = if is_direct_dependency { "_imports" } else { "_imports_indirect" }; @@ -537,7 +528,7 @@ fn create_dll_import_libs<'a>( output_path }) - .collect()) + .collect() } /// Create a static archive. @@ -557,7 +548,7 @@ fn link_staticlib( codegen_results: &CodegenResults, out_filename: &Path, tempdir: &MaybeTempDir, -) -> Result<(), ErrorGuaranteed> { +) { info!("preparing staticlib to {:?}", out_filename); let mut ab = link_rlib( sess, @@ -565,7 +556,7 @@ fn link_staticlib( codegen_results, RlibFlavor::StaticlibBase, tempdir, - )?; + ); let mut all_native_libs = vec![]; let res = each_linked_rlib( @@ -628,8 +619,7 @@ fn link_staticlib( let fmts = codegen_results .crate_info .dependency_formats - .iter() - .find_map(|&(ty, ref list)| if ty == CrateType::Staticlib { Some(list) } else { None }) + .get(&CrateType::Staticlib) .expect("no dependency formats for staticlib"); let mut all_rust_dylibs = vec![]; @@ -656,8 +646,6 @@ fn link_staticlib( print_native_static_libs(sess, &print.out, &all_native_libs, &all_rust_dylibs); } } - - Ok(()) } /// Use `thorin` (rust implementation of a dwarf packaging utility) to link DWARF objects into a @@ -773,7 +761,7 @@ fn link_natively( out_filename: &Path, codegen_results: &CodegenResults, tmpdir: &Path, -) -> Result<(), ErrorGuaranteed> { +) { info!("preparing {:?} to {:?}", crate_type, out_filename); let (linker_path, flavor) = linker_and_flavor(sess); let self_contained_components = self_contained_components(sess, crate_type); @@ -797,7 +785,7 @@ fn link_natively( temp_filename, codegen_results, self_contained_components, - )?; + ); linker::disable_localization(&mut cmd); @@ -998,12 +986,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 @@ -1177,8 +1165,6 @@ fn link_natively( ab.add_file(temp_filename); ab.build(out_filename); } - - Ok(()) } fn strip_symbols_with_external_utility( @@ -2232,7 +2218,7 @@ fn linker_with_args( out_filename: &Path, codegen_results: &CodegenResults, self_contained_components: LinkSelfContainedComponents, -) -> Result<Command, ErrorGuaranteed> { +) -> Command { let self_contained_crt_objects = self_contained_components.is_crt_objects_enabled(); let cmd = &mut *super::linker::get_linker( sess, @@ -2356,18 +2342,17 @@ fn linker_with_args( codegen_results.crate_info.used_libraries.iter(), tmpdir, true, - )? { + ) { cmd.add_object(&output_path); } // As with add_upstream_native_libraries, we need to add the upstream raw-dylib symbols in case // they are used within inlined functions or instantiated generic functions. We do this *after* // handling the raw-dylib symbols in the current crate to make sure that those are chosen first // by the linker. - let (_, dependency_linkage) = codegen_results + let dependency_linkage = codegen_results .crate_info .dependency_formats - .iter() - .find(|(ty, _)| *ty == crate_type) + .get(&crate_type) .expect("failed to find crate type in dependency format list"); // We sort the libraries below @@ -2388,7 +2373,7 @@ fn linker_with_args( native_libraries_from_nonstatics, tmpdir, false, - )? { + ) { cmd.add_object(&output_path); } @@ -2435,7 +2420,7 @@ fn linker_with_args( // to it and remove the option. Currently the last holdout is wasm32-unknown-emscripten. add_post_link_args(cmd, sess, flavor); - Ok(cmd.take_cmd()) + cmd.take_cmd() } fn add_order_independent_options( @@ -2746,13 +2731,21 @@ fn add_upstream_rust_crates( // Linking to a rlib involves just passing it to the linker (the linker // will slurp up the object files inside), and linking to a dynamic library // involves just passing the right -l flag. - let (_, data) = codegen_results + let data = codegen_results .crate_info .dependency_formats - .iter() - .find(|(ty, _)| *ty == crate_type) + .get(&crate_type) .expect("failed to find crate type in dependency format list"); + if sess.target.is_like_aix { + // Unlike ELF linkers, AIX doesn't feature `DT_SONAME` to override + // the dependency name when outputing a shared library. Thus, `ld` will + // use the full path to shared libraries as the dependency if passed it + // by default unless `noipath` is passed. + // https://www.ibm.com/docs/en/aix/7.3?topic=l-ld-command. + cmd.link_or_cc_arg("-bnoipath"); + } + for &cnum in &codegen_results.crate_info.used_crates { // We may not pass all crates through to the linker. Some crates may appear statically in // an existing dylib, meaning we'll pick up all the symbols from the dylib. @@ -2989,7 +2982,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 05dfbd40a0a..301b22f2be4 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -1694,6 +1694,8 @@ impl<'a> Linker for AixLinker<'a> { fn pgo_gen(&mut self) { self.link_arg("-bdbg:namedsects:ss"); + self.link_arg("-u"); + self.link_arg("__llvm_profile_runtime"); } fn control_flow_guard(&mut self) {} @@ -1747,7 +1749,7 @@ fn for_each_exported_symbols_include_dep<'tcx>( } let formats = tcx.dependency_formats(()); - let deps = formats.iter().find_map(|(t, list)| (*t == crate_type).then_some(list)).unwrap(); + let deps = &formats[&crate_type]; for (index, dep_format) in deps.iter().enumerate() { let cnum = CrateNum::new(index + 1); 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 7c0e9cfd5a7..683defcafee 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -6,9 +6,9 @@ use std::sync::Arc; use std::sync::mpsc::{Receiver, Sender, channel}; use std::{fs, io, mem, str, thread}; -use jobserver::{Acquired, Client}; use rustc_ast::attr; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; +use rustc_data_structures::jobserver::{self, Acquired}; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::profiling::{SelfProfilerRef, VerboseTimingGuard}; use rustc_errors::emitter::Emitter; @@ -34,7 +34,7 @@ use rustc_session::config::{ }; use rustc_span::source_map::SourceMap; use rustc_span::symbol::sym; -use rustc_span::{BytePos, FileName, InnerSpan, Pos, Span}; +use rustc_span::{FileName, InnerSpan, Span, SpanData}; use rustc_target::spec::{MergeFunctions, SanitizerSet}; use tracing::debug; @@ -456,7 +456,6 @@ pub(crate) fn start_async_codegen<B: ExtraBackendMethods>( metadata_module: Option<CompiledModule>, ) -> OngoingCodegen<B> { let (coordinator_send, coordinator_receive) = channel(); - let sess = tcx.sess; let crate_attrs = tcx.hir().attrs(rustc_hir::CRATE_HIR_ID); let no_builtins = attr::contains_name(crate_attrs, sym::no_builtins); @@ -477,7 +476,6 @@ pub(crate) fn start_async_codegen<B: ExtraBackendMethods>( shared_emitter, codegen_worker_send, coordinator_receive, - sess.jobserver.clone(), Arc::new(regular_config), Arc::new(metadata_config), Arc::new(allocator_config), @@ -1093,7 +1091,6 @@ fn start_executing_work<B: ExtraBackendMethods>( shared_emitter: SharedEmitter, codegen_worker_send: Sender<CguMessage>, coordinator_receive: Receiver<Box<dyn Any + Send>>, - jobserver: Client, regular_config: Arc<ModuleConfig>, metadata_config: Arc<ModuleConfig>, allocator_config: Arc<ModuleConfig>, @@ -1145,7 +1142,7 @@ fn start_executing_work<B: ExtraBackendMethods>( // get tokens on `coordinator_receive` which will // get managed in the main loop below. let coordinator_send2 = coordinator_send.clone(); - let helper = jobserver + let helper = jobserver::client() .into_helper_thread(move |token| { drop(coordinator_send2.send(Box::new(Message::Token::<B>(token)))); }) @@ -1837,7 +1834,7 @@ fn spawn_work<'a, B: ExtraBackendMethods>( enum SharedEmitterMessage { Diagnostic(Diagnostic), - InlineAsmError(u32, String, Level, Option<(String, Vec<InnerSpan>)>), + InlineAsmError(SpanData, String, Level, Option<(String, Vec<InnerSpan>)>), Fatal(String), } @@ -1859,12 +1856,12 @@ impl SharedEmitter { pub fn inline_asm_error( &self, - cookie: u32, + span: SpanData, msg: String, level: Level, source: Option<(String, Vec<InnerSpan>)>, ) { - drop(self.sender.send(SharedEmitterMessage::InlineAsmError(cookie, msg, level, source))); + drop(self.sender.send(SharedEmitterMessage::InlineAsmError(span, msg, level, source))); } fn fatal(&self, msg: &str) { @@ -1883,7 +1880,11 @@ impl Translate for SharedEmitter { } impl Emitter for SharedEmitter { - fn emit_diagnostic(&mut self, mut diag: rustc_errors::DiagInner) { + fn emit_diagnostic( + &mut self, + mut diag: rustc_errors::DiagInner, + _registry: &rustc_errors::registry::Registry, + ) { // Check that we aren't missing anything interesting when converting to // the cut-down local `DiagInner`. assert_eq!(diag.span, MultiSpan::new()); @@ -1949,17 +1950,12 @@ impl SharedEmitterMain { dcx.emit_diagnostic(d); sess.dcx().abort_if_errors(); } - Ok(SharedEmitterMessage::InlineAsmError(cookie, msg, level, source)) => { + Ok(SharedEmitterMessage::InlineAsmError(span, msg, level, source)) => { assert_matches!(level, Level::Error | Level::Warning | Level::Note); - let msg = msg.strip_prefix("error: ").unwrap_or(&msg).to_string(); let mut err = Diag::<()>::new(sess.dcx(), level, msg); - - // If the cookie is 0 then we don't have span information. - if cookie != 0 { - let pos = BytePos::from_u32(cookie); - let span = Span::with_root_ctxt(pos, pos); - err.span(span); - }; + if !span.is_dummy() { + err.span(span.span()); + } // Point to the generated assembly if it is available. if let Some((buffer, spans)) = source { @@ -2028,8 +2024,6 @@ pub struct OngoingCodegen<B: ExtraBackendMethods> { impl<B: ExtraBackendMethods> OngoingCodegen<B> { pub fn join(self, sess: &Session) -> (CodegenResults, FxIndexMap<WorkProductId, WorkProduct>) { - let _timer = sess.timer("finish_ongoing_codegen"); - self.shared_emitter_main.check(sess, true); let compiled_modules = sess.time("join_worker_thread", || match self.coordinator.join() { Ok(Ok(compiled_modules)) => compiled_modules, diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 27c9cb0b31e..dab035d3339 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}; @@ -31,6 +30,7 @@ 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 +873,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 a5acd8170ab..b243f904aee 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -1,13 +1,13 @@ -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}; -use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::weak_lang_items::WEAK_LANG_ITEMS; -use rustc_hir::{LangItem, lang_items}; +use rustc_hir::{self as hir, HirId, LangItem, lang_items}; use rustc_middle::middle::codegen_fn_attrs::{ CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, }; @@ -78,6 +78,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { let mut inline_span = None; let mut link_ordinal_span = None; let mut no_sanitize_span = None; + let mut mixed_export_name_no_mangle_lint_state = MixedExportNameAndNoMangleState::default(); for attr in attrs.iter() { // In some cases, attribute are only valid on functions, but it's the `check_attr` @@ -116,7 +117,12 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { sym::naked => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED, sym::no_mangle => { if tcx.opt_item_name(did.to_def_id()).is_some() { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; + mixed_export_name_no_mangle_lint_state.track_no_mangle( + attr.span, + tcx.local_def_id_to_hir_id(did), + attr, + ); } else { tcx.dcx() .struct_span_err( @@ -240,12 +246,13 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { .emit(); } codegen_fn_attrs.export_name = Some(s); + mixed_export_name_no_mangle_lint_state.track_export_name(attr.span); } } 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 @@ -419,7 +426,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(), @@ -513,61 +520,65 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } } + mixed_export_name_no_mangle_lint_state.lint_if_mixed(tcx); + codegen_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| { 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 } }); + // naked function MUST NOT be inlined! This attribute is required for the rust compiler itself, + // but not for the code generation backend because at that point the naked function will just be + // a declaration, with a definition provided in global assembly. + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) { + codegen_fn_attrs.inline = InlineAttr::Never; + } + codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::None, |ia, attr| { if !attr.has_name(sym::optimize) { 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 } }); @@ -626,10 +637,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } } - if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) { - codegen_fn_attrs.inline = InlineAttr::Never; - } - // Weak lang items have the same semantics as "std internal" symbols in the // sense that they're preserved through all our LTO passes and only // strippable by the linker. @@ -719,7 +726,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(); @@ -779,6 +786,49 @@ fn check_link_name_xor_ordinal( } } +#[derive(Default)] +struct MixedExportNameAndNoMangleState<'a> { + export_name: Option<Span>, + hir_id: Option<HirId>, + no_mangle: Option<Span>, + no_mangle_attr: Option<&'a hir::Attribute>, +} + +impl<'a> MixedExportNameAndNoMangleState<'a> { + fn track_export_name(&mut self, span: Span) { + self.export_name = Some(span); + } + + 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); + } + + /// Emit diagnostics if the lint condition is met. + fn lint_if_mixed(self, tcx: TyCtxt<'_>) { + if let Self { + export_name: Some(export_name), + no_mangle: Some(no_mangle), + hir_id: Some(hir_id), + no_mangle_attr: Some(no_mangle_attr), + } = self + { + tcx.emit_node_span_lint( + lint::builtin::UNUSED_ATTRIBUTES, + hir_id, + no_mangle, + errors::MixedExportNameAndNoMangle { + no_mangle, + no_mangle_attr: rustc_hir_pretty::attribute_to_string(&tcx, no_mangle_attr), + export_name, + removal_span: no_mangle, + }, + ); + } + } +} + pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { codegen_fn_attrs, should_inherit_track_caller, ..*providers }; } diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 6c4f6d37972..cf72c2ed742 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -432,11 +432,8 @@ fn push_debuginfo_type_name<'tcx>( push_closure_or_coroutine_name(tcx, def_id, args, qualified, output, visited); } } - // Type parameters from polymorphized functions. - ty::Param(_) => { - write!(output, "{t:?}").unwrap(); - } - ty::Error(_) + ty::Param(_) + | ty::Error(_) | ty::Infer(_) | ty::Placeholder(..) | ty::Alias(..) diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index f93cb52ea3e..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}; @@ -10,7 +11,7 @@ use rustc_errors::codes::*; use rustc_errors::{ Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, IntoDiagArg, Level, }; -use rustc_macros::{Diagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::LayoutError; use rustc_span::{Span, Symbol}; @@ -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. @@ -1114,3 +1176,15 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for TargetFeatureDisableOrEnable<'_ #[derive(Diagnostic)] #[diag(codegen_ssa_aix_strip_not_used)] pub(crate) struct AixStripNotUsed; + +#[derive(LintDiagnostic)] +#[diag(codegen_ssa_mixed_export_name_and_no_mangle)] +pub(crate) struct MixedExportNameAndNoMangle { + #[label] + pub no_mangle: Span, + pub no_mangle_attr: String, + #[note] + pub export_name: Span, + #[suggestion(style = "verbose", code = "", applicability = "machine-applicable")] + pub removal_span: Span, +} diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index e3ed12b5ce6..b0a1dedd646 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -847,10 +847,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let (instance, mut llfn) = match *callee.layout.ty.kind() { ty::FnDef(def_id, args) => ( - Some( - ty::Instance::expect_resolve(bx.tcx(), bx.typing_env(), def_id, args, fn_span) - .polymorphize(bx.tcx()), - ), + Some(ty::Instance::expect_resolve( + bx.tcx(), + bx.typing_env(), + def_id, + args, + fn_span, + )), None, ), ty::FnPtr(..) => (None, Some(callee.immediate())), diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 0cbc5c45736..62f69af3f2f 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -20,6 +20,7 @@ mod coverageinfo; pub mod debuginfo; mod intrinsic; mod locals; +mod naked_asm; pub mod operand; pub mod place; mod rvalue; @@ -176,6 +177,11 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty()); debug!("fn_abi: {:?}", fn_abi); + if cx.tcx().codegen_fn_attrs(instance.def_id()).flags.contains(CodegenFnAttrFlags::NAKED) { + crate::mir::naked_asm::codegen_naked_asm::<Bx>(cx, &mir, instance); + return; + } + let debug_context = cx.create_function_debug_context(instance, fn_abi, llfn, mir); let start_llbb = Bx::append_block(cx, llfn, "start"); diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs new file mode 100644 index 00000000000..cac3cc587cb --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -0,0 +1,266 @@ +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}; +use rustc_middle::ty::{Instance, TyCtxt}; +use rustc_middle::{bug, ty}; +use rustc_span::sym; + +use crate::common; +use crate::traits::{AsmCodegenMethods, BuilderMethods, GlobalAsmOperandRef, MiscCodegenMethods}; + +pub(crate) fn codegen_naked_asm<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + cx: &'a Bx::CodegenCx, + mir: &Body<'tcx>, + instance: Instance<'tcx>, +) { + let rustc_middle::mir::TerminatorKind::InlineAsm { + asm_macro: _, + template, + ref operands, + options, + line_spans, + targets: _, + unwind: _, + } = mir.basic_blocks.iter().next().unwrap().terminator().kind + else { + bug!("#[naked] functions should always terminate with an asm! block") + }; + + let operands: Vec<_> = + operands.iter().map(|op| inline_to_global_operand::<Bx>(cx, instance, op)).collect(); + + let item_data = cx.codegen_unit().items().get(&MonoItem::Fn(instance)).unwrap(); + let name = cx.mangled_name(instance); + let (begin, end) = prefix_and_suffix(cx.tcx(), instance, &name, item_data); + + let mut template_vec = Vec::new(); + template_vec.push(rustc_ast::ast::InlineAsmTemplatePiece::String(begin.into())); + template_vec.extend(template.iter().cloned()); + template_vec.push(rustc_ast::ast::InlineAsmTemplatePiece::String(end.into())); + + cx.codegen_global_asm(&template_vec, &operands, options, line_spans); +} + +fn inline_to_global_operand<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + cx: &'a Bx::CodegenCx, + instance: Instance<'tcx>, + op: &InlineAsmOperand<'tcx>, +) -> GlobalAsmOperandRef<'tcx> { + match op { + InlineAsmOperand::Const { value } => { + let const_value = instance + .instantiate_mir_and_normalize_erasing_regions( + cx.tcx(), + cx.typing_env(), + ty::EarlyBinder::bind(value.const_), + ) + .eval(cx.tcx(), cx.typing_env(), value.span) + .expect("erroneous constant missed by mono item collection"); + + let mono_type = instance.instantiate_mir_and_normalize_erasing_regions( + cx.tcx(), + cx.typing_env(), + ty::EarlyBinder::bind(value.ty()), + ); + + let string = common::asm_const_to_str( + cx.tcx(), + value.span, + const_value, + cx.layout_of(mono_type), + ); + + GlobalAsmOperandRef::Const { string } + } + InlineAsmOperand::SymFn { value } => { + let mono_type = instance.instantiate_mir_and_normalize_erasing_regions( + cx.tcx(), + cx.typing_env(), + ty::EarlyBinder::bind(value.ty()), + ); + + let instance = match mono_type.kind() { + &ty::FnDef(def_id, args) => Instance::new(def_id, args), + _ => bug!("asm sym is not a function"), + }; + + GlobalAsmOperandRef::SymFn { instance } + } + InlineAsmOperand::SymStatic { def_id } => { + GlobalAsmOperandRef::SymStatic { def_id: *def_id } + } + InlineAsmOperand::In { .. } + | InlineAsmOperand::Out { .. } + | InlineAsmOperand::InOut { .. } + | InlineAsmOperand::Label { .. } => { + bug!("invalid operand type for naked_asm!") + } + } +} + +enum AsmBinaryFormat { + Elf, + Macho, + Coff, +} + +impl AsmBinaryFormat { + fn from_target(target: &rustc_target::spec::Target) -> Self { + if target.is_like_windows { + Self::Coff + } else if target.is_like_osx { + Self::Macho + } else { + Self::Elf + } + } +} + +fn prefix_and_suffix<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + asm_name: &str, + item_data: &MonoItemData, +) -> (String, String) { + use std::fmt::Write; + + let asm_binary_format = AsmBinaryFormat::from_target(&tcx.sess.target); + + let is_arm = tcx.sess.target.arch == "arm"; + let is_thumb = tcx.sess.unstable_target_features.contains(&sym::thumb_mode); + + let attrs = tcx.codegen_fn_attrs(instance.def_id()); + let link_section = attrs.link_section.map(|symbol| symbol.as_str().to_string()); + let align = attrs.alignment.map(|a| a.bytes()).unwrap_or(4); + + // See https://sourceware.org/binutils/docs/as/ARM-Directives.html for info on these directives. + // In particular, `.arm` can also be written `.code 32` and `.thumb` as `.code 16`. + let (arch_prefix, arch_suffix) = if is_arm { + ( + match attrs.instruction_set { + None => match is_thumb { + true => ".thumb\n.thumb_func", + false => ".arm", + }, + Some(InstructionSetAttr::ArmT32) => ".thumb\n.thumb_func", + Some(InstructionSetAttr::ArmA32) => ".arm", + }, + match is_thumb { + true => ".thumb", + false => ".arm", + }, + ) + } else { + ("", "") + }; + + let emit_fatal = |msg| tcx.dcx().span_fatal(tcx.def_span(instance.def_id()), msg); + + // see https://godbolt.org/z/cPK4sxKor. + let write_linkage = |w: &mut String| -> std::fmt::Result { + match item_data.linkage { + Linkage::External => { + writeln!(w, ".globl {asm_name}")?; + } + Linkage::LinkOnceAny | Linkage::LinkOnceODR | Linkage::WeakAny | Linkage::WeakODR => { + match asm_binary_format { + AsmBinaryFormat::Elf | AsmBinaryFormat::Coff => { + writeln!(w, ".weak {asm_name}")?; + } + AsmBinaryFormat::Macho => { + writeln!(w, ".globl {asm_name}")?; + writeln!(w, ".weak_definition {asm_name}")?; + } + } + } + Linkage::Internal | Linkage::Private => { + // write nothing + } + Linkage::Appending => emit_fatal("Only global variables can have appending linkage!"), + Linkage::Common => emit_fatal("Functions may not have common linkage"), + Linkage::AvailableExternally => { + // this would make the function equal an extern definition + emit_fatal("Functions may not have available_externally linkage") + } + Linkage::ExternalWeak => { + // FIXME: actually this causes a SIGILL in LLVM + emit_fatal("Functions may not have external weak linkage") + } + } + + Ok(()) + }; + + let mut begin = String::new(); + let mut end = String::new(); + match asm_binary_format { + AsmBinaryFormat::Elf => { + let section = link_section.unwrap_or(format!(".text.{asm_name}")); + + let progbits = match is_arm { + true => "%progbits", + false => "@progbits", + }; + + let function = match is_arm { + true => "%function", + false => "@function", + }; + + writeln!(begin, ".pushsection {section},\"ax\", {progbits}").unwrap(); + writeln!(begin, ".balign {align}").unwrap(); + write_linkage(&mut begin).unwrap(); + if let Visibility::Hidden = item_data.visibility { + writeln!(begin, ".hidden {asm_name}").unwrap(); + } + writeln!(begin, ".type {asm_name}, {function}").unwrap(); + if !arch_prefix.is_empty() { + writeln!(begin, "{}", arch_prefix).unwrap(); + } + writeln!(begin, "{asm_name}:").unwrap(); + + writeln!(end).unwrap(); + writeln!(end, ".size {asm_name}, . - {asm_name}").unwrap(); + writeln!(end, ".popsection").unwrap(); + if !arch_suffix.is_empty() { + writeln!(end, "{}", arch_suffix).unwrap(); + } + } + AsmBinaryFormat::Macho => { + let section = link_section.unwrap_or("__TEXT,__text".to_string()); + writeln!(begin, ".pushsection {},regular,pure_instructions", section).unwrap(); + writeln!(begin, ".balign {align}").unwrap(); + write_linkage(&mut begin).unwrap(); + if let Visibility::Hidden = item_data.visibility { + writeln!(begin, ".private_extern {asm_name}").unwrap(); + } + writeln!(begin, "{asm_name}:").unwrap(); + + writeln!(end).unwrap(); + writeln!(end, ".popsection").unwrap(); + if !arch_suffix.is_empty() { + writeln!(end, "{}", arch_suffix).unwrap(); + } + } + AsmBinaryFormat::Coff => { + let section = link_section.unwrap_or(format!(".text.{asm_name}")); + writeln!(begin, ".pushsection {},\"xr\"", section).unwrap(); + writeln!(begin, ".balign {align}").unwrap(); + write_linkage(&mut begin).unwrap(); + writeln!(begin, ".def {asm_name}").unwrap(); + writeln!(begin, ".scl 2").unwrap(); + writeln!(begin, ".type 32").unwrap(); + writeln!(begin, ".endef {asm_name}").unwrap(); + writeln!(begin, "{asm_name}:").unwrap(); + + writeln!(end).unwrap(); + writeln!(end, ".popsection").unwrap(); + if !arch_suffix.is_empty() { + writeln!(end, "{}", arch_suffix).unwrap(); + } + } + } + + (begin, end) +} diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index c38484109d2..a9e80e27ed4 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -422,10 +422,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/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index f63b2d139c5..cf537392234 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -478,8 +478,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { def_id, args, ) - .unwrap() - .polymorphize(bx.cx().tcx()); + .unwrap(); OperandValue::Immediate(bx.get_fn_addr(instance)) } _ => bug!("{} cannot be reified to a fn ptr", operand.layout.ty), @@ -493,8 +492,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { def_id, args, ty::ClosureKind::FnOnce, - ) - .polymorphize(bx.cx().tcx()); + ); OperandValue::Immediate(bx.cx().get_fn_addr(instance)) } _ => bug!("{} cannot be cast to a fn ptr", operand.layout.ty), diff --git a/compiler/rustc_codegen_ssa/src/mono_item.rs b/compiler/rustc_codegen_ssa/src/mono_item.rs index 038c5857fac..6749bc63327 100644 --- a/compiler/rustc_codegen_ssa/src/mono_item.rs +++ b/compiler/rustc_codegen_ssa/src/mono_item.rs @@ -1,4 +1,5 @@ use rustc_hir as hir; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::mir::mono::{Linkage, MonoItem, Visibility}; use rustc_middle::ty::Instance; @@ -135,7 +136,13 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> { cx.predefine_static(def_id, linkage, visibility, symbol_name); } MonoItem::Fn(instance) => { - cx.predefine_fn(instance, linkage, visibility, symbol_name); + let attrs = cx.tcx().codegen_fn_attrs(instance.def_id()); + + if attrs.flags.contains(CodegenFnAttrFlags::NAKED) { + // do not define this function; it will become a global assembly block + } else { + cx.predefine_fn(instance, linkage, visibility, symbol_name); + }; } MonoItem::GlobalAsm(..) => {} } diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index eee7cc75400..c31e8aa7d31 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -1,8 +1,8 @@ -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; @@ -11,7 +11,7 @@ use rustc_middle::ty::TyCtxt; use rustc_session::parse::feature_err; use rustc_span::Span; use rustc_span::symbol::{Symbol, sym}; -use rustc_target::target_features::{self, Stability}; +use rustc_target::target_features; use crate::errors; @@ -19,8 +19,8 @@ use crate::errors; /// Enabled target features are added to `target_features`. pub(crate) fn from_target_feature_attr( tcx: TyCtxt<'_>, - attr: &ast::Attribute, - rust_target_features: &UnordMap<String, target_features::Stability>, + attr: &hir::Attribute, + rust_target_features: &UnordMap<String, target_features::StabilityComputed>, target_features: &mut Vec<TargetFeature>, ) { let Some(list) = attr.meta_item_list() else { return }; @@ -63,32 +63,24 @@ pub(crate) fn from_target_feature_attr( return None; }; - // Only allow target features whose feature gates have been enabled. - let allowed = match stability { - Stability::Forbidden { .. } => false, - Stability::Stable => true, - Stability::Unstable(name) => rust_features.enabled(*name), - }; - if !allowed { - match stability { - Stability::Stable => unreachable!(), - &Stability::Unstable(lang_feature_name) => { - feature_err( - &tcx.sess, - lang_feature_name, - item.span(), - format!("the target feature `{feature}` is currently unstable"), - ) - .emit(); - } - Stability::Forbidden { reason } => { - tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr { - span: item.span(), - feature, - reason, - }); - } - } + // Only allow target features whose feature gates have been enabled + // and which are permitted to be toggled. + if let Err(reason) = stability.toggle_allowed(/*enable*/ true) { + tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr { + span: item.span(), + feature, + reason, + }); + } else if let Some(nightly_feature) = stability.requires_nightly() + && !rust_features.enabled(nightly_feature) + { + feature_err( + &tcx.sess, + nightly_feature, + item.span(), + format!("the target feature `{feature}` is currently unstable"), + ) + .emit(); } Some(Symbol::intern(feature)) })); @@ -156,18 +148,19 @@ pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { rust_target_features: |tcx, cnum| { assert_eq!(cnum, LOCAL_CRATE); + let target = &tcx.sess.target; if tcx.sess.opts.actually_rustdoc { // rustdoc needs to be able to document functions that use all the features, so // whitelist them all rustc_target::target_features::all_rust_features() - .map(|(a, b)| (a.to_string(), b)) + .map(|(a, b)| (a.to_string(), b.compute_toggleability(target))) .collect() } else { tcx.sess .target .rust_target_features() .iter() - .map(|&(a, b, _)| (a.to_string(), b)) + .map(|(a, b, _)| (a.to_string(), b.compute_toggleability(target))) .collect() } }, diff --git a/compiler/rustc_codegen_ssa/src/traits/asm.rs b/compiler/rustc_codegen_ssa/src/traits/asm.rs index f4853da1156..7767bffbfbf 100644 --- a/compiler/rustc_codegen_ssa/src/traits/asm.rs +++ b/compiler/rustc_codegen_ssa/src/traits/asm.rs @@ -68,4 +68,11 @@ pub trait AsmCodegenMethods<'tcx> { options: InlineAsmOptions, line_spans: &[Span], ); + + /// The mangled name of this instance + /// + /// Additional mangling is used on + /// some targets to add a leading underscore (Mach-O) + /// or byte count suffixes (x86 Windows). + fn mangled_name(&self, instance: Instance<'tcx>) -> String; } diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 7eab889edf0..4b17db2c49e 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -4,7 +4,6 @@ use std::hash::Hash; use rustc_ast::expand::allocator::AllocatorKind; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::{DynSend, DynSync}; -use rustc_errors::ErrorGuaranteed; use rustc_metadata::EncodedMetadata; use rustc_metadata::creader::MetadataLoaderDyn; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; @@ -46,7 +45,9 @@ pub trait CodegenBackend { fn print(&self, _req: &PrintRequest, _out: &mut String, _sess: &Session) {} - fn target_features(&self, _sess: &Session, _allow_unstable: bool) -> Vec<Symbol> { + /// Returns the features that should be set in `cfg(target_features)`. + /// RUSTC_SPECIFIC_FEATURES should be skipped here, those are handled outside codegen. + fn target_features_cfg(&self, _sess: &Session, _allow_unstable: bool) -> Vec<Symbol> { vec![] } @@ -84,13 +85,8 @@ pub trait CodegenBackend { ) -> (CodegenResults, FxIndexMap<WorkProductId, WorkProduct>); /// This is called on the returned [`CodegenResults`] from [`join_codegen`](Self::join_codegen). - fn link( - &self, - sess: &Session, - codegen_results: CodegenResults, - outputs: &OutputFilenames, - ) -> Result<(), ErrorGuaranteed> { - link_binary(sess, &ArArchiveBuilderBuilder, codegen_results, outputs) + fn link(&self, sess: &Session, codegen_results: CodegenResults, outputs: &OutputFilenames) { + link_binary(sess, &ArArchiveBuilderBuilder, codegen_results, outputs); } /// Returns `true` if this backend can be safely called from multiple threads. 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 916929b6c0b..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}; @@ -573,12 +573,27 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { ) => {} Rvalue::ShallowInitBox(_, _) => {} - Rvalue::UnaryOp(_, operand) => { + Rvalue::UnaryOp(op, operand) => { let ty = operand.ty(self.body, self.tcx); - if is_int_bool_float_or_char(ty) { - // Int, bool, float, and char operations are fine. - } else { - span_bug!(self.span, "non-primitive type in `Rvalue::UnaryOp`: {:?}", ty); + match op { + UnOp::Not | UnOp::Neg => { + if is_int_bool_float_or_char(ty) { + // Int, bool, float, and char operations are fine. + } else { + span_bug!( + self.span, + "non-primitive type in `Rvalue::UnaryOp{op:?}`: {ty:?}", + ); + } + } + UnOp::PtrMetadata => { + if !ty.is_ref() && !ty.is_unsafe_ptr() { + span_bug!( + self.span, + "non-pointer type in `Rvalue::UnaryOp({op:?})`: {ty:?}", + ); + } + } } } 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 489bb54a6f9..23f2aa4d029 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -140,7 +140,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { err, param_ty.name.as_str(), &constraint, - None, + Some(trait_ref.def_id), None, ); } diff --git a/compiler/rustc_const_eval/src/check_consts/resolver.rs b/compiler/rustc_const_eval/src/check_consts/resolver.rs index 0f9a460ca1b..763c37a41af 100644 --- a/compiler/rustc_const_eval/src/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/check_consts/resolver.rs @@ -329,7 +329,7 @@ where self.transfer_function(state).initialize_state(); } - fn apply_statement_effect( + fn apply_primary_statement_effect( &mut self, state: &mut Self::Domain, statement: &mir::Statement<'tcx>, @@ -338,7 +338,7 @@ where self.transfer_function(state).visit_statement(statement, location); } - fn apply_terminator_effect<'mir>( + fn apply_primary_terminator_effect<'mir>( &mut self, state: &mut Self::Domain, terminator: &'mir mir::Terminator<'tcx>, 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 e49d702127d..817acfcca74 100644 --- a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs @@ -168,9 +168,9 @@ impl<'tcx> interpret::Machine<'tcx> for DummyMachine { }) } - fn expose_ptr( - _ecx: &mut InterpCx<'tcx, Self>, - _ptr: interpret::Pointer<Self::Provenance>, + fn expose_provenance( + _ecx: &InterpCx<'tcx, Self>, + _provenance: Self::Provenance, ) -> interpret::InterpResult<'tcx> { unimplemented!() } diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index 3cb77d1dcb5..fee7287951d 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -170,7 +170,7 @@ where let reported = if allowed_in_infallible { ReportedErrorInfo::allowed_in_infallible(g) } else { - ReportedErrorInfo::from(g) + ReportedErrorInfo::const_eval_error(g) }; ErrorHandled::Reported(reported, span) } diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 647d880e2bf..ce8eceebdf8 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -3,13 +3,13 @@ use std::sync::atomic::Ordering::Relaxed; use either::{Left, Right}; use rustc_abi::{self as abi, BackendRepr}; use rustc_hir::def::DefKind; -use rustc_middle::bug; -use rustc_middle::mir::interpret::{AllocId, ErrorHandled, InterpErrorInfo}; +use rustc_middle::mir::interpret::{AllocId, ErrorHandled, InterpErrorInfo, ReportedErrorInfo}; use rustc_middle::mir::{self, ConstAlloc, ConstValue}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::{bug, throw_inval}; use rustc_span::def_id::LocalDefId; use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument, trace}; @@ -93,18 +93,18 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>( match intern_result { Ok(()) => {} Err(InternResult::FoundDanglingPointer) => { - return Err(ecx - .tcx - .dcx() - .emit_err(errors::DanglingPtrInFinal { span: ecx.tcx.span, kind: intern_kind })) - .into(); + throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error( + ecx.tcx + .dcx() + .emit_err(errors::DanglingPtrInFinal { span: ecx.tcx.span, kind: intern_kind }), + ))); } Err(InternResult::FoundBadMutablePointer) => { - return Err(ecx - .tcx - .dcx() - .emit_err(errors::MutablePtrInFinal { span: ecx.tcx.span, kind: intern_kind })) - .into(); + throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error( + ecx.tcx + .dcx() + .emit_err(errors::MutablePtrInFinal { span: ecx.tcx.span, kind: intern_kind }), + ))); } } 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 beff0cd99fc..babf99c4c1f 100644 --- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs @@ -4,27 +4,29 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; -pub fn is_parent_const_impl_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { +fn parent_impl_constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Constness { let parent_id = tcx.local_parent(def_id); - matches!(tcx.def_kind(parent_id), DefKind::Impl { .. }) - && tcx.constness(parent_id) == hir::Constness::Const + if matches!(tcx.def_kind(parent_id), DefKind::Impl { .. }) + && let Some(header) = tcx.impl_trait_header(parent_id) + { + header.constness + } else { + hir::Constness::NotConst + } } -/// Checks whether an item is considered to be `const`. If it is a constructor, anonymous const, -/// const block, const item or associated const, it is const. If it is a trait impl/function, +/// Checks whether an item is considered to be `const`. If it is a constructor, it is const. +/// If it is an assoc method or function, /// return if it has a `const` modifier. If it is an intrinsic, report whether said intrinsic -/// has a `rustc_const_{un,}stable` attribute. Otherwise, return `Constness::NotConst`. +/// has a `rustc_const_{un,}stable` attribute. Otherwise, panic. fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Constness { let node = tcx.hir_node_by_def_id(def_id); match node { - hir::Node::Ctor(_) - | hir::Node::AnonConst(_) - | hir::Node::ConstBlock(_) + hir::Node::Ctor(hir::VariantData::Tuple(..)) | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. }) => { hir::Constness::Const } - hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(impl_), .. }) => impl_.constness, hir::Node::ForeignItem(_) => { // Foreign items cannot be evaluated at compile-time. hir::Constness::NotConst @@ -38,10 +40,12 @@ fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Constness { // If the function itself is not annotated with `const`, it may still be a `const fn` // if it resides in a const trait impl. - let is_const = is_parent_const_impl_raw(tcx, def_id); - if is_const { hir::Constness::Const } else { hir::Constness::NotConst } + parent_impl_constness(tcx, def_id) } else { - hir::Constness::NotConst + tcx.dcx().span_bug( + tcx.def_span(def_id), + format!("should not be requesting the constness of items that can't be const: {node:#?}: {:?}", tcx.def_kind(def_id)) + ) } } } @@ -53,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 b27e3606f38..23683851799 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -8,6 +8,7 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexMap, IndexEntry}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{self as hir, CRATE_HIR_ID, LangItem}; use rustc_middle::mir::AssertMessage; +use rustc_middle::mir::interpret::ReportedErrorInfo; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout}; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -21,9 +22,8 @@ use crate::errors::{LongRunning, LongRunningWarn}; use crate::fluent_generated as fluent; use crate::interpret::{ self, AllocId, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, GlobalAlloc, ImmTy, - InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, RangeSet, Scalar, compile_time_machine, - interp_ok, throw_exhaust, throw_inval, throw_ub, throw_ub_custom, throw_unsup, - throw_unsup_format, + InterpCx, InterpResult, MPlaceTy, OpTy, RangeSet, Scalar, compile_time_machine, interp_ok, + throw_exhaust, throw_inval, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format, }; /// When hitting this many interpreted terminators we emit a deny by default lint @@ -249,10 +249,9 @@ impl<'tcx> CompileTimeInterpCx<'tcx> { } else if self.tcx.is_lang_item(def_id, LangItem::PanicFmt) { // For panic_fmt, call const_panic_fmt instead. let const_def_id = self.tcx.require_lang_item(LangItem::ConstPanicFmt, None); - // FIXME(@lcnr): why does this use an empty env if we've got a `param_env` right here. let new_instance = ty::Instance::expect_resolve( *self.tcx, - ty::TypingEnv::fully_monomorphized(), + self.typing_env(), const_def_id, instance.args, self.cur_span(), @@ -564,7 +563,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { .tcx .dcx() .span_delayed_bug(span, "The deny lint should have already errored"); - throw_inval!(AlreadyReported(guard.into())); + throw_inval!(AlreadyReported(ReportedErrorInfo::allowed_in_infallible(guard))); } } else if new_steps > start && new_steps.is_power_of_two() { // Only report after a certain number of terminators have been evaluated and the @@ -586,7 +585,10 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { } #[inline(always)] - fn expose_ptr(_ecx: &mut InterpCx<'tcx, Self>, _ptr: Pointer) -> InterpResult<'tcx> { + fn expose_provenance( + _ecx: &InterpCx<'tcx, Self>, + _provenance: Self::Provenance, + ) -> InterpResult<'tcx> { // This is only reachable with -Zunleash-the-miri-inside-of-you. throw_unsup_format!("exposing pointers is not possible at compile-time") } diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 515028e6826..6f51b09323d 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -1,6 +1,6 @@ use rustc_abi::{BackendRepr, VariantIdx}; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId}; +use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId, ReportedErrorInfo}; use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt}; use rustc_middle::{bug, mir}; @@ -261,7 +261,7 @@ pub(crate) fn eval_to_valtree<'tcx>( ValTreeCreationError::NodesOverflow => { let handled = tcx.dcx().emit_err(MaxNumNodesInConstErr { span, global_const_id }); - Err(handled.into()) + Err(ReportedErrorInfo::allowed_in_infallible(handled).into()) } ValTreeCreationError::NonSupportedType(ty) => Ok(Err(ty)), } diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index c95e51f0a1f..ef3e96784ce 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -238,7 +238,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let scalar = src.to_scalar(); let ptr = scalar.to_pointer(self)?; match ptr.into_pointer_or_addr() { - Ok(ptr) => M::expose_ptr(self, ptr)?, + Ok(ptr) => M::expose_provenance(self, ptr.provenance)?, Err(_) => {} // Do nothing, exposing an invalid pointer (`None` provenance) is a NOP. }; interp_ok(ImmTy::from_scalar( diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 241be5e175c..95a72d3cbc1 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -268,7 +268,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }; // do not continue if typeck errors occurred (can only occur in local crate) if let Some(err) = body.tainted_by_errors { - throw_inval!(AlreadyReported(ReportedErrorInfo::from(err))); + throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error(err))); } interp_ok(body) } @@ -317,7 +317,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Ok(None) => throw_inval!(TooGeneric), // FIXME(eddyb) this could be a bit more specific than `AlreadyReported`. - Err(error_reported) => throw_inval!(AlreadyReported(error_reported.into())), + Err(error_guaranteed) => throw_inval!(AlreadyReported( + ReportedErrorInfo::non_const_eval_error(error_guaranteed) + )), } } diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index dbe09d55b2d..9ac2a024ccf 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -327,11 +327,11 @@ pub trait Machine<'tcx>: Sized { addr: u64, ) -> InterpResult<'tcx, Pointer<Option<Self::Provenance>>>; - /// Marks a pointer as exposed, allowing it's provenance + /// Marks a pointer as exposed, allowing its provenance /// to be recovered. "Pointer-to-int cast" - fn expose_ptr( - ecx: &mut InterpCx<'tcx, Self>, - ptr: Pointer<Self::Provenance>, + fn expose_provenance( + ecx: &InterpCx<'tcx, Self>, + provenance: Self::Provenance, ) -> InterpResult<'tcx>; /// Convert a pointer with provenance into an allocation-offset pair and extra provenance info. @@ -540,10 +540,14 @@ pub trait Machine<'tcx>: Sized { interp_ok(ReturnAction::Normal) } - /// Called immediately after an "immediate" local variable is read + /// Called immediately after an "immediate" local variable is read in a given frame /// (i.e., this is called for reads that do not end up accessing addressable memory). #[inline(always)] - fn after_local_read(_ecx: &InterpCx<'tcx, Self>, _local: mir::Local) -> InterpResult<'tcx> { + fn after_local_read( + _ecx: &InterpCx<'tcx, Self>, + _frame: &Frame<'tcx, Self::Provenance, Self::FrameExtra>, + _local: mir::Local, + ) -> InterpResult<'tcx> { interp_ok(()) } diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 277d293597a..027ba9644cb 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -944,6 +944,52 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { interp_ok(()) } + /// Handle the effect an FFI call might have on the state of allocations. + /// This overapproximates the modifications which external code might make to memory: + /// We set all reachable allocations as initialized, mark all provenances as exposed + /// and overwrite them with `Provenance::WILDCARD`. + pub fn prepare_for_native_call( + &mut self, + id: AllocId, + initial_prov: M::Provenance, + ) -> InterpResult<'tcx> { + // Expose provenance of the root allocation. + M::expose_provenance(self, initial_prov)?; + + let mut done = FxHashSet::default(); + let mut todo = vec![id]; + while let Some(id) = todo.pop() { + if !done.insert(id) { + // We already saw this allocation before, don't process it again. + continue; + } + let info = self.get_alloc_info(id); + + // If there is no data behind this pointer, skip this. + if !matches!(info.kind, AllocKind::LiveData) { + continue; + } + + // Expose all provenances in this allocation, and add them to `todo`. + let alloc = self.get_alloc_raw(id)?; + for prov in alloc.provenance().provenances() { + M::expose_provenance(self, prov)?; + if let Some(id) = prov.get_alloc_id() { + todo.push(id); + } + } + + // Prepare for possible write from native code if mutable. + if info.mutbl.is_mut() { + self.get_alloc_raw_mut(id)? + .0 + .prepare_for_native_write() + .map_err(|e| e.to_interp_error(id))?; + } + } + interp_ok(()) + } + /// Create a lazy debug printer that prints the given allocation and all allocations it points /// to, recursively. #[must_use] diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 0157e6c2125..b861ffb6110 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -15,9 +15,9 @@ use rustc_middle::{bug, mir, span_bug, ty}; use tracing::trace; use super::{ - CtfeProvenance, InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, OffsetMode, - PlaceTy, Pointer, Projectable, Provenance, Scalar, alloc_range, err_ub, from_known_layout, - interp_ok, mir_assign_valid_types, throw_ub, + CtfeProvenance, Frame, InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, + OffsetMode, PlaceTy, Pointer, Projectable, Provenance, Scalar, alloc_range, err_ub, + from_known_layout, interp_ok, mir_assign_valid_types, throw_ub, }; /// An `Immediate` represents a single immediate self-contained Rust value. @@ -297,6 +297,7 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { #[inline] pub fn from_bool(b: bool, tcx: TyCtxt<'tcx>) -> Self { + // Can use any typing env, since `bool` is always monomorphic. let layout = tcx .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(tcx.types.bool)) .unwrap(); @@ -305,17 +306,18 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { #[inline] pub fn from_ordering(c: std::cmp::Ordering, tcx: TyCtxt<'tcx>) -> Self { + // Can use any typing env, since `Ordering` is always monomorphic. let ty = tcx.ty_ordering_enum(None); let layout = tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty)).unwrap(); Self::from_scalar(Scalar::from_i8(c as i8), layout) } - pub fn from_pair(a: Self, b: Self, tcx: TyCtxt<'tcx>) -> Self { - let layout = tcx + pub fn from_pair(a: Self, b: Self, cx: &(impl HasTypingEnv<'tcx> + HasTyCtxt<'tcx>)) -> Self { + let layout = cx + .tcx() .layout_of( - ty::TypingEnv::fully_monomorphized() - .as_query_input(Ty::new_tup(tcx, &[a.layout.ty, b.layout.ty])), + cx.typing_env().as_query_input(Ty::new_tup(cx.tcx(), &[a.layout.ty, b.layout.ty])), ) .unwrap(); Self::from_scalar_pair(a.to_scalar(), b.to_scalar(), layout) @@ -706,23 +708,32 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { interp_ok(str) } - /// Read from a local of the current frame. + /// Read from a local of the current frame. Convenience method for [`InterpCx::local_at_frame_to_op`]. + pub fn local_to_op( + &self, + local: mir::Local, + layout: Option<TyAndLayout<'tcx>>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + self.local_at_frame_to_op(self.frame(), local, layout) + } + + /// Read from a local of a given frame. /// Will not access memory, instead an indirect `Operand` is returned. /// - /// This is public because it is used by [priroda](https://github.com/oli-obk/priroda) to get an - /// OpTy from a local. - pub fn local_to_op( + /// This is public because it is used by [Aquascope](https://github.com/cognitive-engineering-lab/aquascope/) + /// to get an OpTy from a local. + pub fn local_at_frame_to_op( &self, + frame: &Frame<'tcx, M::Provenance, M::FrameExtra>, local: mir::Local, layout: Option<TyAndLayout<'tcx>>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - let frame = self.frame(); let layout = self.layout_of_local(frame, local, layout)?; let op = *frame.locals[local].access()?; if matches!(op, Operand::Immediate(_)) { assert!(!layout.is_unsized()); } - M::after_local_read(self, local)?; + M::after_local_read(self, frame, local)?; interp_ok(OpTy { op, layout }) } diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index 1fa5dcbd24f..8b7b78c7129 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -222,7 +222,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let res = ImmTy::from_scalar_int(result, left.layout); return interp_ok(if with_overflow { let overflow = ImmTy::from_bool(overflow, *self.tcx); - ImmTy::from_pair(res, overflow, *self.tcx) + ImmTy::from_pair(res, overflow, self) } else { res }); @@ -279,7 +279,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let res = ImmTy::from_scalar_int(result, left.layout); if with_overflow { let overflow = ImmTy::from_bool(overflow, *self.tcx); - ImmTy::from_pair(res, overflow, *self.tcx) + ImmTy::from_pair(res, overflow, self) } else { res } diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 2beec544fad..810e9356b26 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -5,8 +5,7 @@ use std::assert_matches::assert_matches; use either::{Either, Left, Right}; -use rustc_abi::{Align, BackendRepr, HasDataLayout, Size}; -use rustc_ast::Mutability; +use rustc_abi::{BackendRepr, HasDataLayout, Size}; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::{bug, mir, span_bug}; @@ -1018,29 +1017,39 @@ where self.allocate_dyn(layout, kind, MemPlaceMeta::None) } - /// Returns a wide MPlace of type `str` to a new 1-aligned allocation. - /// Immutable strings are deduplicated and stored in global memory. - pub fn allocate_str( + /// Allocates a sequence of bytes in the interpreter's memory with alignment 1. + /// This is allocated in immutable global memory and deduplicated. + pub fn allocate_bytes_dedup( + &mut self, + bytes: &[u8], + ) -> InterpResult<'tcx, Pointer<M::Provenance>> { + let salt = M::get_global_alloc_salt(self, None); + let id = self.tcx.allocate_bytes_dedup(bytes, salt); + + // Turn untagged "global" pointers (obtained via `tcx`) into the machine pointer to the allocation. + M::adjust_alloc_root_pointer( + &self, + Pointer::from(id), + M::GLOBAL_KIND.map(MemoryKind::Machine), + ) + } + + /// Allocates a string in the interpreter's memory, returning it as a (wide) place. + /// This is allocated in immutable global memory and deduplicated. + pub fn allocate_str_dedup( &mut self, str: &str, - kind: MemoryKind<M::MemoryKind>, - mutbl: Mutability, ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { - let tcx = self.tcx.tcx; + let bytes = str.as_bytes(); + let ptr = self.allocate_bytes_dedup(bytes)?; - // Use cache for immutable strings. - let ptr = if mutbl.is_not() { - // Use dedup'd allocation function. - let salt = M::get_global_alloc_salt(self, None); - let id = tcx.allocate_bytes_dedup(str.as_bytes(), salt); + // Create length metadata for the string. + let meta = Scalar::from_target_usize(u64::try_from(bytes.len()).unwrap(), self); - // Turn untagged "global" pointers (obtained via `tcx`) into the machine pointer to the allocation. - M::adjust_alloc_root_pointer(&self, Pointer::from(id), Some(kind))? - } else { - self.allocate_bytes_ptr(str.as_bytes(), Align::ONE, kind, mutbl)? - }; - let meta = Scalar::from_target_usize(u64::try_from(str.len()).unwrap(), self); + // Get layout for Rust's str type. let layout = self.layout_of(self.tcx.types.str_).unwrap(); + + // Combine pointer and metadata into a wide pointer. interp_ok(self.ptr_with_meta_to_mplace( ptr.into(), MemPlaceMeta::Meta(meta), diff --git a/compiler/rustc_const_eval/src/interpret/stack.rs b/compiler/rustc_const_eval/src/interpret/stack.rs index a9ebf386617..6512675530a 100644 --- a/compiler/rustc_const_eval/src/interpret/stack.rs +++ b/compiler/rustc_const_eval/src/interpret/stack.rs @@ -584,8 +584,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { interp_ok(()) } + /// This is public because it is used by [Aquascope](https://github.com/cognitive-engineering-lab/aquascope/) + /// to analyze all the locals in a stack frame. #[inline(always)] - pub(super) fn layout_of_local( + pub fn layout_of_local( &self, frame: &Frame<'tcx, M::Provenance, M::FrameExtra>, local: mir::Local, diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index b61865be667..a26c2eca107 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -9,6 +9,7 @@ use rustc_middle::ty::layout::FnAbiOf; use rustc_middle::ty::{self, Instance, Ty}; use rustc_middle::{bug, mir, span_bug}; use rustc_span::source_map::Spanned; +use rustc_span::{DesugaringKind, Span}; use rustc_target::callconv::FnAbi; use tracing::{info, instrument, trace}; @@ -80,7 +81,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { use rustc_middle::mir::StatementKind::*; match &stmt.kind { - Assign(box (place, rvalue)) => self.eval_rvalue_into_place(rvalue, *place)?, + Assign(box (place, rvalue)) => { + self.eval_rvalue_into_place(rvalue, *place, stmt.source_info.span)? + } SetDiscriminant { place, variant_index } => { let dest = self.eval_place(**place)?; @@ -159,6 +162,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { &mut self, rvalue: &mir::Rvalue<'tcx>, place: mir::Place<'tcx>, + span: Span, ) -> InterpResult<'tcx> { let dest = self.eval_place(place)?; // FIXME: ensure some kind of non-aliasing between LHS and RHS? @@ -250,8 +254,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let src = self.eval_place(place)?; let place = self.force_allocation(&src)?; let mut val = ImmTy::from_immediate(place.to_ref(self), dest.layout); - if !place_base_raw { + if !place_base_raw + && span.desugaring_kind() != Some(DesugaringKind::IndexBoundsCheckReborrow) + { // If this was not already raw, it needs retagging. + // As a special hack, we exclude the desugared `PtrMetadata(&raw const *_n)` + // from indexing. (Really we should not do any retag on `&raw` but that does not + // currently work with Stacked Borrows.) val = M::retag_ptr_value(self, mir::RetagKind::Raw, &val)?; } self.write_immediate(*val, &dest)?; diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index 8bb5f173a56..ecb7c3fc93c 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -14,10 +14,8 @@ use crate::const_eval::{CompileTimeInterpCx, CompileTimeMachine, InterpretationR /// Checks whether a type contains generic parameters which must be instantiated. /// -/// In case it does, returns a `TooGeneric` const eval error. Note that due to polymorphization -/// types may be "concrete enough" even though they still contain generic parameters in -/// case these parameters are unused. -pub(crate) fn ensure_monomorphic_enough<'tcx, T>(tcx: TyCtxt<'tcx>, ty: T) -> InterpResult<'tcx> +/// In case it does, returns a `TooGeneric` const eval error. +pub(crate) fn ensure_monomorphic_enough<'tcx, T>(_tcx: TyCtxt<'tcx>, ty: T) -> InterpResult<'tcx> where T: TypeVisitable<TyCtxt<'tcx>>, { @@ -27,11 +25,9 @@ where } struct FoundParam; - struct UsedParamsNeedInstantiationVisitor<'tcx> { - tcx: TyCtxt<'tcx>, - } + struct UsedParamsNeedInstantiationVisitor {} - impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for UsedParamsNeedInstantiationVisitor<'tcx> { + impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for UsedParamsNeedInstantiationVisitor { type Result = ControlFlow<FoundParam>; fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { @@ -41,25 +37,7 @@ where match *ty.kind() { ty::Param(_) => ControlFlow::Break(FoundParam), - ty::Closure(def_id, args) - | ty::CoroutineClosure(def_id, args, ..) - | ty::Coroutine(def_id, args, ..) - | ty::FnDef(def_id, args) => { - let instance = ty::InstanceKind::Item(def_id); - let unused_params = self.tcx.unused_generic_params(instance); - for (index, arg) in args.into_iter().enumerate() { - let index = index - .try_into() - .expect("more generic parameters than can fit into a `u32`"); - // Only recurse when generic parameters in fns, closures and coroutines - // are used and have to be instantiated. - // - // Just in case there are closures or coroutines within this arg, - // recurse. - if unused_params.is_used(index) && arg.has_param() { - return arg.visit_with(self); - } - } + ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..) | ty::FnDef(..) => { ControlFlow::Continue(()) } _ => ty.super_visit_with(self), @@ -74,7 +52,7 @@ where } } - let mut vis = UsedParamsNeedInstantiationVisitor { tcx }; + let mut vis = UsedParamsNeedInstantiationVisitor {}; if matches!(ty.visit_with(&mut vis), ControlFlow::Break(FoundParam)) { throw_inval!(TooGeneric); } else { diff --git a/compiler/rustc_const_eval/src/util/caller_location.rs b/compiler/rustc_const_eval/src/util/caller_location.rs index 9bf16d4fe16..6593547cd23 100644 --- a/compiler/rustc_const_eval/src/util/caller_location.rs +++ b/compiler/rustc_const_eval/src/util/caller_location.rs @@ -1,7 +1,7 @@ use rustc_hir::LangItem; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::ty::{self, Mutability}; +use rustc_middle::ty::{self}; use rustc_middle::{bug, mir}; use rustc_span::symbol::Symbol; use tracing::trace; @@ -20,12 +20,9 @@ fn alloc_caller_location<'tcx>( // This can fail if rustc runs out of memory right here. Trying to emit an error would be // pointless, since that would require allocating more memory than these short strings. let file = if loc_details.file { - ecx.allocate_str(filename.as_str(), MemoryKind::CallerLocation, Mutability::Not).unwrap() + ecx.allocate_str_dedup(filename.as_str()).unwrap() } else { - // FIXME: This creates a new allocation each time. It might be preferable to - // perform this allocation only once, and re-use the `MPlaceTy`. - // See https://github.com/rust-lang/rust/pull/89920#discussion_r730012398 - ecx.allocate_str("<redacted>", MemoryKind::CallerLocation, Mutability::Not).unwrap() + ecx.allocate_str_dedup("<redacted>").unwrap() }; let file = file.map_provenance(CtfeProvenance::as_immutable); let line = if loc_details.line { Scalar::from_u32(line) } else { Scalar::from_u32(0) }; diff --git a/compiler/rustc_data_structures/src/jobserver.rs b/compiler/rustc_data_structures/src/jobserver.rs index d09f7efc8ff..1204f2d692d 100644 --- a/compiler/rustc_data_structures/src/jobserver.rs +++ b/compiler/rustc_data_structures/src/jobserver.rs @@ -1,6 +1,6 @@ use std::sync::{LazyLock, OnceLock}; -pub use jobserver_crate::Client; +pub use jobserver_crate::{Acquired, Client, HelperThread}; use jobserver_crate::{FromEnv, FromEnvErrorKind}; // We can only call `from_env_ext` once per process 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/args.rs b/compiler/rustc_driver_impl/src/args.rs index 28574457389..2fc767b3750 100644 --- a/compiler/rustc_driver_impl/src/args.rs +++ b/compiler/rustc_driver_impl/src/args.rs @@ -99,10 +99,7 @@ impl Expander { /// If this function is intended to be used with command line arguments, /// `argv[0]` must be removed prior to calling it manually. #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable -pub fn arg_expand_all( - early_dcx: &EarlyDiagCtxt, - at_args: &[String], -) -> Result<Vec<String>, ErrorGuaranteed> { +pub fn arg_expand_all(early_dcx: &EarlyDiagCtxt, at_args: &[String]) -> Vec<String> { let mut expander = Expander::default(); let mut result = Ok(()); for arg in at_args { @@ -110,7 +107,10 @@ pub fn arg_expand_all( result = Err(early_dcx.early_err(format!("failed to load argument file: {err}"))); } } - result.map(|()| expander.finish()) + if let Err(guar) = result { + guar.raise_fatal(); + } + expander.finish() } /// Gets the raw unprocessed command-line arguments as Unicode strings, without doing any further diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index b7d64f75bf3..ed49dfe1761 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -42,12 +42,10 @@ use rustc_data_structures::profiling::{ }; use rustc_errors::emitter::stderr_destination; use rustc_errors::registry::Registry; -use rustc_errors::{ - ColorConfig, DiagCtxt, ErrCode, ErrorGuaranteed, FatalError, PResult, markdown, -}; +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, Queries, 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; @@ -114,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, @@ -160,13 +158,10 @@ pub trait Callbacks { /// Called after parsing the crate root. Submodules are not yet parsed when /// this callback is called. Return value instructs the compiler whether to /// continue the compilation afterwards (defaults to `Compilation::Continue`) - #[deprecated = "This callback will likely be removed or stop giving access \ - to the TyCtxt in the future. Use either the after_expansion \ - or the after_analysis callback instead."] - fn after_crate_root_parsing<'tcx>( + fn after_crate_root_parsing( &mut self, _compiler: &interface::Compiler, - _queries: &'tcx Queries<'tcx>, + _queries: &ast::Crate, ) -> Compilation { Compilation::Continue } @@ -175,7 +170,7 @@ pub trait Callbacks { fn after_expansion<'tcx>( &mut self, _compiler: &interface::Compiler, - _queries: &'tcx Queries<'tcx>, + _tcx: TyCtxt<'tcx>, ) -> Compilation { Compilation::Continue } @@ -271,14 +266,14 @@ impl<'a> RunCompiler<'a> { } /// Parse args and run the compiler. - pub fn run(self) -> interface::Result<()> { + pub fn run(self) { run_compiler( self.at_args, self.callbacks, self.file_loader, self.make_codegen_backend, self.using_internal_features, - ) + ); } } @@ -290,7 +285,7 @@ fn run_compiler( Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>, >, using_internal_features: Arc<std::sync::atomic::AtomicBool>, -) -> interface::Result<()> { +) { let mut default_early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default()); // Throw away the first argument, the name of the binary. @@ -303,9 +298,11 @@ fn run_compiler( // the compiler with @empty_file as argv[0] and no more arguments. let at_args = at_args.get(1..).unwrap_or_default(); - let args = args::arg_expand_all(&default_early_dcx, at_args)?; + let args = args::arg_expand_all(&default_early_dcx, at_args); - let Some(matches) = handle_options(&default_early_dcx, &args) else { return Ok(()) }; + let Some(matches) = handle_options(&default_early_dcx, &args) else { + return; + }; let sopts = config::build_session_options(&mut default_early_dcx, &matches); // fully initialize ice path static once unstable options are available as context @@ -313,7 +310,7 @@ fn run_compiler( if let Some(ref code) = matches.opt_str("explain") { handle_explain(&default_early_dcx, diagnostics_registry(), code, sopts.color); - return Ok(()); + return; } let (odir, ofile) = make_output(&matches); @@ -338,7 +335,7 @@ fn run_compiler( expanded_args: args, }; - let has_input = match make_input(&default_early_dcx, &matches.free)? { + let has_input = match make_input(&default_early_dcx, &matches.free) { Some(input) => { config.input = input; true // has input: normal compilation @@ -350,6 +347,8 @@ fn run_compiler( callbacks.config(&mut config); + let registered_lints = config.register_lints.is_some(); + interface::run_compiler(config, |compiler| { let sess = &compiler.sess; let codegen_backend = &*compiler.codegen_backend; @@ -358,14 +357,14 @@ fn run_compiler( // printing some information without compiling, or exiting immediately // after parsing, etc. let early_exit = || { - if let Some(guar) = sess.dcx().has_errors() { Err(guar) } else { Ok(()) } + sess.dcx().abort_if_errors(); }; // This implements `-Whelp`. It should be handled very early, like // `--help`/`-Zhelp`/`-Chelp`. This is the earliest it can run, because // it must happen after lints are registered, during session creation. if sess.opts.describe_lints { - describe_lints(sess); + describe_lints(sess, registered_lints); return early_exit(); } @@ -388,82 +387,76 @@ fn run_compiler( return early_exit(); } - let linker = compiler.enter(|queries| { - let early_exit = || early_exit().map(|_| 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"); - return early_exit(); + // 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(); + } - #[allow(deprecated)] - if callbacks.after_crate_root_parsing(compiler, queries) == Compilation::Stop { - 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(); - } + 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 + }; // Make sure name resolution and macro expansion is run. - queries.global_ctxt()?.enter(|tcx| tcx.resolver_for_lowering()); + let _ = tcx.resolver_for_lowering(); if let Some(metrics_dir) = &sess.opts.unstable_opts.metrics_dir { - queries.global_ctxt()?.enter(|tcxt| dump_feature_usage_metrics(tcxt, metrics_dir)); + dump_feature_usage_metrics(tcx, metrics_dir); } - if callbacks.after_expansion(compiler, queries) == Compilation::Stop { + if callbacks.after_expansion(compiler, tcx) == Compilation::Stop { return early_exit(); } - queries.global_ctxt()?.enter(|tcx| { - passes::write_dep_info(tcx); + 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.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(); - } + if sess.opts.unstable_opts.no_analysis { + return early_exit(); + } - tcx.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(); + } - Ok(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 // `GlobalCtxt` within `Queries` can be freed as early as possible. if let Some(linker) = linker { - let _timer = sess.timer("link"); - linker.link(sess, codegen_backend)? + linker.link(sess, codegen_backend); } - - Ok(()) }) } @@ -496,21 +489,17 @@ fn make_output(matches: &getopts::Matches) -> (Option<PathBuf>, Option<OutFileNa /// Extract input (string or file and optional path) from matches. /// This handles reading from stdin if `-` is provided. -fn make_input( - early_dcx: &EarlyDiagCtxt, - free_matches: &[String], -) -> Result<Option<Input>, ErrorGuaranteed> { +fn make_input(early_dcx: &EarlyDiagCtxt, free_matches: &[String]) -> Option<Input> { match free_matches { - [] => Ok(None), // no input: we will exit early, + [] => None, // no input: we will exit early, [ifile] if ifile == "-" => { // read from stdin as `Input::Str` let mut input = String::new(); if io::stdin().read_to_string(&mut input).is_err() { // Immediately stop compilation if there was an issue reading // the input (for example if the input stream is not UTF-8). - let reported = early_dcx - .early_err("couldn't read from stdin, as it did not contain valid UTF-8"); - return Err(reported); + early_dcx + .early_fatal("couldn't read from stdin, as it did not contain valid UTF-8"); } let name = match env::var("UNSTABLE_RUSTDOC_TEST_PATH") { @@ -526,9 +515,9 @@ fn make_input( Err(_) => FileName::anon_source_code(&input), }; - Ok(Some(Input::Str { name, input })) + Some(Input::Str { name, input }) } - [ifile] => Ok(Some(Input::File(PathBuf::from(ifile)))), + [ifile] => Some(Input::File(PathBuf::from(ifile))), [ifile1, ifile2, ..] => early_dcx.early_fatal(format!( "multiple input filenames provided (first two filenames are `{}` and `{}`)", ifile1, ifile2 @@ -663,9 +652,7 @@ fn process_rlink(sess: &Session, compiler: &interface::Compiler) { }; } }; - if compiler.codegen_backend.link(sess, codegen_results, &outputs).is_err() { - FatalError.raise(); - } + compiler.codegen_backend.link(sess, codegen_results, &outputs); } else { dcx.emit_fatal(RlinkNotAFile {}); } @@ -988,7 +975,7 @@ the command line flag directly. } /// Write to stdout lint command options, together with a list of all available lints -pub fn describe_lints(sess: &Session) { +pub fn describe_lints(sess: &Session, registered_lints: bool) { safe_println!( " Available lint options: @@ -1092,7 +1079,7 @@ Available lint options: print_lint_groups(builtin_groups, true); - match (sess.registered_lints, loaded.len(), loaded_groups.len()) { + match (registered_lints, loaded.len(), loaded_groups.len()) { (false, 0, _) | (false, _, 0) => { safe_println!("Lint tools like Clippy can load additional lints and lint groups."); } @@ -1608,7 +1595,8 @@ pub fn main() -> ! { let exit_code = catch_with_exit_code(|| { RunCompiler::new(&args::raw_args(&early_dcx)?, &mut callbacks) .set_using_internal_features(using_internal_features) - .run() + .run(); + Ok(()) }); if let Some(format) = callbacks.time_passes { diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs index 0733b8c0b98..5df960be307 100644 --- a/compiler/rustc_driver_impl/src/pretty.rs +++ b/compiler/rustc_driver_impl/src/pretty.rs @@ -222,8 +222,8 @@ impl<'tcx> PrintExtra<'tcx> { } pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) { - if ppm.needs_analysis() && ex.tcx().analysis(()).is_err() { - FatalError.raise(); + if ppm.needs_analysis() { + ex.tcx().ensure().analysis(()); } let (src, src_name) = get_source(sess); diff --git a/compiler/rustc_error_codes/src/error_codes/E0307.md b/compiler/rustc_error_codes/src/error_codes/E0307.md index 0d29d56ea1a..b9c0493e8d6 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0307.md +++ b/compiler/rustc_error_codes/src/error_codes/E0307.md @@ -65,8 +65,10 @@ impl Trait for Foo { ``` The nightly feature [Arbitrary self types][AST] extends the accepted -set of receiver types to also include any type that can dereference to -`Self`: +set of receiver types to also include any type that implements the +`Receiver` trait and can follow its chain of `Target` types to `Self`. +There's a blanket implementation of `Receiver` for `T: Deref`, so any +type which dereferences to `Self` can be used. ``` #![feature(arbitrary_self_types)] diff --git a/compiler/rustc_error_codes/src/error_codes/E0708.md b/compiler/rustc_error_codes/src/error_codes/E0708.md index 61a853ac446..f793470bafd 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0708.md +++ b/compiler/rustc_error_codes/src/error_codes/E0708.md @@ -5,8 +5,6 @@ Erroneous code example: ```edition2018 -#![feature(async_closure)] - fn main() { let add_one = async |num: u8| { num + 1 @@ -18,8 +16,6 @@ fn main() { version, you can use successfully by using move: ```edition2018 -#![feature(async_closure)] - fn main() { let add_one = async move |num: u8| { // ok! num + 1 diff --git a/compiler/rustc_error_codes/src/error_codes/E0751.md b/compiler/rustc_error_codes/src/error_codes/E0751.md index 8794f7868f3..825809b229a 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0751.md +++ b/compiler/rustc_error_codes/src/error_codes/E0751.md @@ -9,4 +9,4 @@ impl !MyTrait for i32 { } // error! ``` Negative implementations are a promise that the trait will never be implemented -for the given types. Therefore, both cannot exists at the same time. +for the given types. Therefore, both cannot exist at the same time. diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index b4a651b10b1..b337e279400 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -12,6 +12,7 @@ use rustc_span::SourceFile; use rustc_span::source_map::SourceMap; use crate::emitter::FileWithAnnotatedLines; +use crate::registry::Registry; use crate::snippet::Line; use crate::translation::{Translate, to_fluent_args}; use crate::{ @@ -45,7 +46,7 @@ impl Translate for AnnotateSnippetEmitter { impl Emitter for AnnotateSnippetEmitter { /// The entry point for the diagnostics generation - fn emit_diagnostic(&mut self, mut diag: DiagInner) { + fn emit_diagnostic(&mut self, mut diag: DiagInner, _registry: &Registry) { let fluent_args = to_fluent_args(diag.args.iter()); let mut suggestions = diag.suggestions.unwrap_tag(); diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index a386129e814..ac2f91cdeb3 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -27,6 +27,7 @@ use termcolor::{Buffer, BufferWriter, Color, ColorChoice, ColorSpec, StandardStr use tracing::{debug, instrument, trace, warn}; use crate::diagnostic::DiagLocation; +use crate::registry::Registry; use crate::snippet::{ Annotation, AnnotationColumn, AnnotationType, Line, MultilineAnnotation, Style, StyledString, }; @@ -181,7 +182,7 @@ pub type DynEmitter = dyn Emitter + DynSend; /// Emitter trait for emitting errors. pub trait Emitter: Translate { /// Emit a structured diagnostic. - fn emit_diagnostic(&mut self, diag: DiagInner); + fn emit_diagnostic(&mut self, diag: DiagInner, registry: &Registry); /// Emit a notification that an artifact has been output. /// Currently only supported for the JSON format. @@ -189,7 +190,7 @@ pub trait Emitter: Translate { /// Emit a report about future breakage. /// Currently only supported for the JSON format. - fn emit_future_breakage_report(&mut self, _diags: Vec<DiagInner>) {} + fn emit_future_breakage_report(&mut self, _diags: Vec<DiagInner>, _registry: &Registry) {} /// Emit list of unused externs. /// Currently only supported for the JSON format. @@ -500,7 +501,7 @@ impl Emitter for HumanEmitter { self.sm.as_deref() } - fn emit_diagnostic(&mut self, mut diag: DiagInner) { + fn emit_diagnostic(&mut self, mut diag: DiagInner, _registry: &Registry) { let fluent_args = to_fluent_args(diag.args.iter()); let mut suggestions = diag.suggestions.unwrap_tag(); @@ -561,7 +562,7 @@ impl Emitter for SilentEmitter { None } - fn emit_diagnostic(&mut self, mut diag: DiagInner) { + fn emit_diagnostic(&mut self, mut diag: DiagInner, _registry: &Registry) { if self.emit_fatal_diagnostic && diag.level == Level::Fatal { if let Some(fatal_note) = &self.fatal_note { diag.sub(Level::Note, fatal_note.clone(), MultiSpan::new()); @@ -3047,11 +3048,19 @@ impl FileWithAnnotatedLines { // working correctly. let middle = min(ann.line_start + 4, ann.line_end); // We'll show up to 4 lines past the beginning of the multispan start. - // We will *not* include the tail of lines that are only whitespace. + // We will *not* include the tail of lines that are only whitespace, a comment or + // a bare delimiter. + let filter = |s: &str| { + let s = s.trim(); + // Consider comments as empty, but don't consider docstrings to be empty. + !(s.starts_with("//") && !(s.starts_with("///") || s.starts_with("//!"))) + // Consider lines with nothing but whitespace, a single delimiter as empty. + && !["", "{", "}", "(", ")", "[", "]"].contains(&s) + }; let until = (ann.line_start..middle) .rev() .filter_map(|line| file.get_line(line - 1).map(|s| (line + 1, s))) - .find(|(_, s)| !s.trim().is_empty()) + .find(|(_, s)| filter(s)) .map(|(line, _)| line) .unwrap_or(ann.line_start); for line in ann.line_start + 1..until { @@ -3059,7 +3068,8 @@ impl FileWithAnnotatedLines { add_annotation_to_file(&mut output, Lrc::clone(&file), line, ann.as_line()); } let line_end = ann.line_end - 1; - if middle < line_end { + let end_is_empty = file.get_line(line_end - 1).map_or(false, |s| !filter(&s)); + if middle < line_end && !end_is_empty { add_annotation_to_file(&mut output, Lrc::clone(&file), line_end, ann.as_line()); } } else { diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index e3b6dcea892..97df7f9265a 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -44,7 +44,6 @@ mod tests; pub struct JsonEmitter { #[setters(skip)] dst: IntoDynSyncSend<Box<dyn Write + Send>>, - registry: Option<Registry>, #[setters(skip)] sm: Lrc<SourceMap>, fluent_bundle: Option<Lrc<FluentBundle>>, @@ -74,7 +73,6 @@ impl JsonEmitter { ) -> JsonEmitter { JsonEmitter { dst: IntoDynSyncSend(dst), - registry: None, sm, fluent_bundle: None, fallback_bundle, @@ -121,8 +119,8 @@ impl Translate for JsonEmitter { } impl Emitter for JsonEmitter { - fn emit_diagnostic(&mut self, diag: crate::DiagInner) { - let data = Diagnostic::from_errors_diagnostic(diag, self); + fn emit_diagnostic(&mut self, diag: crate::DiagInner, registry: &Registry) { + let data = Diagnostic::from_errors_diagnostic(diag, self, registry); let result = self.emit(EmitTyped::Diagnostic(data)); if let Err(e) = result { panic!("failed to print diagnostics: {e:?}"); @@ -137,7 +135,7 @@ impl Emitter for JsonEmitter { } } - fn emit_future_breakage_report(&mut self, diags: Vec<crate::DiagInner>) { + fn emit_future_breakage_report(&mut self, diags: Vec<crate::DiagInner>, registry: &Registry) { let data: Vec<FutureBreakageItem<'_>> = diags .into_iter() .map(|mut diag| { @@ -151,7 +149,7 @@ impl Emitter for JsonEmitter { } FutureBreakageItem { diagnostic: EmitTyped::Diagnostic(Diagnostic::from_errors_diagnostic( - diag, self, + diag, self, registry, )), } }) @@ -291,7 +289,11 @@ struct UnusedExterns<'a> { impl Diagnostic { /// Converts from `rustc_errors::DiagInner` to `Diagnostic`. - fn from_errors_diagnostic(diag: crate::DiagInner, je: &JsonEmitter) -> Diagnostic { + fn from_errors_diagnostic( + diag: crate::DiagInner, + je: &JsonEmitter, + registry: &Registry, + ) -> Diagnostic { let args = to_fluent_args(diag.args.iter()); let sugg_to_diag = |sugg: &CodeSuggestion| { let translated_message = @@ -344,7 +346,7 @@ impl Diagnostic { let code = if let Some(code) = diag.code { Some(DiagnosticCode { code: code.to_string(), - explanation: je.registry.as_ref().unwrap().try_find_description(code).ok(), + explanation: registry.try_find_description(code).ok(), }) } else if let Some(IsLint { name, .. }) = &diag.is_lint { Some(DiagnosticCode { code: name.to_string(), explanation: None }) @@ -382,7 +384,7 @@ impl Diagnostic { } else { OutputTheme::Ascii }) - .emit_diagnostic(diag); + .emit_diagnostic(diag, registry); let buf = Arc::try_unwrap(buf.0).unwrap().into_inner().unwrap(); let buf = String::from_utf8(buf).unwrap(); diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 98200c367f9..58fe3ec4b85 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -55,7 +55,6 @@ pub use diagnostic_impls::{ }; pub use emitter::ColorConfig; use emitter::{DynEmitter, Emitter, is_case_difference, is_different}; -use registry::Registry; use rustc_data_structures::AtomicRef; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::stable_hasher::{Hash128, StableHasher}; @@ -77,6 +76,8 @@ pub use snippet::Style; pub use termcolor::{Color, ColorSpec, WriteColor}; use tracing::debug; +use crate::registry::Registry; + pub mod annotate_snippet_emitter_writer; pub mod codes; mod diagnostic; @@ -93,8 +94,7 @@ mod styled_buffer; mod tests; pub mod translation; -pub type PErr<'a> = Diag<'a>; -pub type PResult<'a, T> = Result<T, PErr<'a>>; +pub type PResult<'a, T> = Result<T, Diag<'a>>; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } @@ -483,6 +483,8 @@ impl<'a> std::ops::Deref for DiagCtxtHandle<'a> { struct DiagCtxtInner { flags: DiagCtxtFlags, + registry: Registry, + /// The error guarantees from all emitted errors. The length gives the error count. err_guars: Vec<ErrorGuaranteed>, /// The error guarantee from all emitted lint errors. The length gives the @@ -573,6 +575,10 @@ pub enum StashKey { UndeterminedMacroResolution, /// Used by `Parser::maybe_recover_trailing_expr` ExprInPat, + /// If in the parser we detect a field expr with turbofish generic params it's possible that + /// it's a method call without parens. If later on in `hir_typeck` we find out that this is + /// the case we suppress this message and we give a better suggestion. + GenericInFieldExpr, } fn default_track_diagnostic<R>(diag: DiagInner, f: &mut dyn FnMut(DiagInner) -> R) -> R { @@ -619,9 +625,7 @@ impl Drop for DiagCtxtInner { // Important: it is sound to produce an `ErrorGuaranteed` when emitting // delayed bugs because they are guaranteed to be emitted here if // necessary. - if self.err_guars.is_empty() { - self.flush_delayed() - } + self.flush_delayed(); // Sanity check: did we use some of the expensive `trimmed_def_paths` functions // unexpectedly, that is, without producing diagnostics? If so, for debugging purposes, we @@ -664,6 +668,11 @@ impl DiagCtxt { self } + pub fn with_registry(mut self, registry: Registry) -> Self { + self.inner.get_mut().registry = registry; + self + } + pub fn new(emitter: Box<DynEmitter>) -> Self { Self { inner: Lock::new(DiagCtxtInner::new(emitter)) } } @@ -694,7 +703,7 @@ impl DiagCtxt { struct FalseEmitter; impl Emitter for FalseEmitter { - fn emit_diagnostic(&mut self, _: DiagInner) { + fn emit_diagnostic(&mut self, _: DiagInner, _: &Registry) { unimplemented!("false emitter must only used during `wrap_emitter`") } @@ -759,6 +768,7 @@ impl DiagCtxt { let mut inner = self.inner.borrow_mut(); let DiagCtxtInner { flags: _, + registry: _, err_guars, lint_err_guars, delayed_bugs, @@ -964,7 +974,7 @@ impl<'a> DiagCtxtHandle<'a> { self.inner.borrow().has_errors_or_delayed_bugs() } - pub fn print_error_count(&self, registry: &Registry) { + pub fn print_error_count(&self) { let mut inner = self.inner.borrow_mut(); // Any stashed diagnostics should have been handled by @@ -1014,7 +1024,7 @@ impl<'a> DiagCtxtHandle<'a> { .emitted_diagnostic_codes .iter() .filter_map(|&code| { - if registry.try_find_description(code).is_ok() { + if inner.registry.try_find_description(code).is_ok() { Some(code.to_string()) } else { None @@ -1075,10 +1085,10 @@ impl<'a> DiagCtxtHandle<'a> { } pub fn emit_future_breakage_report(&self) { - let mut inner = self.inner.borrow_mut(); + let inner = &mut *self.inner.borrow_mut(); let diags = std::mem::take(&mut inner.future_breakage_diagnostics); if !diags.is_empty() { - inner.emitter.emit_future_breakage_report(diags); + inner.emitter.emit_future_breakage_report(diags, &inner.registry); } } @@ -1284,7 +1294,7 @@ impl<'a> DiagCtxtHandle<'a> { Diag::<ErrorGuaranteed>::new(self, DelayedBug, msg.into()).emit() } - /// Ensures that an error is printed. See `Level::DelayedBug`. + /// Ensures that an error is printed. See [`Level::DelayedBug`]. /// /// Note: this function used to be called `delay_span_bug`. It was renamed /// to match similar functions like `span_err`, `span_warn`, etc. @@ -1409,6 +1419,7 @@ impl DiagCtxtInner { fn new(emitter: Box<DynEmitter>) -> Self { Self { flags: DiagCtxtFlags { can_emit_warnings: true, ..Default::default() }, + registry: Registry::new(&[]), err_guars: Vec::new(), lint_err_guars: Vec::new(), delayed_bugs: Vec::new(), @@ -1558,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()); @@ -1582,7 +1593,7 @@ impl DiagCtxtInner { } self.has_printed = true; - self.emitter.emit_diagnostic(diagnostic); + self.emitter.emit_diagnostic(diagnostic, &self.registry); } if is_error { @@ -1695,7 +1706,13 @@ impl DiagCtxtInner { // eventually happened. assert!(self.stashed_diagnostics.is_empty()); + if !self.err_guars.is_empty() { + // If an error happened already. We shouldn't expose delayed bugs. + return; + } + if self.delayed_bugs.is_empty() { + // Nothing to do. return; } 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..82d4847e27a 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}; @@ -782,10 +782,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 +807,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 +818,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 +844,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 +1307,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 +1315,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/config.rs b/compiler/rustc_expand/src/config.rs index dc6aa110f45..8e500f538a7 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, @@ -362,7 +362,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/expand.rs b/compiler/rustc_expand/src/expand.rs index e5500c8bba1..575108a548f 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -723,7 +723,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { item_inner.kind, ItemKind::Mod( _, - ModKind::Unloaded | ModKind::Loaded(_, Inline::No, _), + ModKind::Unloaded | ModKind::Loaded(_, Inline::No, _, _), ) ) => { @@ -889,7 +889,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { fn visit_item(&mut self, item: &'ast ast::Item) { match &item.kind { ItemKind::Mod(_, mod_kind) - if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _)) => + if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _, _)) => { feature_err( self.sess, @@ -990,7 +990,7 @@ pub fn parse_ast_fragment<'a>( } } AstFragmentKind::Ty => AstFragment::Ty(this.parse_ty()?), - AstFragmentKind::Pat => AstFragment::Pat(this.parse_pat_allow_top_alt( + AstFragmentKind::Pat => AstFragment::Pat(this.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::Yes, @@ -1195,7 +1195,7 @@ impl InvocationCollectorNode for P<ast::Item> { let ecx = &mut collector.cx; let (file_path, dir_path, dir_ownership) = match mod_kind { - ModKind::Loaded(_, inline, _) => { + ModKind::Loaded(_, inline, _, _) => { // Inline `mod foo { ... }`, but we still need to push directories. let (dir_path, dir_ownership) = mod_dir_path( ecx.sess, @@ -1217,15 +1217,21 @@ impl InvocationCollectorNode for P<ast::Item> { ModKind::Unloaded => { // We have an outline `mod foo;` so we need to parse the file. let old_attrs_len = attrs.len(); - let ParsedExternalMod { items, spans, file_path, dir_path, dir_ownership } = - parse_external_mod( - ecx.sess, - ident, - span, - &ecx.current_expansion.module, - ecx.current_expansion.dir_ownership, - &mut attrs, - ); + let ParsedExternalMod { + items, + spans, + file_path, + dir_path, + dir_ownership, + had_parse_error, + } = parse_external_mod( + ecx.sess, + ident, + span, + &ecx.current_expansion.module, + ecx.current_expansion.dir_ownership, + &mut attrs, + ); if let Some(lint_store) = ecx.lint_store { lint_store.pre_expansion_lint( @@ -1239,7 +1245,7 @@ impl InvocationCollectorNode for P<ast::Item> { ); } - *mod_kind = ModKind::Loaded(items, Inline::No, spans); + *mod_kind = ModKind::Loaded(items, Inline::No, spans, had_parse_error); node.attrs = attrs; if node.attrs.len() > old_attrs_len { // If we loaded an out-of-line module and added some inner attributes, @@ -1907,7 +1913,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/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index a373c753cc1..ae8e24007bd 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; @@ -371,7 +372,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/module.rs b/compiler/rustc_expand/src/module.rs index 614f52bbd28..85ea42e78ad 100644 --- a/compiler/rustc_expand/src/module.rs +++ b/compiler/rustc_expand/src/module.rs @@ -37,6 +37,7 @@ pub(crate) struct ParsedExternalMod { pub file_path: PathBuf, pub dir_path: PathBuf, pub dir_ownership: DirOwnership, + pub had_parse_error: Result<(), ErrorGuaranteed>, } pub enum ModError<'a> { @@ -74,14 +75,17 @@ pub(crate) fn parse_external_mod( attrs.extend(inner_attrs); (items, inner_span, mp.file_path) }; + // (1) ...instead, we return a dummy module. - let (items, spans, file_path) = - result.map_err(|err| err.report(sess, span)).unwrap_or_default(); + let ((items, spans, file_path), had_parse_error) = match result { + Err(err) => (Default::default(), Err(err.report(sess, span))), + Ok(result) => (result, Ok(())), + }; // Extract the directory path for submodules of the module. let dir_path = file_path.parent().unwrap_or(&file_path).to_owned(); - ParsedExternalMod { items, spans, file_path, dir_path, dir_ownership } + ParsedExternalMod { items, spans, file_path, dir_path, dir_ownership, had_parse_error } } pub(crate) fn mod_dir_path( diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index f044d964f13..9e459bd81a1 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -174,6 +174,7 @@ pub(crate) fn placeholder( vis, is_placeholder: true, safety: Safety::Default, + default: None, }]), AstFragmentKind::Variants => AstFragment::Variants(smallvec![ast::Variant { attrs: Default::default(), diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 53362cb2529..26ae541d08b 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -72,6 +72,8 @@ declare_features! ( (accepted, associated_types, "1.0.0", None), /// Allows free and inherent `async fn`s, `async` blocks, and `<expr>.await` expressions. (accepted, async_await, "1.39.0", Some(50547)), + /// Allows `async || body` closures. + (accepted, async_closure, "CURRENT_RUSTC_VERSION", Some(62290)), /// Allows async functions to be declared, implemented, and used in traits. (accepted, async_fn_in_trait, "1.75.0", Some(91611)), /// Allows all literals in attribute lists and values of key-value pairs. @@ -155,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`. diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index fd08b35d242..bf221158d57 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -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!( @@ -1104,10 +1103,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ WarnFollowing, EncodeCrossCrate::No ), rustc_attr!( - TEST, rustc_polymorphize_error, Normal, template!(Word), - WarnFollowing, EncodeCrossCrate::Yes - ), - rustc_attr!( TEST, rustc_def_path, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No ), diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index 8b4f441dafe..e25840ba5fc 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -126,9 +126,6 @@ declare_features! ( better implied higher-ranked implied bounds support" ) ), - /// Allows `impl Trait` in bindings (`let`, `const`, `static`). - (removed, impl_trait_in_bindings, "1.55.0", Some(63065), - Some("the implementation was not maintainable, the feature may get reintroduced once the current refactorings are done")), (removed, import_shadowing, "1.0.0", None, None), /// Allows in-band quantification of lifetime bindings (e.g., `fn foo(x: &'a u8) -> &'a u8`). (removed, in_band_lifetimes, "1.23.0", Some(44524), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index abc7200699c..e07ddae06d5 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -332,6 +332,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)), @@ -342,6 +343,7 @@ declare_features! ( (unstable, sse4a_target_feature, "1.27.0", Some(44839)), (unstable, tbm_target_feature, "1.27.0", Some(44839)), (unstable, wasm_target_feature, "1.30.0", Some(44839)), + (unstable, x87_target_feature, "CURRENT_RUSTC_VERSION", Some(44839)), // !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! // Features are listed in alphabetical order. Tidy will fail if you don't keep it this way. // !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! @@ -388,8 +390,8 @@ declare_features! ( (unstable, associated_const_equality, "1.58.0", Some(92827)), /// Allows associated type defaults. (unstable, associated_type_defaults, "1.2.0", Some(29661)), - /// Allows `async || body` closures. - (unstable, async_closure, "1.37.0", Some(62290)), + /// Allows async functions to be called from `dyn Trait`. + (incomplete, async_fn_in_dyn_trait, "CURRENT_RUSTC_VERSION", Some(133119)), /// Allows `#[track_caller]` on async functions. (unstable, async_fn_track_caller, "1.73.0", Some(110011)), /// Allows `for await` loops. @@ -446,15 +448,15 @@ 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]`. (unstable, custom_test_frameworks, "1.30.0", Some(50297)), /// Allows declarative macros 2.0 (`macro`). (unstable, decl_macro, "1.17.0", Some(39412)), + /// Allows the use of default values on struct definitions and the construction of struct + /// literals with the functional update syntax without a base. + (unstable, default_field_values, "CURRENT_RUSTC_VERSION", Some(132162)), /// Allows using `#[deprecated_safe]` to deprecate the safeness of a function or trait (unstable, deprecated_safe, "1.61.0", Some(94978)), /// Allows having using `suggestion` in the `#[deprecated]` attribute. @@ -505,12 +507,16 @@ declare_features! ( (incomplete, generic_const_items, "1.73.0", Some(113521)), /// Allows registering static items globally, possibly across crates, to iterate over at runtime. (unstable, global_registration, "1.80.0", Some(125119)), + /// Allows using guards in patterns. + (incomplete, guard_patterns, "CURRENT_RUSTC_VERSION", Some(129967)), /// Allows using `..=X` as a patterns in slices. (unstable, half_open_range_patterns_in_slices, "1.66.0", Some(67264)), /// Allows `if let` guard in match arms. (unstable, if_let_guard, "1.47.0", Some(51114)), /// Allows `impl Trait` to be used inside associated types (RFC 2515). (unstable, impl_trait_in_assoc_type, "1.70.0", Some(63063)), + /// Allows `impl Trait` in bindings (`let`). + (unstable, impl_trait_in_bindings, "1.64.0", Some(63065)), /// Allows `impl Trait` as output type in `Fn` traits in return position of functions. (unstable, impl_trait_in_fn_trait_return, "1.64.0", Some(99697)), /// Allows associated types in inherent impls. @@ -630,6 +636,8 @@ declare_features! ( /// Allows creation of instances of a struct by moving fields that have /// not changed from prior instances of the same struct (RFC #2528) (unstable, type_changing_struct_update, "1.58.0", Some(86555)), + /// Allows using `unsafe<'a> &'a T` unsafe binder types. + (incomplete, unsafe_binders, "CURRENT_RUSTC_VERSION", Some(130516)), /// Allows declaring fields `unsafe`. (incomplete, unsafe_fields, "CURRENT_RUSTC_VERSION", Some(132922)), /// Allows const generic parameters to be defined with types that 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/def.rs b/compiler/rustc_hir/src/def.rs index 0b7ffc4af45..705de389e07 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -9,7 +9,6 @@ use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::Symbol; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::hygiene::MacroKind; -use rustc_span::symbol::kw; use crate::definitions::DefPathData; use crate::hir; @@ -256,7 +255,6 @@ impl DefKind { pub fn def_path_data(self, name: Symbol) -> DefPathData { match self { - DefKind::Struct | DefKind::Union if name == kw::Empty => DefPathData::AnonAdt, DefKind::Mod | DefKind::Struct | DefKind::Union diff --git a/compiler/rustc_hir/src/definitions.rs b/compiler/rustc_hir/src/definitions.rs index 9873a58cfe9..d85f9586d42 100644 --- a/compiler/rustc_hir/src/definitions.rs +++ b/compiler/rustc_hir/src/definitions.rs @@ -289,8 +289,6 @@ pub enum DefPathData { /// An existential `impl Trait` type node. /// Argument position `impl Trait` have a `TypeNs` with their pretty-printed name. OpaqueTy, - /// An anonymous struct or union type i.e. `struct { foo: Type }` or `union { bar: Type }` - AnonAdt, } impl Definitions { @@ -415,7 +413,7 @@ impl DefPathData { TypeNs(name) | ValueNs(name) | MacroNs(name) | LifetimeNs(name) => Some(name), Impl | ForeignMod | CrateRoot | Use | GlobalAsm | Closure | Ctor | AnonConst - | OpaqueTy | AnonAdt => None, + | OpaqueTy => None, } } @@ -438,7 +436,6 @@ impl DefPathData { Ctor => DefPathDataName::Anon { namespace: sym::constructor }, AnonConst => DefPathDataName::Anon { namespace: sym::constant }, OpaqueTy => DefPathDataName::Anon { namespace: sym::opaque }, - AnonAdt => DefPathDataName::Anon { namespace: sym::anon_adt }, } } } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index a9696627f11..56dba0c61e2 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1,14 +1,17 @@ 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, - ImplPolarity, IsAuto, Movability, Mutability, UnOp, + ImplPolarity, IsAuto, Movability, Mutability, UnOp, UnsafeBinderCastKind, }; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::sorted_map::SortedMap; @@ -21,6 +24,7 @@ use rustc_span::symbol::{Ident, Symbol, kw, sym}; use rustc_span::{BytePos, DUMMY_SP, ErrorGuaranteed, Span}; use rustc_target::asm::InlineAsmRegOrRegClass; use smallvec::SmallVec; +use thin_vec::ThinVec; use tracing::debug; use crate::LangItem; @@ -937,6 +941,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> { @@ -1740,6 +1988,7 @@ impl Expr<'_> { | ExprKind::Struct(..) | ExprKind::Tup(_) | ExprKind::Type(..) + | ExprKind::UnsafeBinderCast(..) | ExprKind::Err(_) => ExprPrecedence::Unambiguous, ExprKind::DropTemps(ref expr, ..) => expr.precedence(), @@ -1769,6 +2018,9 @@ impl Expr<'_> { // https://github.com/rust-lang/rfcs/blob/master/text/0803-type-ascription.md#type-ascription-and-temporaries ExprKind::Type(ref e, _) => e.is_place_expr(allow_projections_from), + // Unsafe binder cast preserves place-ness of the sub-expression. + ExprKind::UnsafeBinderCast(_, e, _) => e.is_place_expr(allow_projections_from), + ExprKind::Unary(UnOp::Deref, _) => true, ExprKind::Field(ref base, _) | ExprKind::Index(ref base, _, _) => { @@ -1850,14 +2102,20 @@ impl Expr<'_> { | ExprKind::Field(base, _) | ExprKind::Index(base, _, _) | ExprKind::AddrOf(.., base) - | ExprKind::Cast(base, _) => { + | ExprKind::Cast(base, _) + | ExprKind::UnsafeBinderCast(_, base, _) => { // This isn't exactly true for `Index` and all `Unary`, but we are using this // method exclusively for diagnostics and there's a *cultural* pressure against // them being used only for its side-effects. base.can_have_side_effects() } ExprKind::Struct(_, fields, init) => { - fields.iter().map(|field| field.expr).chain(init).any(|e| e.can_have_side_effects()) + let init_side_effects = match init { + StructTailExpr::Base(init) => init.can_have_side_effects(), + StructTailExpr::DefaultFields(_) | StructTailExpr::None => false, + }; + fields.iter().map(|field| field.expr).any(|e| e.can_have_side_effects()) + || init_side_effects } ExprKind::Array(args) @@ -1926,20 +2184,52 @@ impl Expr<'_> { ExprKind::Path(QPath::Resolved(None, path2)), ) => path1.res == path2.res, ( - ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, _), [val1], None), - ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, _), [val2], None), + ExprKind::Struct( + QPath::LangItem(LangItem::RangeTo, _), + [val1], + StructTailExpr::None, + ), + ExprKind::Struct( + QPath::LangItem(LangItem::RangeTo, _), + [val2], + StructTailExpr::None, + ), ) | ( - ExprKind::Struct(QPath::LangItem(LangItem::RangeToInclusive, _), [val1], None), - ExprKind::Struct(QPath::LangItem(LangItem::RangeToInclusive, _), [val2], None), + ExprKind::Struct( + QPath::LangItem(LangItem::RangeToInclusive, _), + [val1], + StructTailExpr::None, + ), + ExprKind::Struct( + QPath::LangItem(LangItem::RangeToInclusive, _), + [val2], + StructTailExpr::None, + ), ) | ( - ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, _), [val1], None), - ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, _), [val2], None), + ExprKind::Struct( + QPath::LangItem(LangItem::RangeFrom, _), + [val1], + StructTailExpr::None, + ), + ExprKind::Struct( + QPath::LangItem(LangItem::RangeFrom, _), + [val2], + StructTailExpr::None, + ), ) => val1.expr.equivalent_for_indexing(val2.expr), ( - ExprKind::Struct(QPath::LangItem(LangItem::Range, _), [val1, val3], None), - ExprKind::Struct(QPath::LangItem(LangItem::Range, _), [val2, val4], None), + ExprKind::Struct( + QPath::LangItem(LangItem::Range, _), + [val1, val3], + StructTailExpr::None, + ), + ExprKind::Struct( + QPath::LangItem(LangItem::Range, _), + [val2, val4], + StructTailExpr::None, + ), ) => { val1.expr.equivalent_for_indexing(val2.expr) && val3.expr.equivalent_for_indexing(val4.expr) @@ -1983,6 +2273,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 @@ -2096,7 +2402,7 @@ pub enum ExprKind<'hir> { /// /// E.g., `Foo {x: 1, y: 2}`, or `Foo {x: 1, .. base}`, /// where `base` is the `Option<Expr>`. - Struct(&'hir QPath<'hir>, &'hir [ExprField<'hir>], Option<&'hir Expr<'hir>>), + Struct(&'hir QPath<'hir>, &'hir [ExprField<'hir>], StructTailExpr<'hir>), /// An array literal constructed from one repeated element. /// @@ -2107,10 +2413,27 @@ pub enum ExprKind<'hir> { /// A suspension point for coroutines (i.e., `yield <expr>`). Yield(&'hir Expr<'hir>, YieldSource), + /// Operators which can be used to interconvert `unsafe` binder types. + /// e.g. `unsafe<'a> &'a i32` <=> `&i32`. + UnsafeBinderCast(UnsafeBinderCastKind, &'hir Expr<'hir>, Option<&'hir Ty<'hir>>), + /// A placeholder for an expression that wasn't syntactically well formed in some way. Err(rustc_span::ErrorGuaranteed), } +#[derive(Debug, Clone, Copy, HashStable_Generic)] +pub enum StructTailExpr<'hir> { + /// A struct expression where all the fields are explicitly enumerated: `Foo { a, b }`. + None, + /// A struct expression with a "base", an expression of the same type as the outer struct that + /// will be used to populate any fields not explicitly mentioned: `Foo { ..base }` + Base(&'hir Expr<'hir>), + /// A struct expression with a `..` tail but no "base" expression. The values from the struct + /// fields' default values will be used to populate any fields not explicitly mentioned: + /// `Foo { .. }`. + DefaultFields(Span), +} + /// Represents an optionally `Self`-qualified value/type path or associated extension. /// /// To resolve the path to a `DefId`, call [`qpath_res`]. @@ -2731,6 +3054,12 @@ pub struct BareFnTy<'hir> { } #[derive(Debug, Clone, Copy, HashStable_Generic)] +pub struct UnsafeBinderTy<'hir> { + pub generic_params: &'hir [GenericParam<'hir>], + pub inner_ty: &'hir Ty<'hir>, +} + +#[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct OpaqueTy<'hir> { pub hir_id: HirId, pub def_id: LocalDefId, @@ -2828,12 +3157,12 @@ pub enum TyKind<'hir> { Ref(&'hir Lifetime, MutTy<'hir>), /// A bare function (e.g., `fn(usize) -> bool`). BareFn(&'hir BareFnTy<'hir>), + /// An unsafe binder type (e.g. `unsafe<'a> Foo<'a>`). + UnsafeBinder(&'hir UnsafeBinderTy<'hir>), /// The never type (`!`). Never, /// A tuple (`(A, B, C, D, ...)`). Tup(&'hir [Ty<'hir>]), - /// An anonymous struct or union type i.e. `struct { foo: Type }` or `union { foo: Type }` - AnonAdt(ItemId), /// A path to a type definition (`module::module::...::Type`), or an /// associated type (e.g., `<Vec<T> as Trait>::Type` or `<T>::Target`). /// @@ -2841,6 +3170,8 @@ pub enum TyKind<'hir> { Path(QPath<'hir>), /// An opaque type definition itself. This is only used for `impl Trait`. OpaqueDef(&'hir OpaqueTy<'hir>), + /// A trait ascription type, which is `impl Trait` within a local binding. + TraitAscription(GenericBounds<'hir>), /// A trait object type `Bound1 + Bound2 + Bound3` /// where `Bound` is a trait or a lifetime. TraitObject(&'hir [PolyTraitRef<'hir>], &'hir Lifetime, TraitObjectSyntax), @@ -3172,6 +3503,7 @@ pub struct FieldDef<'hir> { pub def_id: LocalDefId, pub ty: &'hir Ty<'hir>, pub safety: Safety, + pub default: Option<&'hir AnonConst>, } impl FieldDef<'_> { @@ -3350,6 +3682,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 { @@ -3394,7 +3739,7 @@ impl FnHeader { } pub fn is_unsafe(&self) -> bool { - matches!(&self.safety, Safety::Unsafe) + self.safety.is_unsafe() } } @@ -3993,9 +4338,7 @@ impl<'hir> Node<'hir> { _ => None, }, Node::TraitItem(ti) => match ti.kind { - TraitItemKind::Fn(ref sig, TraitFn::Provided(_)) => { - Some(FnKind::Method(ti.ident, sig)) - } + TraitItemKind::Fn(ref sig, _) => Some(FnKind::Method(ti.ident, sig)), _ => None, }, Node::ImplItem(ii) => match ii.kind { diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 602aa8be740..5ed5a43d522 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -64,8 +64,8 @@ //! 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}; @@ -748,7 +748,10 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) ExprKind::Struct(ref qpath, fields, ref optional_base) => { try_visit!(visitor.visit_qpath(qpath, expression.hir_id, expression.span)); walk_list!(visitor, visit_expr_field, fields); - visit_opt!(visitor, visit_expr, optional_base); + match optional_base { + StructTailExpr::Base(base) => try_visit!(visitor.visit_expr(base)), + StructTailExpr::None | StructTailExpr::DefaultFields(_) => {} + } } ExprKind::Tup(subexpressions) => { walk_list!(visitor, visit_expr, subexpressions); @@ -854,6 +857,10 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) ExprKind::Yield(ref subexpression, _) => { try_visit!(visitor.visit_expr(subexpression)); } + ExprKind::UnsafeBinderCast(_kind, expr, ty) => { + try_visit!(visitor.visit_expr(expr)); + visit_opt!(visitor, visit_ty, ty); + } ExprKind::Lit(_) | ExprKind::Err(_) => {} } V::Result::output() @@ -883,12 +890,19 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul walk_list!(visitor, visit_generic_param, function_declaration.generic_params); try_visit!(visitor.visit_fn_decl(function_declaration.decl)); } + TyKind::UnsafeBinder(ref unsafe_binder) => { + walk_list!(visitor, visit_generic_param, unsafe_binder.generic_params); + try_visit!(visitor.visit_ty(unsafe_binder.inner_ty)); + } TyKind::Path(ref qpath) => { try_visit!(visitor.visit_qpath(qpath, typ.hir_id, typ.span)); } TyKind::OpaqueDef(opaque) => { try_visit!(visitor.visit_opaque_ty(opaque)); } + TyKind::TraitAscription(bounds) => { + walk_list!(visitor, visit_param_bound, bounds); + } TyKind::Array(ref ty, ref length) => { try_visit!(visitor.visit_ty(ty)); try_visit!(visitor.visit_const_arg(length)); @@ -901,9 +915,6 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul } TyKind::Typeof(ref expression) => try_visit!(visitor.visit_anon_const(expression)), TyKind::Infer | TyKind::InferDelegation(..) | TyKind::Err(_) => {} - TyKind::AnonAdt(item_id) => { - try_visit!(visitor.visit_nested_item(item_id)); - } TyKind::Pat(ty, pat) => { try_visit!(visitor.visit_ty(ty)); try_visit!(visitor.visit_pattern_type_pattern(pat)); @@ -1190,10 +1201,14 @@ pub fn walk_struct_def<'v, V: Visitor<'v>>( V::Result::output() } -pub fn walk_field_def<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v FieldDef<'v>) -> V::Result { - try_visit!(visitor.visit_id(field.hir_id)); - try_visit!(visitor.visit_ident(field.ident)); - visitor.visit_ty(field.ty) +pub fn walk_field_def<'v, V: Visitor<'v>>( + visitor: &mut V, + FieldDef { hir_id, ident, ty, default, span: _, vis_span: _, def_id: _, safety: _ }: &'v FieldDef<'v>, +) -> V::Result { + try_visit!(visitor.visit_id(*hir_id)); + try_visit!(visitor.visit_ident(*ident)); + visit_opt!(visitor, visit_anon_const, default); + visitor.visit_ty(*ty) } pub fn walk_enum_def<'v, V: Visitor<'v>>( diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 15cb331d07a..3684695774e 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -7,7 +7,7 @@ //! * 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}; @@ -153,11 +153,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/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/tests.rs b/compiler/rustc_hir/src/tests.rs index 5c10fa46971..953e48a6d33 100644 --- a/compiler/rustc_hir/src/tests.rs +++ b/compiler/rustc_hir/src/tests.rs @@ -1,3 +1,5 @@ +#![cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] + use rustc_data_structures::stable_hasher::Hash64; use rustc_span::def_id::{DefPathHash, StableCrateId}; use rustc_span::edition::Edition; 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 070d63b48b7..0c3ed9b5c60 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -96,12 +96,14 @@ hir_analysis_coercion_between_struct_same_note = expected coercion between the s hir_analysis_coercion_between_struct_single_note = expected a single field to be coerced, none found -hir_analysis_const_bound_for_non_const_trait = - `{$modifier}` can only be applied to `#[const_trait]` traits - -hir_analysis_const_impl_for_non_const_trait = - const `impl` for trait `{$trait_name}` which is not marked with `#[const_trait]` - .suggestion = mark `{$trait_name}` as const +hir_analysis_const_bound_for_non_const_trait = `{$modifier}` can only be applied to `#[const_trait]` traits + .label = can't be applied to `{$trait_name}` + .note = `{$trait_name}` can't be used with `{$modifier}` because it isn't annotated with `#[const_trait]` + .suggestion = {$suggestion_pre}mark `{$trait_name}` as `#[const_trait]` to allow it to have `const` implementations + +hir_analysis_const_impl_for_non_const_trait = const `impl` for trait `{$trait_name}` which is not marked with `#[const_trait]` + .label = this trait is not `const` + .suggestion = {$suggestion_pre}mark `{$trait_name}` as `#[const_trait]` to allow it to have `const` implementations .note = marking a trait with `#[const_trait]` ensures all default method bodies are `const` .adding = adding a non-const method body in the future would be a breaking change @@ -239,11 +241,23 @@ hir_analysis_invalid_generic_receiver_ty_help = use a concrete type such as `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 = invalid `self` parameter type: `{$receiver_ty}` - .note = type of `self` must be `Self` or a type that dereferences to it + .note = type of `self` must be `Self` or some type implementing `Receiver` hir_analysis_invalid_receiver_ty_help = + consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box<Self>`, `self: Rc<Self>`, or `self: Arc<Self>` + +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 + hir_analysis_invalid_union_field = field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union .note = union fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>` diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs index 5a66c31a0cc..d8e9227a87c 100644 --- a/compiler/rustc_hir_analysis/src/autoderef.rs +++ b/compiler/rustc_hir_analysis/src/autoderef.rs @@ -18,7 +18,6 @@ pub enum AutoderefKind { /// A type which must dispatch to a `Deref` implementation. Overloaded, } - struct AutoderefSnapshot<'tcx> { at_start: bool, reached_recursion_limit: bool, @@ -27,6 +26,10 @@ struct AutoderefSnapshot<'tcx> { obligations: PredicateObligations<'tcx>, } +/// Recursively dereference a type, considering both built-in +/// dereferences (`*`) and the `Deref` trait. +/// Although called `Autoderef` it can be configured to use the +/// `Receiver` trait instead of the `Deref` trait. pub struct Autoderef<'a, 'tcx> { // Meta infos: infcx: &'a InferCtxt<'tcx>, @@ -39,6 +42,7 @@ pub struct Autoderef<'a, 'tcx> { // Configurations: include_raw_pointers: bool, + use_receiver_trait: bool, silence_errors: bool, } @@ -69,6 +73,10 @@ impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> { } // Otherwise, deref if type is derefable: + // NOTE: in the case of self.use_receiver_trait = true, you might think it would + // be better to skip this clause and use the Overloaded case only, since &T + // and &mut T implement Receiver. But built-in derefs apply equally to Receiver + // and Deref, and this has benefits for const and the emitted MIR. let (kind, new_ty) = if let Some(ty) = self.state.cur_ty.builtin_deref(self.include_raw_pointers) { debug_assert_eq!(ty, self.infcx.resolve_vars_if_possible(ty)); @@ -111,7 +119,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { body_def_id: LocalDefId, span: Span, base_ty: Ty<'tcx>, - ) -> Autoderef<'a, 'tcx> { + ) -> Self { Autoderef { infcx, span, @@ -125,6 +133,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { reached_recursion_limit: false, }, include_raw_pointers: false, + use_receiver_trait: false, silence_errors: false, } } @@ -137,8 +146,13 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { return None; } - // <ty as Deref> - let trait_ref = ty::TraitRef::new(tcx, tcx.lang_items().deref_trait()?, [ty]); + // <ty as Deref>, or whatever the equivalent trait is that we've been asked to walk. + let (trait_def_id, trait_target_def_id) = if self.use_receiver_trait { + (tcx.lang_items().receiver_trait()?, tcx.lang_items().receiver_target()?) + } else { + (tcx.lang_items().deref_trait()?, tcx.lang_items().deref_target()?) + }; + let trait_ref = ty::TraitRef::new(tcx, trait_def_id, [ty]); let cause = traits::ObligationCause::misc(self.span, self.body_id); let obligation = traits::Obligation::new( tcx, @@ -151,11 +165,8 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { return None; } - let (normalized_ty, obligations) = self.structurally_normalize(Ty::new_projection( - tcx, - tcx.lang_items().deref_target()?, - [ty], - ))?; + let (normalized_ty, obligations) = + self.structurally_normalize(Ty::new_projection(tcx, trait_target_def_id, [ty]))?; debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations); self.state.obligations.extend(obligations); @@ -234,6 +245,14 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { self } + /// Use `core::ops::Receiver` and `core::ops::Receiver::Target` as + /// the trait and associated type to iterate, instead of + /// `core::ops::Deref` and `core::ops::Deref::Target` + pub fn use_receiver_trait(mut self) -> Self { + self.use_receiver_trait = true; + self + } + pub fn silence_errors(mut self) -> Self { self.silence_errors = true; self 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/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs index df4da03f0f5..90e93bdbb50 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs @@ -3,6 +3,7 @@ use std::assert_matches::debug_assert_matches; use rustc_abi::FieldIdx; use rustc_ast::InlineAsmTemplatePiece; use rustc_data_structures::fx::FxIndexSet; +use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, LangItem}; use rustc_middle::bug; use rustc_middle::ty::{self, FloatTy, IntTy, Ty, TyCtxt, TypeVisitableExt, UintTy}; @@ -21,6 +22,12 @@ pub struct InlineAsmCtxt<'a, 'tcx> { get_operand_ty: Box<dyn Fn(&'tcx hir::Expr<'tcx>) -> Ty<'tcx> + 'a>, } +enum NonAsmTypeReason<'tcx> { + UnevaluatedSIMDArrayLength(DefId, ty::Const<'tcx>), + Invalid(Ty<'tcx>), + InvalidElement(DefId, Ty<'tcx>), +} + impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { pub fn new_global_asm(tcx: TyCtxt<'tcx>) -> Self { InlineAsmCtxt { @@ -56,7 +63,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { false } - fn get_asm_ty(&self, ty: Ty<'tcx>) -> Option<InlineAsmType> { + fn get_asm_ty(&self, ty: Ty<'tcx>) -> Result<InlineAsmType, NonAsmTypeReason<'tcx>> { let asm_ty_isize = match self.tcx.sess.target.pointer_width { 16 => InlineAsmType::I16, 32 => InlineAsmType::I32, @@ -65,64 +72,62 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { }; match *ty.kind() { - ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Some(InlineAsmType::I8), - ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Some(InlineAsmType::I16), - ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Some(InlineAsmType::I32), - ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Some(InlineAsmType::I64), - ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => Some(InlineAsmType::I128), - ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Some(asm_ty_isize), - ty::Float(FloatTy::F16) => Some(InlineAsmType::F16), - ty::Float(FloatTy::F32) => Some(InlineAsmType::F32), - ty::Float(FloatTy::F64) => Some(InlineAsmType::F64), - ty::Float(FloatTy::F128) => Some(InlineAsmType::F128), - ty::FnPtr(..) => Some(asm_ty_isize), - ty::RawPtr(ty, _) if self.is_thin_ptr_ty(ty) => Some(asm_ty_isize), + ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Ok(InlineAsmType::I8), + ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Ok(InlineAsmType::I16), + ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Ok(InlineAsmType::I32), + ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Ok(InlineAsmType::I64), + ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => Ok(InlineAsmType::I128), + ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Ok(asm_ty_isize), + ty::Float(FloatTy::F16) => Ok(InlineAsmType::F16), + ty::Float(FloatTy::F32) => Ok(InlineAsmType::F32), + ty::Float(FloatTy::F64) => Ok(InlineAsmType::F64), + ty::Float(FloatTy::F128) => Ok(InlineAsmType::F128), + ty::FnPtr(..) => Ok(asm_ty_isize), + ty::RawPtr(ty, _) if self.is_thin_ptr_ty(ty) => Ok(asm_ty_isize), ty::Adt(adt, args) if adt.repr().simd() => { let fields = &adt.non_enum_variant().fields; - let elem_ty = fields[FieldIdx::ZERO].ty(self.tcx, args); + let field = &fields[FieldIdx::ZERO]; + let elem_ty = field.ty(self.tcx, args); let (size, ty) = match elem_ty.kind() { ty::Array(ty, len) => { + let len = self.tcx.normalize_erasing_regions(self.typing_env, *len); if let Some(len) = len.try_to_target_usize(self.tcx) { (len, *ty) } else { - return None; + return Err(NonAsmTypeReason::UnevaluatedSIMDArrayLength( + field.did, len, + )); } } _ => (fields.len() as u64, elem_ty), }; match ty.kind() { - ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Some(InlineAsmType::VecI8(size)), - ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => { - Some(InlineAsmType::VecI16(size)) - } - ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => { - Some(InlineAsmType::VecI32(size)) - } - ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => { - Some(InlineAsmType::VecI64(size)) - } + ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Ok(InlineAsmType::VecI8(size)), + ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Ok(InlineAsmType::VecI16(size)), + ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Ok(InlineAsmType::VecI32(size)), + ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Ok(InlineAsmType::VecI64(size)), ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => { - Some(InlineAsmType::VecI128(size)) + Ok(InlineAsmType::VecI128(size)) } ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => { - Some(match self.tcx.sess.target.pointer_width { + Ok(match self.tcx.sess.target.pointer_width { 16 => InlineAsmType::VecI16(size), 32 => InlineAsmType::VecI32(size), 64 => InlineAsmType::VecI64(size), width => bug!("unsupported pointer width: {width}"), }) } - ty::Float(FloatTy::F16) => Some(InlineAsmType::VecF16(size)), - ty::Float(FloatTy::F32) => Some(InlineAsmType::VecF32(size)), - ty::Float(FloatTy::F64) => Some(InlineAsmType::VecF64(size)), - ty::Float(FloatTy::F128) => Some(InlineAsmType::VecF128(size)), - _ => None, + ty::Float(FloatTy::F16) => Ok(InlineAsmType::VecF16(size)), + ty::Float(FloatTy::F32) => Ok(InlineAsmType::VecF32(size)), + ty::Float(FloatTy::F64) => Ok(InlineAsmType::VecF64(size)), + ty::Float(FloatTy::F128) => Ok(InlineAsmType::VecF128(size)), + _ => Err(NonAsmTypeReason::InvalidElement(field.did, ty)), } } ty::Infer(_) => bug!("unexpected infer ty in asm operand"), - _ => None, + _ => Err(NonAsmTypeReason::Invalid(ty)), } } @@ -163,17 +168,42 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { } _ => self.get_asm_ty(ty), }; - let Some(asm_ty) = asm_ty else { - let msg = format!("cannot use value of type `{ty}` for inline assembly"); - self.tcx - .dcx() - .struct_span_err(expr.span, msg) - .with_note( - "only integers, floats, SIMD vectors, pointers and function pointers \ - can be used as arguments for inline assembly", - ) - .emit(); - return None; + let asm_ty = match asm_ty { + Ok(asm_ty) => asm_ty, + Err(reason) => { + match reason { + NonAsmTypeReason::UnevaluatedSIMDArrayLength(did, len) => { + let msg = format!("cannot evaluate SIMD vector length `{len}`"); + self.tcx + .dcx() + .struct_span_err(self.tcx.def_span(did), msg) + .with_span_note( + expr.span, + "SIMD vector length needs to be known statically for use in `asm!`", + ) + .emit(); + } + NonAsmTypeReason::Invalid(ty) => { + let msg = format!("cannot use value of type `{ty}` for inline assembly"); + self.tcx.dcx().struct_span_err(expr.span, msg).with_note( + "only integers, floats, SIMD vectors, pointers and function pointers \ + can be used as arguments for inline assembly", + ).emit(); + } + NonAsmTypeReason::InvalidElement(did, ty) => { + let msg = format!( + "cannot use SIMD vector with element type `{ty}` for inline assembly" + ); + self.tcx.dcx() + .struct_span_err(self.tcx.def_span(did), msg).with_span_note( + expr.span, + "only integers, floats, SIMD vectors, pointers and function pointers \ + can be used as arguments for inline assembly", + ).emit(); + } + } + return None; + } }; // Check that the type implements Copy. The only case where this can diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 2b33da3c49a..95ad8225f61 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -44,6 +44,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> { @@ -1104,6 +1105,25 @@ fn check_type_defn<'tcx>( for variant in variants.iter() { // All field types must be well-formed. for field in &variant.fields { + if let Some(def_id) = field.value + && let Some(_ty) = tcx.type_of(def_id).no_bound_vars() + { + // FIXME(generic_const_exprs, default_field_values): this is a hack and needs to + // be refactored to check the instantiate-ability of the code better. + if let Some(def_id) = def_id.as_local() + && let hir::Node::AnonConst(anon) = tcx.hir_node_by_def_id(def_id) + && let expr = &tcx.hir().body(anon.body).value + && let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind + && let Res::Def(DefKind::ConstParam, _def_id) = path.res + { + // Do not evaluate bare `const` params, as those would ICE and are only + // usable if `#![feature(generic_const_exprs)]` is enabled. + } else { + // Evaluate the constant proactively, to emit an error if the constant has + // an unconditional error. We only do so if the const has no type params. + let _ = tcx.const_eval_poly(def_id.into()); + } + } let field_id = field.did.expect_local(); let hir::FieldDef { ty: hir_ty, .. } = tcx.hir_node_by_def_id(field_id).expect_field(); @@ -1729,8 +1749,25 @@ fn check_method_receiver<'tcx>( // Report error; would not have worked with `arbitrary_self_types[_pointers]`. { match receiver_validity_err { + ReceiverValidityError::DoesNotDeref if arbitrary_self_types_level.is_some() => { + 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::InvalidReceiverTy { span, receiver_ty }) + tcx.dcx().emit_err(errors::InvalidReceiverTyNoArbitrarySelfTypes { + span, + receiver_ty, + }) } ReceiverValidityError::MethodGenericParamUsed => { tcx.dcx().emit_err(errors::InvalidGenericReceiverTy { span, receiver_ty }) @@ -1802,13 +1839,18 @@ fn receiver_is_valid<'tcx>( let mut autoderef = Autoderef::new(infcx, wfcx.param_env, wfcx.body_def_id, span, receiver_ty); + // The `arbitrary_self_types` feature allows custom smart pointer + // types to be method receivers, as identified by following the Receiver<Target=T> + // chain. + if arbitrary_self_types_enabled.is_some() { + autoderef = autoderef.use_receiver_trait(); + } + // The `arbitrary_self_types_pointers` feature allows raw pointer receivers like `self: *const Self`. if arbitrary_self_types_enabled == Some(ArbitrarySelfTypesLevel::WithPointers) { autoderef = autoderef.include_raw_pointers(); } - let receiver_trait_def_id = tcx.require_lang_item(LangItem::LegacyReceiver, Some(span)); - // Keep dereferencing `receiver_ty` until we get to `self_ty`. while let Some((potential_self_ty, _)) = autoderef.next() { debug!( @@ -1830,11 +1872,13 @@ fn receiver_is_valid<'tcx>( } // Without `feature(arbitrary_self_types)`, we require that each step in the - // deref chain implement `receiver`. + // deref chain implement `LegacyReceiver`. if arbitrary_self_types_enabled.is_none() { - if !receiver_is_implemented( + let legacy_receiver_trait_def_id = + tcx.require_lang_item(LangItem::LegacyReceiver, Some(span)); + if !legacy_receiver_is_implemented( wfcx, - receiver_trait_def_id, + legacy_receiver_trait_def_id, cause.clone(), potential_self_ty, ) { @@ -1847,7 +1891,7 @@ fn receiver_is_valid<'tcx>( cause.clone(), wfcx.param_env, potential_self_ty, - receiver_trait_def_id, + legacy_receiver_trait_def_id, ); } } @@ -1856,14 +1900,14 @@ fn receiver_is_valid<'tcx>( Err(ReceiverValidityError::DoesNotDeref) } -fn receiver_is_implemented<'tcx>( +fn legacy_receiver_is_implemented<'tcx>( wfcx: &WfCheckingCtxt<'_, 'tcx>, - receiver_trait_def_id: DefId, + legacy_receiver_trait_def_id: DefId, cause: ObligationCause<'tcx>, receiver_ty: Ty<'tcx>, ) -> bool { let tcx = wfcx.tcx(); - let trait_ref = ty::TraitRef::new(tcx, receiver_trait_def_id, [receiver_ty]); + let trait_ref = ty::TraitRef::new(tcx, legacy_receiver_trait_def_id, [receiver_ty]); let obligation = Obligation::new(tcx, cause, wfcx.param_env, trait_ref); @@ -1871,7 +1915,7 @@ fn receiver_is_implemented<'tcx>( true } else { debug!( - "receiver_is_implemented: type `{:?}` does not implement `Receiver` trait", + "receiver_is_implemented: type `{:?}` does not implement `LegacyReceiver` trait", receiver_ty ); false diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 3b49bc41ffe..2eea65125b0 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -103,7 +103,7 @@ fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaran } let cause = traits::ObligationCause::misc(DUMMY_SP, impl_did); - match type_allowed_to_implement_copy(tcx, param_env, self_type, cause) { + match type_allowed_to_implement_copy(tcx, param_env, self_type, cause, impl_header.safety) { Ok(()) => Ok(()), Err(CopyImplementationError::InfringingFields(fields)) => { let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span; @@ -123,6 +123,12 @@ fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaran let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span; Err(tcx.dcx().emit_err(errors::CopyImplOnTypeWithDtor { span })) } + Err(CopyImplementationError::HasUnsafeFields) => { + let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span; + Err(tcx + .dcx() + .span_delayed_bug(span, format!("cannot implement `Copy` for `{}`", self_type))) + } } } diff --git a/compiler/rustc_hir_analysis/src/coherence/unsafety.rs b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs index d66114a50d7..86839e40330 100644 --- a/compiler/rustc_hir_analysis/src/coherence/unsafety.rs +++ b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs @@ -3,7 +3,7 @@ use rustc_errors::codes::*; use rustc_errors::struct_span_code_err; -use rustc_hir::Safety; +use rustc_hir::{LangItem, Safety}; use rustc_middle::ty::ImplPolarity::*; use rustc_middle::ty::print::PrintTraitRefExt as _; use rustc_middle::ty::{ImplTraitHeader, TraitDef, TyCtxt}; @@ -20,7 +20,19 @@ pub(super) fn check_item( tcx.generics_of(def_id).own_params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle"); let trait_ref = trait_header.trait_ref.instantiate_identity(); - match (trait_def.safety, unsafe_attr, trait_header.safety, trait_header.polarity) { + let is_copy = tcx.is_lang_item(trait_def.def_id, LangItem::Copy); + let trait_def_safety = if is_copy { + // If `Self` has unsafe fields, `Copy` is unsafe to implement. + if trait_header.trait_ref.skip_binder().self_ty().has_unsafe_fields() { + rustc_hir::Safety::Unsafe + } else { + rustc_hir::Safety::Safe + } + } else { + trait_def.safety + }; + + match (trait_def_safety, unsafe_attr, trait_header.safety, trait_header.polarity) { (Safety::Safe, None, Safety::Unsafe, Positive | Reservation) => { let span = tcx.def_span(def_id); return Err(struct_span_code_err!( @@ -48,12 +60,22 @@ pub(super) fn check_item( "the trait `{}` requires an `unsafe impl` declaration", trait_ref.print_trait_sugared() ) - .with_note(format!( - "the trait `{}` enforces invariants that the compiler can't check. \ - Review the trait documentation and make sure this implementation \ - upholds those invariants before adding the `unsafe` keyword", - trait_ref.print_trait_sugared() - )) + .with_note(if is_copy { + format!( + "the trait `{}` cannot be safely implemented for `{}` \ + because it has unsafe fields. Review the invariants \ + of those fields before adding an `unsafe impl`", + trait_ref.print_trait_sugared(), + trait_ref.self_ty(), + ) + } else { + format!( + "the trait `{}` enforces invariants that the compiler can't check. \ + Review the trait documentation and make sure this implementation \ + upholds those invariants before adding the `unsafe` keyword", + trait_ref.print_trait_sugared() + ) + }) .with_span_suggestion_verbose( span.shrink_to_lo(), "add `unsafe` to this trait implementation", diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index a4636da3f62..8c9da78a659 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -29,7 +29,7 @@ use rustc_errors::{ use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor, walk_generics}; -use rustc_hir::{self as hir, GenericParamKind, Node}; +use rustc_hir::{self as hir, GenericParamKind, HirId, Node}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::ObligationCause; use rustc_middle::hir::nested_filter; @@ -201,12 +201,13 @@ pub(crate) fn placeholder_type_error_diag<'cx, 'tcx>( placeholder_types.iter().map(|sp| (*sp, (*type_name).to_string())).collect(); if let Some(generics) = generics { - if let Some(arg) = params.iter().find(|arg| { - matches!(arg.name, hir::ParamName::Plain(Ident { name: kw::Underscore, .. })) + if let Some(span) = params.iter().find_map(|arg| match arg.name { + hir::ParamName::Plain(Ident { name: kw::Underscore, span }) => Some(span), + _ => None, }) { // Account for `_` already present in cases like `struct S<_>(_);` and suggest // `struct S<T>(T);` instead of `struct S<_, T>(T);`. - sugg.push((arg.span, (*type_name).to_string())); + sugg.push((span, (*type_name).to_string())); } else if let Some(span) = generics.span_for_param_suggestion() { // Account for bounds, we want `fn foo<T: E, K>(_: K)` not `fn foo<T, K: E>(_: K)`. sugg.push((span, format!(", {type_name}"))); @@ -436,6 +437,15 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { ty::Const::new_error_with_message(self.tcx(), span, "bad placeholder constant") } + fn register_trait_ascription_bounds( + &self, + _: Vec<(ty::Clause<'tcx>, Span)>, + _: HirId, + span: Span, + ) { + self.dcx().span_delayed_bug(span, "trait ascription type not allowed here"); + } + fn probe_ty_param_bounds( &self, span: Span, @@ -1021,12 +1031,12 @@ impl<'tcx> FieldUniquenessCheckContext<'tcx> { } } -fn lower_variant( - tcx: TyCtxt<'_>, +fn lower_variant<'tcx>( + tcx: TyCtxt<'tcx>, variant_did: Option<LocalDefId>, ident: Ident, discr: ty::VariantDiscr, - def: &hir::VariantData<'_>, + def: &hir::VariantData<'tcx>, adt_kind: ty::AdtKind, parent_did: LocalDefId, ) -> ty::VariantDef { @@ -1042,6 +1052,7 @@ fn lower_variant( name: f.ident.name, vis: tcx.visibility(f.def_id), safety: f.safety, + value: f.default.map(|v| v.def_id.to_def_id()), }) .collect(); let recovered = match def { @@ -1329,7 +1340,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, .. }) => { @@ -1346,7 +1357,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) } } @@ -1396,99 +1407,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>( @@ -1610,7 +1630,7 @@ fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::ImplTrai impl_.of_trait.as_ref().map(|ast_trait_ref| { let selfty = tcx.type_of(def_id).instantiate_identity(); - check_impl_constness(tcx, tcx.is_const_trait_impl(def_id.to_def_id()), ast_trait_ref); + check_impl_constness(tcx, impl_.constness, ast_trait_ref); let trait_ref = icx.lowerer().lower_impl_trait_ref(ast_trait_ref, selfty); @@ -1618,33 +1638,46 @@ fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::ImplTrai trait_ref: ty::EarlyBinder::bind(trait_ref), safety: impl_.safety, polarity: polarity_of_impl(tcx, def_id, impl_, item.span), + constness: impl_.constness, } }) } fn check_impl_constness( tcx: TyCtxt<'_>, - is_const: bool, + constness: hir::Constness, hir_trait_ref: &hir::TraitRef<'_>, -) -> Option<ErrorGuaranteed> { - if !is_const { - return None; +) { + if let hir::Constness::NotConst = constness { + return; } - let trait_def_id = hir_trait_ref.trait_def_id()?; + let Some(trait_def_id) = hir_trait_ref.trait_def_id() else { return }; if tcx.is_const_trait(trait_def_id) { - return None; + return; } let trait_name = tcx.item_name(trait_def_id).to_string(); - Some(tcx.dcx().emit_err(errors::ConstImplForNonConstTrait { + let (local_trait_span, suggestion_pre) = + match (trait_def_id.is_local(), tcx.sess.is_nightly_build()) { + (true, true) => ( + Some(tcx.def_span(trait_def_id).shrink_to_lo()), + if tcx.features().const_trait_impl() { + "" + } else { + "enable `#![feature(const_trait_impl)]` in your crate and " + }, + ), + (false, _) | (_, false) => (None, ""), + }; + tcx.dcx().emit_err(errors::ConstImplForNonConstTrait { trait_ref_span: hir_trait_ref.path.span, trait_name, - local_trait_span: - trait_def_id.as_local().map(|_| tcx.def_span(trait_def_id).shrink_to_lo()), + local_trait_span, + suggestion_pre, marking: (), adding: (), - })) + }); } fn polarity_of_impl( diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 1d9114b0ef3..f52d4f42eca 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -470,6 +470,12 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<S self.outer_index.shift_out(1); res } + hir::TyKind::UnsafeBinder(_) => { + self.outer_index.shift_in(1); + let res = intravisit::walk_ty(self, ty); + self.outer_index.shift_out(1); + res + } _ => intravisit::walk_ty(self, ty), } } diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index ca2597e79fd..1a6c0a93436 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -711,12 +711,19 @@ pub(super) fn assert_only_contains_predicates_from<'tcx>( `{filter:?}` implied bounds: {clause:?}" ); } + ty::ClauseKind::HostEffect(host_effect_predicate) => { + assert_eq!( + host_effect_predicate.self_ty(), + ty, + "expected `Self` predicate when computing \ + `{filter:?}` implied bounds: {clause:?}" + ); + } ty::ClauseKind::RegionOutlives(_) | ty::ClauseKind::ConstArgHasType(_, _) | ty::ClauseKind::WellFormed(_) - | ty::ClauseKind::ConstEvaluatable(_) - | ty::ClauseKind::HostEffect(..) => { + | ty::ClauseKind::ConstEvaluatable(_) => { bug!( "unexpected non-`Self` predicate when computing \ `{filter:?}` implied bounds: {clause:?}" 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 74f381d2661..0f797bcdae4 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -781,6 +781,36 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { intravisit::walk_ty(this, ty); }); } + hir::TyKind::UnsafeBinder(binder) => { + let (mut bound_vars, binders): (FxIndexMap<LocalDefId, ResolvedArg>, Vec<_>) = + binder + .generic_params + .iter() + .enumerate() + .map(|(late_bound_idx, param)| { + ( + (param.def_id, ResolvedArg::late(late_bound_idx as u32, param)), + late_arg_as_bound_arg(self.tcx, param), + ) + }) + .unzip(); + + deny_non_region_late_bound(self.tcx, &mut bound_vars, "function pointer types"); + + self.record_late_bound_vars(ty.hir_id, binders); + let scope = Scope::Binder { + hir_id: ty.hir_id, + bound_vars, + s: self.scope, + scope_type: BinderScopeType::Normal, + where_bound_origin: None, + }; + self.with(scope, |this| { + // a bare fn has no bounds, so everything + // contained within is scoped within its binder. + intravisit::walk_ty(this, ty); + }); + } hir::TyKind::TraitObject(bounds, lifetime, _) => { debug!(?bounds, ?lifetime, "TraitObject"); let scope = Scope::TraitRefBoundary { s: self.scope }; @@ -822,6 +852,21 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { }; self.with(scope, |this| this.visit_ty(mt.ty)); } + hir::TyKind::TraitAscription(bounds) => { + let scope = Scope::TraitRefBoundary { s: self.scope }; + self.with(scope, |this| { + let scope = Scope::LateBoundary { + s: this.scope, + what: "`impl Trait` in binding", + deny_late_regions: true, + }; + this.with(scope, |this| { + for bound in bounds { + this.visit_param_bound(bound); + } + }) + }); + } _ => intravisit::walk_ty(self, ty), } } diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 72d5b3ac4f5..5595504c3d9 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -125,6 +125,12 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { return ty; } + Node::Field(&hir::FieldDef { default: Some(c), def_id: field_def_id, .. }) + if c.hir_id == hir_id => + { + tcx.type_of(field_def_id).instantiate_identity() + } + _ => Ty::new_error_with_message( tcx, span, diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 4142dcff226..d46f60b16f5 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -530,10 +530,16 @@ pub(crate) struct GenericArgsOnOverriddenImpl { #[diag(hir_analysis_const_impl_for_non_const_trait)] pub(crate) struct ConstImplForNonConstTrait { #[primary_span] + #[label] pub trait_ref_span: Span, pub trait_name: String, - #[suggestion(applicability = "machine-applicable", code = "#[const_trait]")] + #[suggestion( + applicability = "machine-applicable", + code = "#[const_trait] ", + style = "verbose" + )] pub local_trait_span: Option<Span>, + pub suggestion_pre: &'static str, #[note] pub marking: (), #[note(hir_analysis_adding)] @@ -544,8 +550,19 @@ pub(crate) struct ConstImplForNonConstTrait { #[diag(hir_analysis_const_bound_for_non_const_trait)] pub(crate) struct ConstBoundForNonConstTrait { #[primary_span] + #[label] pub span: Span, pub modifier: &'static str, + #[note] + pub def_span: Option<Span>, + pub suggestion_pre: &'static str, + #[suggestion( + applicability = "machine-applicable", + code = "#[const_trait] ", + style = "verbose" + )] + pub suggestion: Option<Span>, + pub trait_name: String, } #[derive(Diagnostic)] @@ -1638,6 +1655,24 @@ 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] +#[help(hir_analysis_invalid_receiver_ty_help_no_arbitrary_self_types)] +pub(crate) struct InvalidReceiverTyNoArbitrarySelfTypes<'tcx> { + #[primary_span] + pub span: Span, + pub receiver_ty: Ty<'tcx>, +} + #[derive(Diagnostic)] #[diag(hir_analysis_invalid_receiver_ty, code = E0307)] #[note] @@ -1646,6 +1681,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/dyn_compatibility.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs index 321a8aba7f7..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.projection_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, ) { @@ -413,7 +417,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { late_bound_in_projection_term, late_bound_in_term, |br_name| { - let item_name = tcx.item_name(pred.projection_def_id()); + let item_name = tcx.item_name(pred.item_def_id()); struct_span_code_err!( self.dcx(), span, 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 ff449a858d6..00c1f9b332b 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -9,7 +9,6 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_middle::bug; -use rustc_middle::query::Key; use rustc_middle::ty::print::{PrintPolyTraitRefExt as _, PrintTraitRefExt as _}; use rustc_middle::ty::{ self, AdtDef, Binder, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeVisitableExt, @@ -279,7 +278,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } else { let mut err = self.dcx().create_err(err); if suggest_constraining_type_param( - tcx, generics, &mut err, &qself_str, &trait_ref, None, None, + tcx, + generics, + &mut err, + &qself_str, + &trait_ref, + Some(best_trait), + None, ) && !identically_named { // We suggested constraining a type parameter, but the associated item on it @@ -716,51 +721,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()); } } @@ -808,6 +804,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}`"), @@ -821,10 +818,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), @@ -834,81 +830,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() { @@ -919,32 +917,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); @@ -1001,8 +996,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { )), .. }) = node - && let Some(ty_def_id) = qself_ty.ty_def_id() - && let [inherent_impl] = tcx.inherent_impls(ty_def_id) + && let Some(adt_def) = qself_ty.ty_adt_def() + && let [inherent_impl] = tcx.inherent_impls(adt_def.did()) && let name = format!("{ident2}_{ident3}") && let Some(ty::AssocItem { kind: ty::AssocKind::Fn, .. }) = tcx .associated_items(inherent_impl) 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 7ab254e5f4b..a357ade0294 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -50,7 +50,7 @@ use rustc_span::{DUMMY_SP, Span}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::wf::object_region_bounds; use rustc_trait_selection::traits::{self, ObligationCtxt}; -use tracing::{debug, debug_span, instrument}; +use tracing::{debug, instrument}; use crate::bounds::Bounds; use crate::check::check_abi_fn_ptr; @@ -123,6 +123,13 @@ pub trait HirTyLowerer<'tcx> { /// Returns the const to use when a const is omitted. fn ct_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Const<'tcx>; + fn register_trait_ascription_bounds( + &self, + bounds: Vec<(ty::Clause<'tcx>, Span)>, + hir_id: HirId, + span: Span, + ); + /// Probe bounds in scope where the bounded type coincides with the given type parameter. /// /// Rephrased, this returns bounds of the form `T: Trait`, where `T` is a type parameter @@ -737,9 +744,26 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if let hir::BoundConstness::Always(span) | hir::BoundConstness::Maybe(span) = constness && !self.tcx().is_const_trait(trait_def_id) { + let (def_span, suggestion, suggestion_pre) = + match (trait_def_id.is_local(), self.tcx().sess.is_nightly_build()) { + (true, true) => ( + None, + Some(tcx.def_span(trait_def_id).shrink_to_lo()), + if self.tcx().features().const_trait_impl() { + "" + } else { + "enable `#![feature(const_trait_impl)]` in your crate and " + }, + ), + (false, _) | (_, false) => (Some(tcx.def_span(trait_def_id)), None, ""), + }; self.dcx().emit_err(crate::errors::ConstBoundForNonConstTrait { span, modifier: constness.as_str(), + def_span, + trait_name: self.tcx().def_path_str(trait_def_id), + suggestion_pre, + suggestion, }); } @@ -998,6 +1022,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.lower_const_arg(ct, FeedConstTy::No).into() } }; + if term.references_error() { + continue; + } // FIXME(#97583): This isn't syntactically well-formed! where_bounds.push(format!( " T: {trait}::{assoc_name} = {term}", @@ -2284,19 +2311,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir::TyKind::Tup(fields) => { Ty::new_tup_from_iter(tcx, fields.iter().map(|t| self.lower_ty(t))) } - hir::TyKind::AnonAdt(item_id) => { - let _guard = debug_span!("AnonAdt"); - - let did = item_id.owner_id.def_id; - let adt_def = tcx.adt_def(did); - - let args = ty::GenericArgs::for_item(tcx, did.to_def_id(), |param, _| { - tcx.mk_param_from_def(param) - }); - debug!(?args); - - Ty::new_adt(tcx, adt_def, tcx.mk_args(args)) - } hir::TyKind::BareFn(bf) => { require_c_abi_if_c_variadic(tcx, bf.decl, bf.abi, hir_ty.span); @@ -2305,6 +2319,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.lower_fn_ty(hir_ty.hir_id, bf.safety, bf.abi, bf.decl, None, Some(hir_ty)), ) } + hir::TyKind::UnsafeBinder(_binder) => { + let guar = self + .dcx() + .struct_span_err(hir_ty.span, "unsafe binders are not yet implemented") + .emit(); + Ty::new_error(tcx, guar) + } hir::TyKind::TraitObject(bounds, lifetime, repr) => { if let Some(guar) = self.prohibit_or_lint_bare_trait_object_ty(hir_ty) { // Don't continue with type analysis if the `dyn` keyword is missing @@ -2361,6 +2382,25 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.lower_opaque_ty(opaque_ty.def_id, in_trait) } + hir::TyKind::TraitAscription(hir_bounds) => { + // Impl trait in bindings lower as an infer var with additional + // set of type bounds. + let self_ty = self.ty_infer(None, hir_ty.span); + let mut bounds = Bounds::default(); + self.lower_bounds( + self_ty, + hir_bounds.iter(), + &mut bounds, + ty::List::empty(), + PredicateFilter::All, + ); + self.register_trait_ascription_bounds( + bounds.clauses().collect(), + hir_ty.hir_id, + hir_ty.span, + ); + self_ty + } // If we encounter a type relative path with RTN generics, then it must have // *not* gone through `lower_ty_maybe_return_type_notation`, and therefore // it's certainly in an illegal position. diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 0f3dcebc092..feb483a8bbb 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -11,16 +11,18 @@ 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, Span}; 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 +70,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 +266,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 +293,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 +348,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)); @@ -288,7 +394,13 @@ impl<'a> State<'a> { hir::TyKind::BareFn(f) => { self.print_ty_fn(f.abi, f.safety, f.decl, None, f.generic_params, f.param_names); } + hir::TyKind::UnsafeBinder(unsafe_binder) => { + self.print_unsafe_binder(unsafe_binder); + } hir::TyKind::OpaqueDef(..) => self.word("/*impl Trait*/"), + hir::TyKind::TraitAscription(bounds) => { + self.print_bounds("impl", bounds); + } hir::TyKind::Path(ref qpath) => self.print_qpath(qpath, false), hir::TyKind::TraitObject(bounds, lifetime, syntax) => { if syntax == ast::TraitObjectSyntax::Dyn { @@ -330,7 +442,6 @@ impl<'a> State<'a> { hir::TyKind::Infer | hir::TyKind::InferDelegation(..) => { self.word("_"); } - hir::TyKind::AnonAdt(..) => self.word("/* anonymous adt */"), hir::TyKind::Pat(ty, pat) => { self.print_type(ty); self.word(" is "); @@ -340,6 +451,15 @@ impl<'a> State<'a> { self.end() } + fn print_unsafe_binder(&mut self, unsafe_binder: &hir::UnsafeBinderTy<'_>) { + self.ibox(INDENT_UNIT); + self.word("unsafe"); + self.print_generic_params(unsafe_binder.generic_params); + self.nbsp(); + self.print_type(unsafe_binder.inner_ty); + self.end(); + } + fn print_foreign_item(&mut self, item: &hir::ForeignItem<'_>) { self.hardbreak_if_not_bol(); self.maybe_print_comment(item.span.lo()); @@ -912,14 +1032,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 { @@ -1080,22 +1200,36 @@ impl<'a> State<'a> { &mut self, qpath: &hir::QPath<'_>, fields: &[hir::ExprField<'_>], - wth: Option<&hir::Expr<'_>>, + wth: hir::StructTailExpr<'_>, ) { self.print_qpath(qpath, true); self.word("{"); self.commasep_cmnt(Consistent, fields, |s, field| s.print_expr_field(field), |f| f.span); - if let Some(expr) = wth { - self.ibox(INDENT_UNIT); - if !fields.is_empty() { - self.word(","); - self.space(); + match wth { + hir::StructTailExpr::Base(expr) => { + self.ibox(INDENT_UNIT); + if !fields.is_empty() { + self.word(","); + self.space(); + } + self.word(".."); + self.print_expr(expr); + self.end(); + } + hir::StructTailExpr::DefaultFields(_) => { + self.ibox(INDENT_UNIT); + if !fields.is_empty() { + self.word(","); + self.space(); + } + self.word(".."); + self.end(); + } + hir::StructTailExpr::None => { + if !fields.is_empty() { + self.word(","); + } } - self.word(".."); - self.print_expr(expr); - self.end(); - } else if !fields.is_empty() { - self.word(","); } self.word("}"); @@ -1517,6 +1651,19 @@ impl<'a> State<'a> { self.word(")"); } + hir::ExprKind::UnsafeBinderCast(kind, expr, ty) => { + match kind { + hir::UnsafeBinderCastKind::Wrap => self.word("wrap_binder!("), + hir::UnsafeBinderCastKind::Unwrap => self.word("unwrap_binder!("), + } + self.print_expr(expr); + if let Some(ty) = ty { + self.word(","); + self.space(); + self.print_type(ty); + } + self.word(")"); + } hir::ExprKind::Yield(expr, _) => { self.word_space("yield"); self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Jump); 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/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index b27f7215ae4..a93da52b270 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -10,6 +10,12 @@ hir_typeck_address_of_temporary_taken = cannot take address of a temporary hir_typeck_arg_mismatch_indeterminate = argument type mismatch was detected, but rustc had trouble determining where .note = we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new +hir_typeck_base_expression_double_dot = base expression required after `..` +hir_typeck_base_expression_double_dot_add_expr = add a base expression here +hir_typeck_base_expression_double_dot_enable_default_field_values = + add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields +hir_typeck_base_expression_double_dot_remove = remove the `..` as all the fields are already present + hir_typeck_candidate_trait_note = `{$trait_name}` defines an item `{$item_name}`{$action_or_ty -> [NONE] {""} [implement] , perhaps you need to implement it diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index bfdf764d299..243313ee876 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -57,7 +57,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // type in that case) let mut all_arms_diverge = Diverges::WarnedAlways; - let expected = orig_expected.adjust_for_branches(self); + let expected = + orig_expected.try_structurally_resolve_and_adjust_for_branches(self, expr.span); debug!(?expected); let mut coercion = { diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index e715a7f7e15..b8652d82d91 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -454,20 +454,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { closure_kind: hir::ClosureKind, projection: ty::PolyProjectionPredicate<'tcx>, ) -> Option<ExpectedSig<'tcx>> { - let tcx = self.tcx; - - let trait_def_id = projection.trait_def_id(tcx); + let def_id = projection.item_def_id(); // For now, we only do signature deduction based off of the `Fn` and `AsyncFn` traits, // for closures and async closures, respectively. match closure_kind { - hir::ClosureKind::Closure - if self.tcx.fn_trait_kind_from_def_id(trait_def_id).is_some() => - { + hir::ClosureKind::Closure if self.tcx.is_lang_item(def_id, LangItem::FnOnceOutput) => { self.extract_sig_from_projection(cause_span, projection) } hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async) - if self.tcx.async_fn_trait_kind_from_def_id(trait_def_id).is_some() => + if self.tcx.is_lang_item(def_id, LangItem::AsyncFnOnceOutput) => { self.extract_sig_from_projection(cause_span, projection) } @@ -475,7 +471,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // `F: FnOnce() -> Fut, Fut: Future<Output = T>` style bound. Let's still // guide inference here, since it's beneficial for the user. hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async) - if self.tcx.fn_trait_kind_from_def_id(trait_def_id).is_some() => + if self.tcx.is_lang_item(def_id, LangItem::FnOnceOutput) => { self.extract_sig_from_projection_and_future_bound(cause_span, projection) } diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index bf41dcbe4a3..7d7c9331edf 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -863,7 +863,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 +926,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)); diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 3399a9fe880..d0272651c08 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -917,7 +917,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { [candidate] => format!( "the method of the same name on {} `{}`", match candidate.kind { - probe::CandidateKind::InherentImplCandidate(_) => "the inherent impl for", + probe::CandidateKind::InherentImplCandidate { .. } => "the inherent impl for", _ => "trait", }, self.tcx.def_path_str(candidate.item.container_id(self.tcx)) diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index a2e00859307..7746a5a7132 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -16,6 +16,47 @@ use rustc_span::{Span, Symbol}; use crate::fluent_generated as fluent; #[derive(Diagnostic)] +#[diag(hir_typeck_base_expression_double_dot, code = E0797)] +pub(crate) struct BaseExpressionDoubleDot { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub default_field_values: Option<BaseExpressionDoubleDotEnableDefaultFieldValues>, + #[subdiagnostic] + pub add_expr: Option<BaseExpressionDoubleDotAddExpr>, + #[subdiagnostic] + pub remove_dots: Option<BaseExpressionDoubleDotRemove>, +} + +#[derive(Subdiagnostic)] +#[suggestion( + hir_typeck_base_expression_double_dot_remove, + code = "", + applicability = "machine-applicable", + style = "verbose" +)] +pub(crate) struct BaseExpressionDoubleDotRemove { + #[primary_span] + pub span: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion( + hir_typeck_base_expression_double_dot_add_expr, + code = "/* expr */", + applicability = "has-placeholders", + style = "verbose" +)] +pub(crate) struct BaseExpressionDoubleDotAddExpr { + #[primary_span] + pub span: Span, +} + +#[derive(Subdiagnostic)] +#[help(hir_typeck_base_expression_double_dot_enable_default_field_values)] +pub(crate) struct BaseExpressionDoubleDotEnableDefaultFieldValues; + +#[derive(Diagnostic)] #[diag(hir_typeck_field_multiply_specified_in_initializer, code = E0062)] pub(crate) struct FieldMultiplySpecifiedInInitializer { #[primary_span] diff --git a/compiler/rustc_hir_typeck/src/expectation.rs b/compiler/rustc_hir_typeck/src/expectation.rs index 4653458b5dd..6d95b6917e2 100644 --- a/compiler/rustc_hir_typeck/src/expectation.rs +++ b/compiler/rustc_hir_typeck/src/expectation.rs @@ -39,10 +39,14 @@ impl<'a, 'tcx> Expectation<'tcx> { // an expected type. Otherwise, we might write parts of the type // when checking the 'then' block which are incompatible with the // 'else' branch. - pub(super) fn adjust_for_branches(&self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> { + pub(super) fn try_structurally_resolve_and_adjust_for_branches( + &self, + fcx: &FnCtxt<'a, 'tcx>, + span: Span, + ) -> Expectation<'tcx> { match *self { ExpectHasType(ety) => { - let ety = fcx.shallow_resolve(ety); + let ety = fcx.try_structurally_resolve_type(span, ety); if !ety.is_ty_var() { ExpectHasType(ety) } else { NoExpectation } } ExpectRvalueLikeUnsized(ety) => ExpectRvalueLikeUnsized(ety), diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 4699b342cec..66978399efb 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -44,10 +44,11 @@ use crate::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectatio use crate::TupleArgumentsFlag::DontTupleArguments; use crate::coercion::{CoerceMany, DynamicCoerceMany}; use crate::errors::{ - AddressOfTemporaryTaken, FieldMultiplySpecifiedInInitializer, - FunctionalRecordUpdateOnNonStruct, HelpUseLatestEdition, ReturnLikeStatementKind, - ReturnStmtOutsideOfFnBody, StructExprNonExhaustive, TypeMismatchFruTypo, - YieldExprOutsideOfCoroutine, + AddressOfTemporaryTaken, BaseExpressionDoubleDot, BaseExpressionDoubleDotAddExpr, + BaseExpressionDoubleDotEnableDefaultFieldValues, BaseExpressionDoubleDotRemove, + FieldMultiplySpecifiedInInitializer, FunctionalRecordUpdateOnNonStruct, HelpUseLatestEdition, + ReturnLikeStatementKind, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive, + TypeMismatchFruTypo, YieldExprOutsideOfCoroutine, }; use crate::{ BreakableCtxt, CoroutineTypes, Diverges, FnCtxt, Needs, cast, fatally_break_rust, @@ -71,12 +72,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if self.try_structurally_resolve_type(expr.span, ty).is_never() && self.expr_guaranteed_to_constitute_read_for_never(expr) { - if let Some(_) = self.typeck_results.borrow().adjustments().get(expr.hir_id) { + if let Some(adjustments) = self.typeck_results.borrow().adjustments().get(expr.hir_id) { let reported = self.dcx().span_delayed_bug( expr.span, "expression with never type wound up being adjusted", ); - return Ty::new_error(self.tcx(), reported); + + return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &adjustments[..] { + target.to_owned() + } else { + Ty::new_error(self.tcx(), reported) + }; } let adj_ty = self.next_ty_var(expr.span); @@ -328,6 +334,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Assignment does call `drop_in_place`, though, but its safety // requirements are not the same. ExprKind::AddrOf(..) | hir::ExprKind::Field(..) => false, + + // Place-preserving expressions only constitute reads if their + // parent expression constitutes a read. + ExprKind::Type(..) | ExprKind::UnsafeBinderCast(..) => { + self.expr_guaranteed_to_constitute_read_for_never(expr) + } + ExprKind::Assign(lhs, _, _) => { // Only the LHS does not constitute a read expr.hir_id != lhs.hir_id @@ -352,7 +365,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ExprKind::Binary(_, _, _) | ExprKind::Unary(_, _) | ExprKind::Cast(_, _) - | ExprKind::Type(_, _) | ExprKind::DropTemps(_) | ExprKind::If(_, _, _) | ExprKind::Closure(_) @@ -402,6 +414,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. }) => true, + hir::Node::Pat(_) => { + self.dcx().span_delayed_bug(expr.span, "place expr not allowed in pattern"); + true + } + // These nodes do not have direct sub-exprs. hir::Node::Param(_) | hir::Node::Item(_) @@ -414,7 +431,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | hir::Node::Ty(_) | hir::Node::AssocItemConstraint(_) | hir::Node::TraitRef(_) - | hir::Node::Pat(_) | hir::Node::PatField(_) | hir::Node::LetStmt(_) | hir::Node::Synthetic @@ -559,7 +575,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_expr_index(base, idx, expr, brackets_span) } ExprKind::Yield(value, _) => self.check_expr_yield(value, expr), - hir::ExprKind::Err(guar) => Ty::new_error(tcx, guar), + ExprKind::UnsafeBinderCast(kind, expr, ty) => { + self.check_expr_unsafe_binder_cast(kind, expr, ty, expected) + } + ExprKind::Err(guar) => Ty::new_error(tcx, guar), } } @@ -628,7 +647,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &'tcx hir::Expr<'tcx>, ) -> Ty<'tcx> { let hint = expected.only_has_type(self).map_or(NoExpectation, |ty| { - match ty.kind() { + match self.try_structurally_resolve_type(expr.span, ty).kind() { ty::Ref(_, ty, _) | ty::RawPtr(ty, _) => { if oprnd.is_syntactic_place_expr() { // Places may legitimately have unsized types. @@ -723,7 +742,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.suggest_assoc_method_call(segs); let e = self.dcx().span_delayed_bug(qpath.span(), "`Res::Err` but no error emitted"); - self.set_tainted_by_errors(e); Ty::new_error(tcx, e) } Res::Def(DefKind::Variant, _) => { @@ -1293,7 +1311,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let cond_diverges = self.diverges.get(); self.diverges.set(Diverges::Maybe); - let expected = orig_expected.adjust_for_branches(self); + let expected = orig_expected.try_structurally_resolve_and_adjust_for_branches(self, sp); let then_ty = self.check_expr_with_expectation(then_expr, expected); let then_diverges = self.diverges.get(); self.diverges.set(Diverges::Maybe); @@ -1354,8 +1372,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs: &'tcx hir::Expr<'tcx>, span: Span, ) -> Ty<'tcx> { - let expected_ty = expected.coercion_target_type(self, expr.span); - if expected_ty == self.tcx.types.bool { + let expected_ty = expected.only_has_type(self); + if expected_ty == Some(self.tcx.types.bool) { let guar = self.expr_assign_expected_bool_error(expr, lhs, rhs, span); return Ty::new_error(self.tcx, guar); } @@ -1630,6 +1648,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + fn check_expr_unsafe_binder_cast( + &self, + _kind: hir::UnsafeBinderCastKind, + expr: &'tcx hir::Expr<'tcx>, + _hir_ty: Option<&'tcx hir::Ty<'tcx>>, + _expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + let guar = + self.dcx().struct_span_err(expr.span, "unsafe binders are not yet implemented").emit(); + Ty::new_error(self.tcx, guar) + } + fn check_expr_array( &self, args: &'tcx [hir::Expr<'tcx>], @@ -1639,7 +1669,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let element_ty = if !args.is_empty() { let coerce_to = expected .to_option(self) - .and_then(|uty| match *uty.kind() { + .and_then(|uty| match *self.try_structurally_resolve_type(expr.span, uty).kind() { ty::Array(ty, _) | ty::Slice(ty) => Some(ty), _ => None, }) @@ -1855,11 +1885,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_expr_struct( &self, - expr: &hir::Expr<'_>, + expr: &hir::Expr<'tcx>, expected: Expectation<'tcx>, - qpath: &QPath<'tcx>, + qpath: &'tcx QPath<'tcx>, fields: &'tcx [hir::ExprField<'tcx>], - base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, + base_expr: &'tcx hir::StructTailExpr<'tcx>, ) -> Ty<'tcx> { // Find the relevant variant let (variant, adt_ty) = match self.check_struct_path(qpath, expr.hir_id) { @@ -1899,7 +1929,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, variant: &'tcx ty::VariantDef, hir_fields: &'tcx [hir::ExprField<'tcx>], - base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, + base_expr: &'tcx hir::StructTailExpr<'tcx>, ) { let tcx = self.tcx; @@ -2023,13 +2053,90 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // the fields with the base_expr. This could cause us to hit errors later // when certain fields are assumed to exist that in fact do not. if error_happened { - if let Some(base_expr) = base_expr { + if let hir::StructTailExpr::Base(base_expr) = base_expr { self.check_expr(base_expr); } return; } - if let Some(base_expr) = base_expr { + if let hir::StructTailExpr::DefaultFields(span) = *base_expr { + let mut missing_mandatory_fields = Vec::new(); + let mut missing_optional_fields = Vec::new(); + for f in &variant.fields { + let ident = self.tcx.adjust_ident(f.ident(self.tcx), variant.def_id); + if let Some(_) = remaining_fields.remove(&ident) { + if f.value.is_none() { + missing_mandatory_fields.push(ident); + } else { + missing_optional_fields.push(ident); + } + } + } + if !self.tcx.features().default_field_values() { + self.dcx().emit_err(BaseExpressionDoubleDot { + span: span.shrink_to_hi(), + // We only mention enabling the feature if this is a nightly rustc *and* the + // expression would make sense with the feature enabled. + default_field_values: if self.tcx.sess.is_nightly_build() + && missing_mandatory_fields.is_empty() + && !missing_optional_fields.is_empty() + { + Some(BaseExpressionDoubleDotEnableDefaultFieldValues) + } else { + None + }, + add_expr: if !missing_mandatory_fields.is_empty() + || !missing_optional_fields.is_empty() + { + Some(BaseExpressionDoubleDotAddExpr { span: span.shrink_to_hi() }) + } else { + None + }, + remove_dots: if missing_mandatory_fields.is_empty() + && missing_optional_fields.is_empty() + { + Some(BaseExpressionDoubleDotRemove { span }) + } else { + None + }, + }); + return; + } + if !missing_mandatory_fields.is_empty() { + let s = pluralize!(missing_mandatory_fields.len()); + let fields: Vec<_> = + missing_mandatory_fields.iter().map(|f| format!("`{f}`")).collect(); + let fields = match &fields[..] { + [] => unreachable!(), + [only] => only.to_string(), + [start @ .., last] => format!("{} and {last}", start.join(", ")), + }; + self.dcx() + .struct_span_err( + span.shrink_to_hi(), + format!("missing mandatory field{s} {fields}"), + ) + .emit(); + return; + } + let fru_tys = match adt_ty.kind() { + ty::Adt(adt, args) if adt.is_struct() => variant + .fields + .iter() + .map(|f| self.normalize(span, f.ty(self.tcx, args))) + .collect(), + ty::Adt(adt, args) if adt.is_enum() => variant + .fields + .iter() + .map(|f| self.normalize(span, f.ty(self.tcx, args))) + .collect(), + _ => { + self.dcx().emit_err(FunctionalRecordUpdateOnNonStruct { span }); + return; + } + }; + self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr.hir_id, fru_tys); + } else if let hir::StructTailExpr::Base(base_expr) = base_expr { // FIXME: We are currently creating two branches here in order to maintain // consistency. But they should be merged as much as possible. let fru_tys = if self.tcx.features().type_changing_struct_update() { @@ -2161,12 +2268,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_struct_fields_on_error( &self, fields: &'tcx [hir::ExprField<'tcx>], - base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, + base_expr: &'tcx hir::StructTailExpr<'tcx>, ) { for field in fields { self.check_expr(field.expr); } - if let Some(base) = *base_expr { + if let hir::StructTailExpr::Base(base) = *base_expr { self.check_expr(base); } } @@ -2611,33 +2718,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None } - // Check field access expressions + /// Check field access expressions, this works for both structs and tuples. + /// Returns the Ty of the field. + /// + /// ```not_rust + /// base.field + /// ^^^^^^^^^^ expr + /// ^^^^ base + /// ^^^^^ field + /// ``` fn check_expr_field( &self, expr: &'tcx hir::Expr<'tcx>, base: &'tcx hir::Expr<'tcx>, field: Ident, + // The expected type hint of the field. expected: Expectation<'tcx>, ) -> Ty<'tcx> { debug!("check_field(expr: {:?}, base: {:?}, field: {:?})", expr, base, field); let base_ty = self.check_expr(base); let base_ty = self.structurally_resolve_type(base.span, base_ty); + + // Whether we are trying to access a private field. Used for error reporting. let mut private_candidate = None; + + // Field expressions automatically deref let mut autoderef = self.autoderef(expr.span, base_ty); while let Some((deref_base_ty, _)) = autoderef.next() { debug!("deref_base_ty: {:?}", deref_base_ty); match deref_base_ty.kind() { ty::Adt(base_def, args) if !base_def.is_enum() => { debug!("struct named {:?}", deref_base_ty); - let body_hir_id = self.tcx.local_def_id_to_hir_id(self.body_id); - let (ident, def_scope) = - self.tcx.adjust_ident_and_get_scope(field, base_def.did(), body_hir_id); - // we don't care to report errors for a struct if the struct itself is tainted if let Err(guar) = base_def.non_enum_variant().has_errors() { return Ty::new_error(self.tcx(), guar); } + let fn_body_hir_id = self.tcx.local_def_id_to_hir_id(self.body_id); + let (ident, def_scope) = + self.tcx.adjust_ident_and_get_scope(field, base_def.did(), fn_body_hir_id); + if let Some((idx, field)) = self.find_adt_field(*base_def, ident) { self.write_field_index(expr.hir_id, idx); @@ -2671,6 +2791,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => {} } } + // We failed to check the expression, report an error. + + // Emits an error if we deref an infer variable, like calling `.field` on a base type of &_. self.structurally_resolve_type(autoderef.span(), autoderef.final_ty(false)); if let Some((adjustments, did)) = private_candidate { @@ -2695,6 +2818,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr.hir_id, expected.only_has_type(self), ) { + // If taking a method instead of calling it self.ban_take_value_of_method(expr, base_ty, field) } else if !base_ty.is_primitive_ty() { self.ban_nonexisting_field(field, base, expr, base_ty) @@ -2978,7 +3102,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.help("methods are immutable and cannot be assigned to"); } - err.emit() + // See `StashKey::GenericInFieldExpr` for more info + self.dcx().try_steal_replace_and_emit_err(field.span, StashKey::GenericInFieldExpr, err) } fn point_at_param_definition(&self, err: &mut Diag<'_>, param: ty::ParamTy) { diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 774d00edea0..ecbae6ac72f 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -341,6 +341,10 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx self.walk_expr(subexpr)?; } + hir::ExprKind::UnsafeBinderCast(_, subexpr, _) => { + self.walk_expr(subexpr)?; + } + hir::ExprKind::Unary(hir::UnOp::Deref, base) => { // *base self.walk_expr(base)?; @@ -686,7 +690,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx fn walk_struct_expr<'hir>( &self, fields: &[hir::ExprField<'_>], - opt_with: &Option<&'hir hir::Expr<'_>>, + opt_with: &hir::StructTailExpr<'hir>, ) -> Result<(), Cx::Error> { // Consume the expressions supplying values for each field. for field in fields { @@ -702,8 +706,8 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx } let with_expr = match *opt_with { - Some(w) => &*w, - None => { + hir::StructTailExpr::Base(w) => &*w, + hir::StructTailExpr::DefaultFields(_) | hir::StructTailExpr::None => { return Ok(()); } }; @@ -1360,7 +1364,10 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx self.cat_res(expr.hir_id, expr.span, expr_ty, res) } + // both type ascription and unsafe binder casts don't affect + // the place-ness of the subexpression. hir::ExprKind::Type(e, _) => self.cat_expr(e), + hir::ExprKind::UnsafeBinderCast(_, e, _) => self.cat_expr(e), hir::ExprKind::AddrOf(..) | hir::ExprKind::Call(..) diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index ddd146fe785..c128485d93e 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -613,19 +613,16 @@ impl<'tcx> AnnotateUnitFallbackVisitor<'_, 'tcx> { if arg_segment.args.is_none() && let Some(all_args) = self.fcx.typeck_results.borrow().node_args_opt(id) && let generics = self.fcx.tcx.generics_of(def_id) - && let args = &all_args[generics.parent_count..] + && let args = all_args[generics.parent_count..].iter().zip(&generics.own_params) // We can't turbofish consts :( - && args.iter().all(|arg| matches!(arg.unpack(), ty::GenericArgKind::Type(_) | ty::GenericArgKind::Lifetime(_))) + && args.clone().all(|(_, param)| matches!(param.kind, ty::GenericParamDefKind::Type { .. } | ty::GenericParamDefKind::Lifetime)) { - let n_tys = args - .iter() - .filter(|arg| matches!(arg.unpack(), ty::GenericArgKind::Type(_))) - .count(); - for (idx, arg) in args - .iter() - .filter(|arg| matches!(arg.unpack(), ty::GenericArgKind::Type(_))) - .enumerate() - { + // We filter out APITs, which are not turbofished. + let non_apit_type_args = args.filter(|(_, param)| { + matches!(param.kind, ty::GenericParamDefKind::Type { synthetic: false, .. }) + }); + let n_tys = non_apit_type_args.clone().count(); + for (idx, (arg, _)) in non_apit_type_args.enumerate() { if let Some(ty) = arg.as_type() && let Some(vid) = self.fcx.root_vid(ty) && self.reachable_vids.contains(&vid) @@ -765,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), @@ -785,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), @@ -816,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 863be3bdcb2..0dacfc9b7bf 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -4,11 +4,11 @@ use std::slice; use rustc_abi::FieldIdx; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey}; -use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; -use rustc_hir::{ExprKind, GenericArg, HirId, Node, QPath}; +use rustc_hir::{self as hir, ExprKind, GenericArg, HirId, Node, QPath, intravisit}; use rustc_hir_analysis::hir_ty_lowering::errors::GenericsArgsErrExtend; use rustc_hir_analysis::hir_ty_lowering::generics::{ check_generic_arg_count_for_call, lower_generic_args, @@ -25,7 +25,7 @@ use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt}; use rustc_middle::ty::{ self, AdtKind, CanonicalUserType, GenericArgKind, GenericArgsRef, GenericParamDefKind, - IsIdentity, Ty, TyCtxt, UserArgs, UserSelfTy, UserType, + IsIdentity, Ty, TyCtxt, UserArgs, UserSelfTy, }; use rustc_middle::{bug, span_bug}; use rustc_session::lint; @@ -216,11 +216,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("fcx {}", self.tag()); if Self::can_contain_user_lifetime_bounds((args, user_self_ty)) { - let canonicalized = - self.canonicalize_user_type_annotation(UserType::TypeOf(def_id, UserArgs { - args, - user_self_ty, - })); + let canonicalized = self.canonicalize_user_type_annotation(ty::UserType::new( + ty::UserTypeKind::TypeOf(def_id, UserArgs { args, user_self_ty }), + )); debug!(?canonicalized); self.write_user_type_annotation(hir_id, canonicalized); } @@ -462,13 +460,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { LoweredTy::from_raw(self, hir_ty.span, ty) } + /// Walk a `hir_ty` and collect any clauses that may have come from a type + /// within the `hir_ty`. These clauses will be canonicalized with a user type + /// annotation so that we can enforce these bounds in borrowck, too. + pub(crate) fn collect_impl_trait_clauses_from_hir_ty( + &self, + hir_ty: &'tcx hir::Ty<'tcx>, + ) -> ty::Clauses<'tcx> { + struct CollectClauses<'a, 'tcx> { + clauses: Vec<ty::Clause<'tcx>>, + fcx: &'a FnCtxt<'a, 'tcx>, + } + + impl<'tcx> intravisit::Visitor<'tcx> for CollectClauses<'_, 'tcx> { + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { + if let Some(clauses) = self.fcx.trait_ascriptions.borrow().get(&ty.hir_id.local_id) + { + self.clauses.extend(clauses.iter().cloned()); + } + intravisit::walk_ty(self, ty) + } + } + + let mut clauses = CollectClauses { clauses: vec![], fcx: self }; + clauses.visit_ty(hir_ty); + self.tcx.mk_clauses(&clauses.clauses) + } + #[instrument(level = "debug", skip_all)] - pub(crate) fn lower_ty_saving_user_provided_ty(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { + pub(crate) fn lower_ty_saving_user_provided_ty(&self, hir_ty: &'tcx hir::Ty<'tcx>) -> Ty<'tcx> { let ty = self.lower_ty(hir_ty); debug!(?ty); if Self::can_contain_user_lifetime_bounds(ty.raw) { - let c_ty = self.canonicalize_response(UserType::Ty(ty.raw)); + let c_ty = self.canonicalize_response(ty::UserType::new(ty::UserTypeKind::Ty(ty.raw))); debug!(?c_ty); self.typeck_results.borrow_mut().user_provided_types_mut().insert(hir_ty.hir_id, c_ty); } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 30c838b74af..6b1cceefbee 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -903,6 +903,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + let detect_dotdot = |err: &mut Diag<'_>, ty: Ty<'_>, expr: &hir::Expr<'_>| { + if let ty::Adt(adt, _) = ty.kind() + && self.tcx().lang_items().get(hir::LangItem::RangeFull) == Some(adt.did()) + && let hir::ExprKind::Struct( + hir::QPath::LangItem(hir::LangItem::RangeFull, _), + [], + _, + ) = expr.kind + { + // We have `Foo(a, .., c)`, where the user might be trying to use the "rest" syntax + // from default field values, which is not supported on tuples. + let explanation = if self.tcx.features().default_field_values() { + "this is only supported on non-tuple struct literals" + } else if self.tcx.sess.is_nightly_build() { + "this is only supported on non-tuple struct literals when \ + `#![feature(default_field_values)]` is enabled" + } else { + "this is not supported" + }; + let msg = format!( + "you might have meant to use `..` to skip providing a value for \ + expected fields, but {explanation}; it is instead interpreted as a \ + `std::ops::RangeFull` literal", + ); + err.span_help(expr.span, msg); + } + }; + let mut reported = None; errors.retain(|error| { let Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(e))) = @@ -936,18 +964,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // can be collated pretty easily if needed. // Next special case: if there is only one "Incompatible" error, just emit that - if let [ + if let &[ Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(err))), ] = &errors[..] { - let (formal_ty, expected_ty) = formal_and_expected_inputs[*expected_idx]; - let (provided_ty, provided_arg_span) = provided_arg_tys[*provided_idx]; + let (formal_ty, expected_ty) = formal_and_expected_inputs[expected_idx]; + let (provided_ty, provided_arg_span) = provided_arg_tys[provided_idx]; let trace = mk_trace(provided_arg_span, (formal_ty, expected_ty), provided_ty); - let mut err = - self.err_ctxt().report_and_explain_type_error(trace, self.param_env, *err); + let mut err = self.err_ctxt().report_and_explain_type_error(trace, self.param_env, err); self.emit_coerce_suggestions( &mut err, - provided_args[*provided_idx], + provided_args[provided_idx], provided_ty, Expectation::rvalue_hint(self, expected_ty) .only_has_type(self) @@ -982,7 +1009,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.suggest_ptr_null_mut( expected_ty, provided_ty, - provided_args[*provided_idx], + provided_args[provided_idx], &mut err, ); @@ -992,7 +1019,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_ident, expected_ty, provided_ty, - provided_args[*provided_idx], + provided_args[provided_idx], is_method, ); @@ -1010,6 +1037,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tuple_arguments, ); suggest_confusable(&mut err); + detect_dotdot(&mut err, provided_ty, provided_args[provided_idx]); return err.emit(); } @@ -1134,6 +1162,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None, None, ); + detect_dotdot(&mut err, provided_ty, provided_args[provided_idx]); } Error::Extra(arg_idx) => { let (provided_ty, provided_span) = provided_arg_tys[arg_idx]; @@ -1217,6 +1246,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; prev_extra_idx = Some(arg_idx.index()) } + detect_dotdot(&mut err, provided_ty, provided_args[arg_idx]); } Error::Missing(expected_idx) => { // If there are multiple missing arguments adjacent to each other, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index aacdcf027b6..b9011e89f04 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -10,10 +10,11 @@ use std::ops::Deref; use hir::def_id::CRATE_DEF_ID; use rustc_errors::DiagCtxtHandle; -use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::{self as hir, HirId, ItemLocalMap}; use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, RegionInferReason}; 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; @@ -114,6 +115,12 @@ pub(crate) struct FnCtxt<'a, 'tcx> { pub(super) diverging_fallback_behavior: DivergingFallbackBehavior, pub(super) diverging_block_behavior: DivergingBlockBehavior, + + /// Clauses that we lowered as part of the `impl_trait_in_bindings` feature. + /// + /// These are stored here so we may collect them when canonicalizing user + /// type ascriptions later. + pub(super) trait_ascriptions: RefCell<ItemLocalMap<Vec<ty::Clause<'tcx>>>>, } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -141,6 +148,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fallback_has_occurred: Cell::new(false), diverging_fallback_behavior, diverging_block_behavior, + trait_ascriptions: Default::default(), } } @@ -252,6 +260,30 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> { } } + fn register_trait_ascription_bounds( + &self, + bounds: Vec<(ty::Clause<'tcx>, Span)>, + hir_id: HirId, + _span: Span, + ) { + for (clause, span) in bounds { + if clause.has_escaping_bound_vars() { + self.dcx().span_delayed_bug(span, "clause should have no escaping bound vars"); + continue; + } + + self.trait_ascriptions.borrow_mut().entry(hir_id.local_id).or_default().push(clause); + + let clause = self.normalize(span, clause); + self.register_predicate(Obligation::new( + self.tcx, + self.misc(span), + self.param_env, + clause, + )); + } + } + fn probe_ty_param_bounds( &self, _: Span, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index ddcd90a2a9d..21b1768ae6f 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; @@ -35,7 +35,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 +2647,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 +2860,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 +2901,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/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs index f427b0b805e..48fd5f1f982 100644 --- a/compiler/rustc_hir_typeck/src/gather_locals.rs +++ b/compiler/rustc_hir_typeck/src/gather_locals.rs @@ -2,7 +2,7 @@ use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{HirId, PatKind}; use rustc_infer::traits::ObligationCauseCode; -use rustc_middle::ty::{Ty, UserType}; +use rustc_middle::ty::{self, Ty}; use rustc_span::Span; use rustc_span::def_id::LocalDefId; use tracing::debug; @@ -92,7 +92,12 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { Some(ref ty) => { let o_ty = self.fcx.lower_ty(ty); - let c_ty = self.fcx.infcx.canonicalize_user_type_annotation(UserType::Ty(o_ty.raw)); + let c_ty = self.fcx.infcx.canonicalize_user_type_annotation( + ty::UserType::new_with_bounds( + ty::UserTypeKind::Ty(o_ty.raw), + self.fcx.collect_impl_trait_clauses_from_hir_ty(ty), + ), + ); debug!("visit_local: ty.hir_id={:?} o_ty={:?} c_ty={:?}", ty.hir_id, o_ty, c_ty); self.fcx .typeck_results diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index b31ee1a55d6..ef431c852e9 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -17,7 +17,6 @@ use rustc_middle::ty::adjustment::{ use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::{ self, GenericArgs, GenericArgsRef, GenericParamDefKind, Ty, TyCtxt, TypeVisitableExt, UserArgs, - UserType, }; use rustc_middle::{bug, span_bug}; use rustc_span::{DUMMY_SP, Span}; @@ -491,9 +490,8 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { user_self_ty: None, // not relevant here }; - self.fcx.canonicalize_user_type_annotation(UserType::TypeOf( - pick.item.def_id, - user_args, + self.fcx.canonicalize_user_type_annotation(ty::UserType::new( + ty::UserTypeKind::TypeOf(pick.item.def_id, user_args), )) }); diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 039c117c099..3b377076545 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -79,12 +79,6 @@ pub(crate) struct ProbeContext<'a, 'tcx> { /// used for error reporting static_candidates: RefCell<Vec<CandidateSource>>, - /// Collects near misses when trait bounds for type parameters are unsatisfied and is only used - /// for error reporting - unsatisfied_predicates: RefCell< - Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>)>, - >, - scope_expr_id: HirId, /// Is this probe being done for a diagnostic? This will skip some error reporting @@ -109,7 +103,7 @@ pub(crate) struct Candidate<'tcx> { #[derive(Debug, Clone)] pub(crate) enum CandidateKind<'tcx> { - InherentImplCandidate(DefId), + InherentImplCandidate { impl_def_id: DefId, receiver_steps: usize }, ObjectCandidate(ty::PolyTraitRef<'tcx>), TraitCandidate(ty::PolyTraitRef<'tcx>), WhereClauseCandidate(ty::PolyTraitRef<'tcx>), @@ -162,6 +156,52 @@ impl AutorefOrPtrAdjustment { } } +/// Extra information required only for error reporting. +#[derive(Debug)] +struct PickDiagHints<'a, 'tcx> { + /// Unstable candidates alongside the stable ones. + unstable_candidates: Option<Vec<(Candidate<'tcx>, Symbol)>>, + + /// Collects near misses when trait bounds for type parameters are unsatisfied and is only used + /// for error reporting + unsatisfied_predicates: &'a mut Vec<( + ty::Predicate<'tcx>, + Option<ty::Predicate<'tcx>>, + Option<ObligationCause<'tcx>>, + )>, +} + +/// Criteria to apply when searching for a given Pick. This is used during +/// the search for potentially shadowed methods to ensure we don't search +/// more candidates than strictly necessary. +#[derive(Debug)] +struct PickConstraintsForShadowed { + autoderefs: usize, + receiver_steps: Option<usize>, + def_id: DefId, +} + +impl PickConstraintsForShadowed { + fn may_shadow_based_on_autoderefs(&self, autoderefs: usize) -> bool { + autoderefs == self.autoderefs + } + + fn candidate_may_shadow(&self, candidate: &Candidate<'_>) -> bool { + // An item never shadows itself + candidate.item.def_id != self.def_id + // and we're only concerned about inherent impls doing the shadowing. + // Shadowing can only occur if the shadowed is further along + // the Receiver dereferencing chain than the shadowed. + && match candidate.kind { + CandidateKind::InherentImplCandidate { receiver_steps, .. } => match self.receiver_steps { + Some(shadowed_receiver_steps) => receiver_steps > shadowed_receiver_steps, + _ => false + }, + _ => false + } + } +} + #[derive(Debug, Clone)] pub(crate) struct Pick<'tcx> { pub item: ty::AssocItem, @@ -181,6 +221,11 @@ pub(crate) struct Pick<'tcx> { /// Unstable candidates alongside the stable ones. unstable_candidates: Vec<(Candidate<'tcx>, Symbol)>, + + /// Number of jumps along the `Receiver::Target` chain we followed + /// to identify this method. Used only for deshadowing errors. + /// Only applies for inherent impls. + pub receiver_steps: Option<usize>, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -366,6 +411,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { autoderefs: 0, from_unsafe_deref: false, unsize: false, + reachable_via_deref: true, }]), opt_bad_ty: None, reached_recursion_limit: false, @@ -516,47 +562,93 @@ fn method_autoderef_steps<'tcx>( let (ref infcx, goal, inference_vars) = tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal); let ParamEnvAnd { param_env, value: self_ty } = goal; - let mut autoderef = + // If arbitrary self types is not enabled, we follow the chain of + // `Deref<Target=T>`. If arbitrary self types is enabled, we instead + // follow the chain of `Receiver<Target=T>`, but we also record whether + // such types are reachable by following the (potentially shorter) + // chain of `Deref<Target=T>`. We will use the first list when finding + // potentially relevant function implementations (e.g. relevant impl blocks) + // but the second list when determining types that the receiver may be + // converted to, in order to find out which of those methods might actually + // be callable. + let mut autoderef_via_deref = Autoderef::new(infcx, param_env, hir::def_id::CRATE_DEF_ID, DUMMY_SP, self_ty) .include_raw_pointers() .silence_errors(); - let mut reached_raw_pointer = false; - let mut steps: Vec<_> = autoderef - .by_ref() - .map(|(ty, d)| { - let step = CandidateStep { - self_ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, ty), - autoderefs: d, - from_unsafe_deref: reached_raw_pointer, - unsize: false, - }; - if let ty::RawPtr(_, _) = ty.kind() { - // all the subsequent steps will be from_unsafe_deref - reached_raw_pointer = true; - } - step - }) - .collect(); - let final_ty = autoderef.final_ty(true); + let mut reached_raw_pointer = false; + let arbitrary_self_types_enabled = + tcx.features().arbitrary_self_types() || tcx.features().arbitrary_self_types_pointers(); + let (mut steps, reached_recursion_limit): (Vec<_>, bool) = if arbitrary_self_types_enabled { + let reachable_via_deref = + autoderef_via_deref.by_ref().map(|_| true).chain(std::iter::repeat(false)); + + let mut autoderef_via_receiver = + Autoderef::new(infcx, param_env, hir::def_id::CRATE_DEF_ID, DUMMY_SP, self_ty) + .include_raw_pointers() + .use_receiver_trait() + .silence_errors(); + let steps = autoderef_via_receiver + .by_ref() + .zip(reachable_via_deref) + .map(|((ty, d), reachable_via_deref)| { + let step = CandidateStep { + self_ty: infcx + .make_query_response_ignoring_pending_obligations(inference_vars, ty), + autoderefs: d, + from_unsafe_deref: reached_raw_pointer, + unsize: false, + reachable_via_deref, + }; + if ty.is_unsafe_ptr() { + // all the subsequent steps will be from_unsafe_deref + reached_raw_pointer = true; + } + step + }) + .collect(); + (steps, autoderef_via_receiver.reached_recursion_limit()) + } else { + let steps = autoderef_via_deref + .by_ref() + .map(|(ty, d)| { + let step = CandidateStep { + self_ty: infcx + .make_query_response_ignoring_pending_obligations(inference_vars, ty), + autoderefs: d, + from_unsafe_deref: reached_raw_pointer, + unsize: false, + reachable_via_deref: true, + }; + if ty.is_unsafe_ptr() { + // all the subsequent steps will be from_unsafe_deref + reached_raw_pointer = true; + } + step + }) + .collect(); + (steps, autoderef_via_deref.reached_recursion_limit()) + }; + let final_ty = autoderef_via_deref.final_ty(true); let opt_bad_ty = match final_ty.kind() { ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy { reached_raw_pointer, ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, final_ty), }), ty::Array(elem_ty, _) => { - let dereferences = steps.len() - 1; - + let autoderefs = steps.iter().filter(|s| s.reachable_via_deref).count() - 1; steps.push(CandidateStep { self_ty: infcx.make_query_response_ignoring_pending_obligations( inference_vars, Ty::new_slice(infcx.tcx, *elem_ty), ), - autoderefs: dereferences, + autoderefs, // this could be from an unsafe deref if we had // a *mut/const [T; N] from_unsafe_deref: reached_raw_pointer, unsize: true, + reachable_via_deref: true, // this is always the final type from + // autoderef_via_deref }); None @@ -569,7 +661,7 @@ fn method_autoderef_steps<'tcx>( MethodAutoderefStepsResult { steps: tcx.arena.alloc_from_iter(steps), opt_bad_ty: opt_bad_ty.map(|ty| &*tcx.arena.alloc(ty)), - reached_recursion_limit: autoderef.reached_recursion_limit(), + reached_recursion_limit, } } @@ -600,7 +692,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { private_candidates: Vec::new(), private_candidate: Cell::new(None), static_candidates: RefCell::new(Vec::new()), - unsatisfied_predicates: RefCell::new(Vec::new()), scope_expr_id, is_suggestion, } @@ -613,7 +704,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.private_candidates.clear(); self.private_candidate.set(None); self.static_candidates.borrow_mut().clear(); - self.unsatisfied_predicates.borrow_mut().clear(); } /// When we're looking up a method by path (UFCS), we relate the receiver @@ -652,12 +742,16 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn assemble_inherent_candidates(&mut self) { for step in self.steps.iter() { - self.assemble_probe(&step.self_ty); + self.assemble_probe(&step.self_ty, step.autoderefs); } } #[instrument(level = "debug", skip(self))] - fn assemble_probe(&mut self, self_ty: &Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>) { + fn assemble_probe( + &mut self, + self_ty: &Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>, + receiver_steps: usize, + ) { let raw_self_ty = self_ty.value.value; match *raw_self_ty.kind() { ty::Dynamic(data, ..) if let Some(p) = data.principal() => { @@ -682,22 +776,31 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.fcx.instantiate_canonical(self.span, self_ty); self.assemble_inherent_candidates_from_object(generalized_self_ty); - self.assemble_inherent_impl_candidates_for_type(p.def_id()); + self.assemble_inherent_impl_candidates_for_type(p.def_id(), receiver_steps); if self.tcx.has_attr(p.def_id(), sym::rustc_has_incoherent_inherent_impls) { - self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty); + self.assemble_inherent_candidates_for_incoherent_ty( + raw_self_ty, + receiver_steps, + ); } } ty::Adt(def, _) => { let def_id = def.did(); - self.assemble_inherent_impl_candidates_for_type(def_id); + self.assemble_inherent_impl_candidates_for_type(def_id, receiver_steps); if self.tcx.has_attr(def_id, sym::rustc_has_incoherent_inherent_impls) { - self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty); + self.assemble_inherent_candidates_for_incoherent_ty( + raw_self_ty, + receiver_steps, + ); } } ty::Foreign(did) => { - self.assemble_inherent_impl_candidates_for_type(did); + self.assemble_inherent_impl_candidates_for_type(did, receiver_steps); if self.tcx.has_attr(did, sym::rustc_has_incoherent_inherent_impls) { - self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty); + self.assemble_inherent_candidates_for_incoherent_ty( + raw_self_ty, + receiver_steps, + ); } } ty::Param(p) => { @@ -714,29 +817,35 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { | ty::RawPtr(_, _) | ty::Ref(..) | ty::Never - | ty::Tuple(..) => self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty), + | ty::Tuple(..) => { + self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty, receiver_steps) + } _ => {} } } - fn assemble_inherent_candidates_for_incoherent_ty(&mut self, self_ty: Ty<'tcx>) { + fn assemble_inherent_candidates_for_incoherent_ty( + &mut self, + self_ty: Ty<'tcx>, + receiver_steps: usize, + ) { let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::InstantiateWithInfer) else { bug!("unexpected incoherent type: {:?}", self_ty) }; for &impl_def_id in self.tcx.incoherent_impls(simp).into_iter() { - self.assemble_inherent_impl_probe(impl_def_id); + self.assemble_inherent_impl_probe(impl_def_id, receiver_steps); } } - fn assemble_inherent_impl_candidates_for_type(&mut self, def_id: DefId) { + fn assemble_inherent_impl_candidates_for_type(&mut self, def_id: DefId, receiver_steps: usize) { let impl_def_ids = self.tcx.at(self.span).inherent_impls(def_id).into_iter(); for &impl_def_id in impl_def_ids { - self.assemble_inherent_impl_probe(impl_def_id); + self.assemble_inherent_impl_probe(impl_def_id, receiver_steps); } } #[instrument(level = "debug", skip(self))] - fn assemble_inherent_impl_probe(&mut self, impl_def_id: DefId) { + fn assemble_inherent_impl_probe(&mut self, impl_def_id: DefId, receiver_steps: usize) { if !self.impl_dups.insert(impl_def_id) { return; // already visited } @@ -750,7 +859,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.push_candidate( Candidate { item, - kind: InherentImplCandidate(impl_def_id), + kind: InherentImplCandidate { impl_def_id, receiver_steps }, import_ids: smallvec![], }, true, @@ -989,7 +1098,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn pick(mut self) -> PickResult<'tcx> { assert!(self.method_name.is_some()); - if let Some(r) = self.pick_core() { + let mut unsatisfied_predicates = Vec::new(); + + if let Some(r) = self.pick_core(&mut unsatisfied_predicates) { return r; } @@ -1009,7 +1120,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let static_candidates = std::mem::take(self.static_candidates.get_mut()); let private_candidate = self.private_candidate.take(); - let unsatisfied_predicates = std::mem::take(self.unsatisfied_predicates.get_mut()); // things failed, so lets look at all traits, for diagnostic purposes now: self.reset(); @@ -1019,7 +1129,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.assemble_extension_candidates_for_all_traits(); - let out_of_scope_traits = match self.pick_core() { + let out_of_scope_traits = match self.pick_core(&mut Vec::new()) { Some(Ok(p)) => vec![p.item.container_id(self.tcx)], Some(Err(MethodError::Ambiguity(v))) => v .into_iter() @@ -1054,17 +1164,48 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { })) } - fn pick_core(&self) -> Option<PickResult<'tcx>> { + fn pick_core( + &self, + unsatisfied_predicates: &mut Vec<( + ty::Predicate<'tcx>, + Option<ty::Predicate<'tcx>>, + Option<ObligationCause<'tcx>>, + )>, + ) -> Option<PickResult<'tcx>> { // Pick stable methods only first, and consider unstable candidates if not found. - self.pick_all_method(Some(&mut vec![])).or_else(|| self.pick_all_method(None)) + self.pick_all_method(&mut PickDiagHints { + // This first cycle, maintain a list of unstable candidates which + // we encounter. This will end up in the Pick for diagnostics. + unstable_candidates: Some(Vec::new()), + // Contribute to the list of unsatisfied predicates which may + // also be used for diagnostics. + unsatisfied_predicates, + }) + .or_else(|| { + self.pick_all_method(&mut PickDiagHints { + // On the second search, don't provide a special list of unstable + // candidates. This indicates to the picking code that it should + // in fact include such unstable candidates in the actual + // search. + unstable_candidates: None, + // And there's no need to duplicate ourselves in the + // unsatisifed predicates list. Provide a throwaway list. + unsatisfied_predicates: &mut Vec::new(), + }) + }) } - fn pick_all_method( + fn pick_all_method<'b>( &self, - mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, + pick_diag_hints: &mut PickDiagHints<'b, 'tcx>, ) -> Option<PickResult<'tcx>> { + let track_unstable_candidates = pick_diag_hints.unstable_candidates.is_some(); self.steps .iter() + // At this point we're considering the types to which the receiver can be converted, + // so we want to follow the `Deref` chain not the `Receiver` chain. Filter out + // steps which can only be reached by following the (longer) `Receiver` chain. + .filter(|step| step.reachable_via_deref) .filter(|step| { debug!("pick_all_method: step={:?}", step); // skip types that are from a type error or that would require dereferencing @@ -1082,40 +1223,188 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { .unwrap_or_else(|_| { span_bug!(self.span, "{:?} was applicable but now isn't?", step.self_ty) }); - self.pick_by_value_method(step, self_ty, unstable_candidates.as_deref_mut()) - .or_else(|| { - self.pick_autorefd_method( - step, - self_ty, - hir::Mutability::Not, - unstable_candidates.as_deref_mut(), - ) - .or_else(|| { - self.pick_autorefd_method( + + let by_value_pick = self.pick_by_value_method(step, self_ty, pick_diag_hints); + + // Check for shadowing of a by-reference method by a by-value method (see comments on check_for_shadowing) + 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::Mut, - unstable_candidates.as_deref_mut(), - ) - }) - .or_else(|| { - self.pick_const_ptr_method( + 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, - unstable_candidates.as_deref_mut(), - ) - }) - .or_else(|| { - self.pick_reborrow_pin_method( + hir::Mutability::Mut, + track_unstable_candidates, + ) { + return Some(Err(e)); + } + } + } + return Some(by_value_pick); + } + + let autoref_pick = self.pick_autorefd_method( + step, + self_ty, + hir::Mutability::Not, + pick_diag_hints, + None, + ); + // Check for shadowing of a by-mut-ref method by a by-reference method (see comments on check_for_shadowing) + if let Some(autoref_pick) = autoref_pick { + if let Ok(autoref_pick) = autoref_pick.as_ref() { + // Check we're not shadowing others + if autoref_pick.kind == PickKind::InherentImplPick { + if let Err(e) = self.check_for_shadowed_autorefd_method( + autoref_pick, step, self_ty, - unstable_candidates.as_deref_mut(), - ) - }) - }) + hir::Mutability::Mut, + track_unstable_candidates, + ) { + return Some(Err(e)); + } + } + } + return Some(autoref_pick); + } + + // Note that no shadowing errors are produced from here on, + // as we consider const ptr methods. + // We allow new methods that take *mut T to shadow + // methods which took *const T, so there is no entry in + // this list for the results of `pick_const_ptr_method`. + // The reason is that the standard pointer cast method + // (on a mutable pointer) always already shadows the + // cast method (on a const pointer). So, if we added + // `pick_const_ptr_method` to this method, the anti- + // shadowing algorithm would always complain about + // the conflict between *const::cast and *mut::cast. + // In practice therefore this does constrain us: + // we cannot add new + // self: *mut Self + // methods to types such as NonNull or anything else + // which implements Receiver, because this might in future + // shadow existing methods taking + // self: *const NonNull<Self> + // in the pointee. In practice, methods taking raw pointers + // are rare, and it seems that it should be easily possible + // to avoid such compatibility breaks. + // We also don't check for reborrowed pin methods which + // may be shadowed; these also seem unlikely to occur. + self.pick_autorefd_method( + step, + self_ty, + hir::Mutability::Mut, + pick_diag_hints, + None, + ) + .or_else(|| self.pick_const_ptr_method(step, self_ty, pick_diag_hints)) + .or_else(|| self.pick_reborrow_pin_method(step, self_ty, pick_diag_hints)) }) } + /// Check for cases where arbitrary self types allows shadowing + /// of methods that might be a compatibility break. Specifically, + /// we have something like: + /// ```ignore (illustrative) + /// struct A; + /// impl A { + /// fn foo(self: &NonNull<A>) {} + /// // note this is by reference + /// } + /// ``` + /// then we've come along and added this method to `NonNull`: + /// ```ignore (illustrative) + /// fn foo(self) // note this is by value + /// ``` + /// Report an error in this case. + fn check_for_shadowed_autorefd_method( + &self, + possible_shadower: &Pick<'tcx>, + step: &CandidateStep<'tcx>, + self_ty: Ty<'tcx>, + mutbl: hir::Mutability, + track_unstable_candidates: bool, + ) -> Result<(), MethodError<'tcx>> { + // 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 + // main search. + let mut pick_diag_hints = PickDiagHints { + unstable_candidates: if track_unstable_candidates { Some(Vec::new()) } else { None }, + unsatisfied_predicates: &mut Vec::new(), + }; + // Set criteria for how we find methods possibly shadowed by 'possible_shadower' + let pick_constraints = PickConstraintsForShadowed { + // It's the same `self` type... + autoderefs: possible_shadower.autoderefs, + // ... but the method was found in an impl block determined + // by searching further along the Receiver chain than the other, + // showing that it's a smart pointer type causing the problem... + receiver_steps: possible_shadower.receiver_steps, + // ... and they don't end up pointing to the same item in the + // first place (could happen with things like blanket impls for T) + def_id: possible_shadower.item.def_id, + }; + // A note on the autoderefs above. Within pick_by_value_method, an extra + // autoderef may be applied in order to reborrow a reference with + // a different lifetime. That seems as though it would break the + // logic of these constraints, since the number of autoderefs could + // no longer be used to identify the fundamental type of the receiver. + // However, this extra autoderef is applied only to by-value calls + // where the receiver is already a reference. So this situation would + // only occur in cases where the shadowing looks like this: + // ``` + // struct A; + // impl A { + // fn foo(self: &&NonNull<A>) {} + // // note this is by DOUBLE reference + // } + // ``` + // then we've come along and added this method to `NonNull`: + // ``` + // fn foo(&self) // note this is by single reference + // ``` + // and the call is: + // ``` + // let bar = NonNull<Foo>; + // let bar = &foo; + // bar.foo(); + // ``` + // In these circumstances, the logic is wrong, and we wouldn't spot + // the shadowing, because the autoderef-based maths wouldn't line up. + // This is a niche case and we can live without generating an error + // in the case of such shadowing. + let potentially_shadowed_pick = self.pick_autorefd_method( + step, + self_ty, + mutbl, + &mut pick_diag_hints, + Some(&pick_constraints), + ); + // Look for actual pairs of shadower/shadowed which are + // the sort of shadowing case we want to avoid. Specifically... + if let Some(Ok(possible_shadowed)) = potentially_shadowed_pick.as_ref() { + let sources = [possible_shadower, possible_shadowed] + .into_iter() + .map(|p| self.candidate_source_from_pick(p)) + .collect(); + return Err(MethodError::Ambiguity(sources)); + } + Ok(()) + } + /// For each type `T` in the step list, this attempts to find a method where /// the (transformed) self type is exactly `T`. We do however do one /// transformation on the adjustment: if we are passing a region pointer in, @@ -1126,13 +1415,13 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { &self, step: &CandidateStep<'tcx>, self_ty: Ty<'tcx>, - unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, + pick_diag_hints: &mut PickDiagHints<'_, 'tcx>, ) -> Option<PickResult<'tcx>> { if step.unsize { return None; } - self.pick_method(self_ty, unstable_candidates).map(|r| { + self.pick_method(self_ty, pick_diag_hints, None).map(|r| { r.map(|mut pick| { pick.autoderefs = step.autoderefs; @@ -1170,15 +1459,22 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { step: &CandidateStep<'tcx>, self_ty: Ty<'tcx>, mutbl: hir::Mutability, - unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, + pick_diag_hints: &mut PickDiagHints<'_, 'tcx>, + pick_constraints: Option<&PickConstraintsForShadowed>, ) -> Option<PickResult<'tcx>> { let tcx = self.tcx; + if let Some(pick_constraints) = pick_constraints { + if !pick_constraints.may_shadow_based_on_autoderefs(step.autoderefs) { + return None; + } + } + // In general, during probing we erase regions. let region = tcx.lifetimes.re_erased; let autoref_ty = Ty::new_ref(tcx, region, self_ty, mutbl); - self.pick_method(autoref_ty, unstable_candidates).map(|r| { + self.pick_method(autoref_ty, pick_diag_hints, pick_constraints).map(|r| { r.map(|mut pick| { pick.autoderefs = step.autoderefs; pick.autoref_or_ptr_adjustment = @@ -1189,12 +1485,12 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } /// Looks for applicable methods if we reborrow a `Pin<&mut T>` as a `Pin<&T>`. - #[instrument(level = "debug", skip(self, step, unstable_candidates))] + #[instrument(level = "debug", skip(self, step, pick_diag_hints))] fn pick_reborrow_pin_method( &self, step: &CandidateStep<'tcx>, self_ty: Ty<'tcx>, - unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, + pick_diag_hints: &mut PickDiagHints<'_, 'tcx>, ) -> Option<PickResult<'tcx>> { if !self.tcx.features().pin_ergonomics() { return None; @@ -1215,7 +1511,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let region = self.tcx.lifetimes.re_erased; let autopin_ty = Ty::new_pinned_ref(self.tcx, region, inner_ty, hir::Mutability::Not); - self.pick_method(autopin_ty, unstable_candidates).map(|r| { + self.pick_method(autopin_ty, pick_diag_hints, None).map(|r| { r.map(|mut pick| { pick.autoderefs = step.autoderefs; pick.autoref_or_ptr_adjustment = @@ -1232,7 +1528,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { &self, step: &CandidateStep<'tcx>, self_ty: Ty<'tcx>, - unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, + pick_diag_hints: &mut PickDiagHints<'_, 'tcx>, ) -> Option<PickResult<'tcx>> { // Don't convert an unsized reference to ptr if step.unsize { @@ -1244,7 +1540,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { }; let const_ptr_ty = Ty::new_imm_ptr(self.tcx, ty); - self.pick_method(const_ptr_ty, unstable_candidates).map(|r| { + self.pick_method(const_ptr_ty, pick_diag_hints, None).map(|r| { r.map(|mut pick| { pick.autoderefs = step.autoderefs; pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::ToConstPtr); @@ -1256,40 +1552,35 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn pick_method( &self, self_ty: Ty<'tcx>, - mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, + pick_diag_hints: &mut PickDiagHints<'_, 'tcx>, + pick_constraints: Option<&PickConstraintsForShadowed>, ) -> Option<PickResult<'tcx>> { debug!("pick_method(self_ty={})", self.ty_to_string(self_ty)); - let mut possibly_unsatisfied_predicates = Vec::new(); - for (kind, candidates) in [("inherent", &self.inherent_candidates), ("extension", &self.extension_candidates)] { debug!("searching {} candidates", kind); - let res = self.consider_candidates( - self_ty, - candidates, - &mut possibly_unsatisfied_predicates, - unstable_candidates.as_deref_mut(), - ); + let res = + self.consider_candidates(self_ty, candidates, pick_diag_hints, pick_constraints); if let Some(pick) = res { return Some(pick); } } if self.private_candidate.get().is_none() { - if let Some(Ok(pick)) = - self.consider_candidates(self_ty, &self.private_candidates, &mut vec![], None) - { + if let Some(Ok(pick)) = self.consider_candidates( + self_ty, + &self.private_candidates, + &mut PickDiagHints { + unstable_candidates: None, + unsatisfied_predicates: &mut vec![], + }, + None, + ) { self.private_candidate.set(Some((pick.item.kind.as_def_kind(), pick.item.def_id))); } } - - // `pick_method` may be called twice for the same self_ty if no stable methods - // match. Only extend once. - if unstable_candidates.is_some() { - self.unsatisfied_predicates.borrow_mut().extend(possibly_unsatisfied_predicates); - } None } @@ -1297,17 +1588,25 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { &self, self_ty: Ty<'tcx>, candidates: &[Candidate<'tcx>], - possibly_unsatisfied_predicates: &mut Vec<( - ty::Predicate<'tcx>, - Option<ty::Predicate<'tcx>>, - Option<ObligationCause<'tcx>>, - )>, - mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, + pick_diag_hints: &mut PickDiagHints<'_, 'tcx>, + pick_constraints: Option<&PickConstraintsForShadowed>, ) -> Option<PickResult<'tcx>> { let mut applicable_candidates: Vec<_> = candidates .iter() + .filter(|candidate| { + pick_constraints + .map(|pick_constraints| pick_constraints.candidate_may_shadow(&candidate)) + .unwrap_or(true) + }) .map(|probe| { - (probe, self.consider_probe(self_ty, probe, possibly_unsatisfied_predicates)) + ( + probe, + self.consider_probe( + self_ty, + probe, + &mut pick_diag_hints.unsatisfied_predicates, + ), + ) }) .filter(|&(_, status)| status != ProbeResult::NoMatch) .collect(); @@ -1322,7 +1621,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } - if let Some(uc) = &mut unstable_candidates { + if let Some(uc) = &mut pick_diag_hints.unstable_candidates { applicable_candidates.retain(|&(candidate, _)| { if let stability::EvalResult::Deny { feature, .. } = self.tcx.eval_stability(candidate.item.def_id, None, self.span, None) @@ -1340,10 +1639,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } applicable_candidates.pop().map(|(probe, status)| match status { - ProbeResult::Match => { - Ok(probe - .to_unadjusted_pick(self_ty, unstable_candidates.cloned().unwrap_or_default())) - } + ProbeResult::Match => Ok(probe.to_unadjusted_pick( + self_ty, + pick_diag_hints.unstable_candidates.clone().unwrap_or_default(), + )), ProbeResult::NoMatch | ProbeResult::BadReturnType => Err(MethodError::BadReturnType), }) } @@ -1372,6 +1671,7 @@ impl<'tcx> Pick<'tcx> { autoref_or_ptr_adjustment: _, self_ty, unstable_candidates: _, + receiver_steps: _, } = *self; self_ty != other.self_ty || def_id != other.item.def_id } @@ -1447,7 +1747,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { /// so do not use to make a decision that may lead to a successful compilation. fn candidate_source(&self, candidate: &Candidate<'tcx>, self_ty: Ty<'tcx>) -> CandidateSource { match candidate.kind { - InherentImplCandidate(_) => { + InherentImplCandidate { .. } => { CandidateSource::Impl(candidate.item.container_id(self.tcx)) } ObjectCandidate(_) | WhereClauseCandidate(_) => { @@ -1477,6 +1777,15 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } + fn candidate_source_from_pick(&self, pick: &Pick<'tcx>) -> CandidateSource { + match pick.kind { + InherentImplPick => CandidateSource::Impl(pick.item.container_id(self.tcx)), + ObjectPick | WhereClausePick(_) | TraitPick => { + CandidateSource::Trait(pick.item.container_id(self.tcx)) + } + } + } + #[instrument(level = "trace", skip(self, possibly_unsatisfied_predicates), ret)] fn consider_probe( &self, @@ -1501,7 +1810,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let (mut xform_self_ty, mut xform_ret_ty); match probe.kind { - InherentImplCandidate(impl_def_id) => { + InherentImplCandidate { impl_def_id, .. } => { let impl_args = self.fresh_args_for_item(self.span, impl_def_id); let impl_ty = self.tcx.type_of(impl_def_id).instantiate(self.tcx, impl_args); (xform_self_ty, xform_ret_ty) = @@ -1693,7 +2002,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // We don't normalize the other candidates for perf/backwards-compat reasons... // but `self.return_type` is only set on the diagnostic-path, so we // should be okay doing it here. - if !matches!(probe.kind, InherentImplCandidate(_)) { + if !matches!(probe.kind, InherentImplCandidate { .. }) { xform_ret_ty = ocx.normalize(&cause, self.param_env, xform_ret_ty); } @@ -1771,6 +2080,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { autoref_or_ptr_adjustment: None, self_ty, unstable_candidates: vec![], + receiver_steps: None, }) } @@ -1808,7 +2118,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { pcx.method_name = Some(method_name); pcx.assemble_inherent_candidates(); pcx.assemble_extension_candidates_for_all_traits(); - pcx.pick_core().and_then(|pick| pick.ok()).map(|pick| pick.item) + pcx.pick_core(&mut Vec::new()).and_then(|pick| pick.ok()).map(|pick| pick.item) }) .collect(); @@ -2042,7 +2352,7 @@ impl<'tcx> Candidate<'tcx> { Pick { item: self.item, kind: match self.kind { - InherentImplCandidate(_) => InherentImplPick, + InherentImplCandidate { .. } => InherentImplPick, ObjectCandidate(_) => ObjectPick, TraitCandidate(_) => TraitPick, WhereClauseCandidate(trait_ref) => { @@ -2064,6 +2374,10 @@ impl<'tcx> Candidate<'tcx> { autoref_or_ptr_adjustment: None, self_ty, unstable_candidates, + receiver_steps: match self.kind { + InherentImplCandidate { receiver_steps, .. } => Some(receiver_steps), + _ => None, + }, } } } diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 6b1a288510a..caddfc12540 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; diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 2a8ed26aa2b..e7726845652 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; @@ -94,10 +98,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) } diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index b0c020dd7cb..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 } @@ -1840,7 +1839,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// captured by move. /// /// ```rust -/// #![feature(async_closure)] /// let x = &1i32; // Let's call this lifetime `'1`. /// let c = async move || { /// println!("{:?}", *x); @@ -1855,7 +1853,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// child capture with the lifetime of the parent coroutine-closure's env. /// /// ```rust -/// #![feature(async_closure)] /// let mut x = 1i32; /// let c = async || { /// x = 1; diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index e17a68c8692..6f1e3a0cf8c 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -476,7 +476,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { for (local_id, c_ty) in sorted_user_provided_types { let hir_id = HirId { owner: common_hir_owner, local_id }; - if let ty::UserType::TypeOf(_, user_args) = c_ty.value { + if let ty::UserTypeKind::TypeOf(_, user_args) = c_ty.value.kind { // This is a unit-testing mechanism. let span = self.tcx().hir().span(hir_id); // We need to buffer the errors in order to guarantee a consistent diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs index 1f46155abc8..031c50c45d4 100644 --- a/compiler/rustc_incremental/src/assert_dep_graph.rs +++ b/compiler/rustc_incremental/src/assert_dep_graph.rs @@ -50,7 +50,7 @@ use rustc_middle::{bug, span_bug}; use rustc_span::Span; use rustc_span::symbol::{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 +106,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/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs index 2075d4214c1..a85686e590b 100644 --- a/compiler/rustc_incremental/src/persist/dirty_clean.rs +++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs @@ -19,11 +19,13 @@ //! 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; diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index f6c3e8ebbcb..4ea171ab4a9 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -114,7 +114,6 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_data_structures::svh::Svh; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_data_structures::{base_n, flock}; -use rustc_errors::ErrorGuaranteed; use rustc_fs_util::{LinkOrCopy, link_or_copy, try_canonicalize}; use rustc_middle::bug; use rustc_session::config::CrateType; @@ -212,9 +211,9 @@ pub fn in_incr_comp_dir(incr_comp_session_dir: &Path, file_name: &str) -> PathBu /// The garbage collection will take care of it. /// /// [`rustc_interface::queries::dep_graph`]: ../../rustc_interface/struct.Queries.html#structfield.dep_graph -pub(crate) fn prepare_session_directory(sess: &Session) -> Result<(), ErrorGuaranteed> { +pub(crate) fn prepare_session_directory(sess: &Session) { if sess.opts.incremental.is_none() { - return Ok(()); + return; } let _timer = sess.timer("incr_comp_prepare_session_directory"); @@ -224,7 +223,7 @@ pub(crate) fn prepare_session_directory(sess: &Session) -> Result<(), ErrorGuara // {incr-comp-dir}/{crate-name-and-disambiguator} let crate_dir = crate_path(sess); debug!("crate-dir: {}", crate_dir.display()); - create_dir(sess, &crate_dir, "crate")?; + create_dir(sess, &crate_dir, "crate"); // Hack: canonicalize the path *after creating the directory* // because, on windows, long paths can cause problems; @@ -233,7 +232,7 @@ pub(crate) fn prepare_session_directory(sess: &Session) -> Result<(), ErrorGuara let crate_dir = match try_canonicalize(&crate_dir) { Ok(v) => v, Err(err) => { - return Err(sess.dcx().emit_err(errors::CanonicalizePath { path: crate_dir, err })); + sess.dcx().emit_fatal(errors::CanonicalizePath { path: crate_dir, err }); } }; @@ -248,11 +247,11 @@ pub(crate) fn prepare_session_directory(sess: &Session) -> Result<(), ErrorGuara // Lock the new session directory. If this fails, return an // error without retrying - let (directory_lock, lock_file_path) = lock_directory(sess, &session_dir)?; + let (directory_lock, lock_file_path) = lock_directory(sess, &session_dir); // Now that we have the lock, we can actually create the session // directory - create_dir(sess, &session_dir, "session")?; + create_dir(sess, &session_dir, "session"); // Find a suitable source directory to copy from. Ignore those that we // have already tried before. @@ -266,7 +265,7 @@ pub(crate) fn prepare_session_directory(sess: &Session) -> Result<(), ErrorGuara ); sess.init_incr_comp_session(session_dir, directory_lock); - return Ok(()); + return; }; debug!("attempting to copy data from source: {}", source_directory.display()); @@ -280,7 +279,7 @@ pub(crate) fn prepare_session_directory(sess: &Session) -> Result<(), ErrorGuara } sess.init_incr_comp_session(session_dir, directory_lock); - return Ok(()); + return; } else { debug!("copying failed - trying next directory"); @@ -459,21 +458,17 @@ fn generate_session_dir_path(crate_dir: &Path) -> PathBuf { directory_path } -fn create_dir(sess: &Session, path: &Path, dir_tag: &str) -> Result<(), ErrorGuaranteed> { +fn create_dir(sess: &Session, path: &Path, dir_tag: &str) { match std_fs::create_dir_all(path) { Ok(()) => { debug!("{} directory created successfully", dir_tag); - Ok(()) } - Err(err) => Err(sess.dcx().emit_err(errors::CreateIncrCompDir { tag: dir_tag, path, err })), + Err(err) => sess.dcx().emit_fatal(errors::CreateIncrCompDir { tag: dir_tag, path, err }), } } /// Allocate the lock-file and lock it. -fn lock_directory( - sess: &Session, - session_dir: &Path, -) -> Result<(flock::Lock, PathBuf), ErrorGuaranteed> { +fn lock_directory(sess: &Session, session_dir: &Path) -> (flock::Lock, PathBuf) { let lock_file_path = lock_file_path(session_dir); debug!("lock_directory() - lock_file: {}", lock_file_path.display()); @@ -484,15 +479,15 @@ fn lock_directory( true, ) { // the lock should be exclusive - Ok(lock) => Ok((lock, lock_file_path)), + Ok(lock) => (lock, lock_file_path), Err(lock_err) => { let is_unsupported_lock = flock::Lock::error_unsupported(&lock_err); - Err(sess.dcx().emit_err(errors::CreateLock { + sess.dcx().emit_fatal(errors::CreateLock { lock_err, session_dir, is_unsupported_lock, is_cargo: rustc_session::utils::was_invoked_from_cargo(), - })) + }); } } } diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index c74804cc798..48df84f3d09 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -11,7 +11,6 @@ use rustc_serialize::Decodable; use rustc_serialize::opaque::MemDecoder; use rustc_session::Session; use rustc_session::config::IncrementalStateAssertion; -use rustc_span::ErrorGuaranteed; use tracing::{debug, warn}; use super::data::*; @@ -182,7 +181,7 @@ fn load_dep_graph(sess: &Session) -> LoadResult<(Arc<SerializedDepGraph>, WorkPr /// If we are not in incremental compilation mode, returns `None`. /// Otherwise, tries to load the query result cache from disk, /// creating an empty cache if it could not be loaded. -pub fn load_query_result_cache(sess: &Session) -> Option<OnDiskCache<'_>> { +pub fn load_query_result_cache(sess: &Session) -> Option<OnDiskCache> { if sess.opts.incremental.is_none() { return None; } @@ -194,19 +193,19 @@ pub fn load_query_result_cache(sess: &Session) -> Option<OnDiskCache<'_>> { LoadResult::Ok { data: (bytes, start_pos) } => { let cache = OnDiskCache::new(sess, bytes, start_pos).unwrap_or_else(|()| { sess.dcx().emit_warn(errors::CorruptFile { path: &path }); - OnDiskCache::new_empty(sess.source_map()) + OnDiskCache::new_empty() }); Some(cache) } - _ => Some(OnDiskCache::new_empty(sess.source_map())), + _ => Some(OnDiskCache::new_empty()), } } /// Setups the dependency graph by loading an existing graph from disk and set up streaming of a /// new graph to an incremental session directory. -pub fn setup_dep_graph(sess: &Session) -> Result<DepGraph, ErrorGuaranteed> { +pub fn setup_dep_graph(sess: &Session) -> DepGraph { // `load_dep_graph` can only be called after `prepare_session_directory`. - prepare_session_directory(sess)?; + prepare_session_directory(sess); let res = sess.opts.build_dep_graph().then(|| load_dep_graph(sess)); @@ -222,10 +221,9 @@ pub fn setup_dep_graph(sess: &Session) -> Result<DepGraph, ErrorGuaranteed> { }); } - Ok(res - .and_then(|result| { - let (prev_graph, prev_work_products) = result.open(sess); - build_dep_graph(sess, prev_graph, prev_work_products) - }) - .unwrap_or_else(DepGraph::new_disabled)) + res.and_then(|result| { + let (prev_graph, prev_work_products) = result.open(sess); + build_dep_graph(sess, prev_graph, prev_work_products) + }) + .unwrap_or_else(DepGraph::new_disabled) } diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index de6fa132ca0..aba1e938296 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -296,6 +296,111 @@ impl<T: Idx> From<GrowableBitSet<T>> for BitSet<T> { } } +impl<T> Clone for BitSet<T> { + fn clone(&self) -> Self { + BitSet { domain_size: self.domain_size, words: self.words.clone(), marker: PhantomData } + } + + fn clone_from(&mut self, from: &Self) { + self.domain_size = from.domain_size; + self.words.clone_from(&from.words); + } +} + +impl<T: Idx> fmt::Debug for BitSet<T> { + fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result { + w.debug_list().entries(self.iter()).finish() + } +} + +impl<T: Idx> ToString for BitSet<T> { + fn to_string(&self) -> String { + let mut result = String::new(); + let mut sep = '['; + + // Note: this is a little endian printout of bytes. + + // i tracks how many bits we have printed so far. + let mut i = 0; + for word in &self.words { + let mut word = *word; + for _ in 0..WORD_BYTES { + // for each byte in `word`: + let remain = self.domain_size - i; + // If less than a byte remains, then mask just that many bits. + let mask = if remain <= 8 { (1 << remain) - 1 } else { 0xFF }; + assert!(mask <= 0xFF); + let byte = word & mask; + + result.push_str(&format!("{sep}{byte:02x}")); + + if remain <= 8 { + break; + } + word >>= 8; + i += 8; + sep = '-'; + } + sep = '|'; + } + result.push(']'); + + result + } +} + +pub struct BitIter<'a, T: Idx> { + /// A copy of the current word, but with any already-visited bits cleared. + /// (This lets us use `trailing_zeros()` to find the next set bit.) When it + /// is reduced to 0, we move onto the next word. + word: Word, + + /// The offset (measured in bits) of the current word. + offset: usize, + + /// Underlying iterator over the words. + iter: slice::Iter<'a, Word>, + + marker: PhantomData<T>, +} + +impl<'a, T: Idx> BitIter<'a, T> { + #[inline] + fn new(words: &'a [Word]) -> BitIter<'a, T> { + // We initialize `word` and `offset` to degenerate values. On the first + // call to `next()` we will fall through to getting the first word from + // `iter`, which sets `word` to the first word (if there is one) and + // `offset` to 0. Doing it this way saves us from having to maintain + // additional state about whether we have started. + BitIter { + word: 0, + offset: usize::MAX - (WORD_BITS - 1), + iter: words.iter(), + marker: PhantomData, + } + } +} + +impl<'a, T: Idx> Iterator for BitIter<'a, T> { + type Item = T; + fn next(&mut self) -> Option<T> { + loop { + if self.word != 0 { + // Get the position of the next set bit in the current word, + // then clear the bit. + let bit_pos = self.word.trailing_zeros() as usize; + self.word ^= 1 << bit_pos; + return Some(T::new(bit_pos + self.offset)); + } + + // Move onto the next word. `wrapping_add()` is needed to handle + // the degenerate initial value given to `offset` in `new()`. + self.word = *self.iter.next()?; + self.offset = self.offset.wrapping_add(WORD_BITS); + } + } +} + /// A fixed-size bitset type with a partially dense, partially sparse /// representation. The bitset is broken into chunks, and chunks that are all /// zeros or all ones are represented and handled very efficiently. @@ -305,6 +410,9 @@ impl<T: Idx> From<GrowableBitSet<T>> for BitSet<T> { /// some stretches with lots of 0s and 1s mixed in a way that causes trouble /// for `IntervalSet`. /// +/// Best used via `MixedBitSet`, rather than directly, because `MixedBitSet` +/// has better performance for small bitsets. +/// /// `T` is an index type, typically a newtyped `usize` wrapper, but it can also /// just be `usize`. /// @@ -958,117 +1066,12 @@ fn sequential_update<T: Idx>( it.fold(false, |changed, elem| self_update(elem) | changed) } -impl<T> Clone for BitSet<T> { - fn clone(&self) -> Self { - BitSet { domain_size: self.domain_size, words: self.words.clone(), marker: PhantomData } - } - - fn clone_from(&mut self, from: &Self) { - self.domain_size = from.domain_size; - self.words.clone_from(&from.words); - } -} - -impl<T: Idx> fmt::Debug for BitSet<T> { - fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result { - w.debug_list().entries(self.iter()).finish() - } -} - impl<T: Idx> fmt::Debug for ChunkedBitSet<T> { fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result { w.debug_list().entries(self.iter()).finish() } } -impl<T: Idx> ToString for BitSet<T> { - fn to_string(&self) -> String { - let mut result = String::new(); - let mut sep = '['; - - // Note: this is a little endian printout of bytes. - - // i tracks how many bits we have printed so far. - let mut i = 0; - for word in &self.words { - let mut word = *word; - for _ in 0..WORD_BYTES { - // for each byte in `word`: - let remain = self.domain_size - i; - // If less than a byte remains, then mask just that many bits. - let mask = if remain <= 8 { (1 << remain) - 1 } else { 0xFF }; - assert!(mask <= 0xFF); - let byte = word & mask; - - result.push_str(&format!("{sep}{byte:02x}")); - - if remain <= 8 { - break; - } - word >>= 8; - i += 8; - sep = '-'; - } - sep = '|'; - } - result.push(']'); - - result - } -} - -pub struct BitIter<'a, T: Idx> { - /// A copy of the current word, but with any already-visited bits cleared. - /// (This lets us use `trailing_zeros()` to find the next set bit.) When it - /// is reduced to 0, we move onto the next word. - word: Word, - - /// The offset (measured in bits) of the current word. - offset: usize, - - /// Underlying iterator over the words. - iter: slice::Iter<'a, Word>, - - marker: PhantomData<T>, -} - -impl<'a, T: Idx> BitIter<'a, T> { - #[inline] - fn new(words: &'a [Word]) -> BitIter<'a, T> { - // We initialize `word` and `offset` to degenerate values. On the first - // call to `next()` we will fall through to getting the first word from - // `iter`, which sets `word` to the first word (if there is one) and - // `offset` to 0. Doing it this way saves us from having to maintain - // additional state about whether we have started. - BitIter { - word: 0, - offset: usize::MAX - (WORD_BITS - 1), - iter: words.iter(), - marker: PhantomData, - } - } -} - -impl<'a, T: Idx> Iterator for BitIter<'a, T> { - type Item = T; - fn next(&mut self) -> Option<T> { - loop { - if self.word != 0 { - // Get the position of the next set bit in the current word, - // then clear the bit. - let bit_pos = self.word.trailing_zeros() as usize; - self.word ^= 1 << bit_pos; - return Some(T::new(bit_pos + self.offset)); - } - - // Move onto the next word. `wrapping_add()` is needed to handle - // the degenerate initial value given to `offset` in `new()`. - self.word = *self.iter.next()?; - self.offset = self.offset.wrapping_add(WORD_BITS); - } - } -} - #[inline] fn bitwise<Op>(out_vec: &mut [Word], in_vec: &[Word], op: Op) -> bool where @@ -1106,6 +1109,158 @@ where false } +/// A bitset with a mixed representation, using `BitSet` for small and medium +/// bitsets, and `ChunkedBitSet` for large bitsets, i.e. those with enough bits +/// for at least two chunks. This is a good choice for many bitsets that can +/// have large domain sizes (e.g. 5000+). +/// +/// `T` is an index type, typically a newtyped `usize` wrapper, but it can also +/// just be `usize`. +/// +/// All operations that involve an element will panic if the element is equal +/// to or greater than the domain size. All operations that involve two bitsets +/// will panic if the bitsets have differing domain sizes. +#[derive(PartialEq, Eq)] +pub enum MixedBitSet<T> { + Small(BitSet<T>), + Large(ChunkedBitSet<T>), +} + +impl<T> MixedBitSet<T> { + pub fn domain_size(&self) -> usize { + match self { + MixedBitSet::Small(set) => set.domain_size(), + MixedBitSet::Large(set) => set.domain_size(), + } + } +} + +impl<T: Idx> MixedBitSet<T> { + #[inline] + pub fn new_empty(domain_size: usize) -> MixedBitSet<T> { + if domain_size <= CHUNK_BITS { + MixedBitSet::Small(BitSet::new_empty(domain_size)) + } else { + MixedBitSet::Large(ChunkedBitSet::new_empty(domain_size)) + } + } + + #[inline] + pub fn is_empty(&self) -> bool { + match self { + MixedBitSet::Small(set) => set.is_empty(), + MixedBitSet::Large(set) => set.is_empty(), + } + } + + #[inline] + pub fn contains(&self, elem: T) -> bool { + match self { + MixedBitSet::Small(set) => set.contains(elem), + MixedBitSet::Large(set) => set.contains(elem), + } + } + + #[inline] + pub fn insert(&mut self, elem: T) -> bool { + match self { + MixedBitSet::Small(set) => set.insert(elem), + MixedBitSet::Large(set) => set.insert(elem), + } + } + + pub fn insert_all(&mut self) { + match self { + MixedBitSet::Small(set) => set.insert_all(), + MixedBitSet::Large(set) => set.insert_all(), + } + } + + #[inline] + pub fn remove(&mut self, elem: T) -> bool { + match self { + MixedBitSet::Small(set) => set.remove(elem), + MixedBitSet::Large(set) => set.remove(elem), + } + } + + pub fn iter(&self) -> MixedBitIter<'_, T> { + match self { + MixedBitSet::Small(set) => MixedBitIter::Small(set.iter()), + MixedBitSet::Large(set) => MixedBitIter::Large(set.iter()), + } + } + + bit_relations_inherent_impls! {} +} + +impl<T> Clone for MixedBitSet<T> { + fn clone(&self) -> Self { + match self { + MixedBitSet::Small(set) => MixedBitSet::Small(set.clone()), + MixedBitSet::Large(set) => MixedBitSet::Large(set.clone()), + } + } + + /// WARNING: this implementation of clone_from may panic if the two + /// bitsets have different domain sizes. This constraint is not inherent to + /// `clone_from`, but it works with the existing call sites and allows a + /// faster implementation, which is important because this function is hot. + fn clone_from(&mut self, from: &Self) { + match (self, from) { + (MixedBitSet::Small(set), MixedBitSet::Small(from)) => set.clone_from(from), + (MixedBitSet::Large(set), MixedBitSet::Large(from)) => set.clone_from(from), + _ => panic!("MixedBitSet size mismatch"), + } + } +} + +impl<T: Idx> BitRelations<MixedBitSet<T>> for MixedBitSet<T> { + fn union(&mut self, other: &MixedBitSet<T>) -> bool { + match (self, other) { + (MixedBitSet::Small(set), MixedBitSet::Small(other)) => set.union(other), + (MixedBitSet::Large(set), MixedBitSet::Large(other)) => set.union(other), + _ => panic!("MixedBitSet size mismatch"), + } + } + + fn subtract(&mut self, other: &MixedBitSet<T>) -> bool { + match (self, other) { + (MixedBitSet::Small(set), MixedBitSet::Small(other)) => set.subtract(other), + (MixedBitSet::Large(set), MixedBitSet::Large(other)) => set.subtract(other), + _ => panic!("MixedBitSet size mismatch"), + } + } + + fn intersect(&mut self, _other: &MixedBitSet<T>) -> bool { + unimplemented!("implement if/when necessary"); + } +} + +impl<T: Idx> fmt::Debug for MixedBitSet<T> { + fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + MixedBitSet::Small(set) => set.fmt(w), + MixedBitSet::Large(set) => set.fmt(w), + } + } +} + +pub enum MixedBitIter<'a, T: Idx> { + Small(BitIter<'a, T>), + Large(ChunkedBitIter<'a, T>), +} + +impl<'a, T: Idx> Iterator for MixedBitIter<'a, T> { + type Item = T; + fn next(&mut self) -> Option<T> { + match self { + MixedBitIter::Small(iter) => iter.next(), + MixedBitIter::Large(iter) => iter.next(), + } + } +} + /// A resizable bitset type with a dense representation. /// /// `T` is an index type, typically a newtyped `usize` wrapper, but it can also @@ -1374,7 +1529,7 @@ impl<R: Idx, C: Idx> fmt::Debug for BitMatrix<R, C> { /// sparse representation. /// /// Initially, every row has no explicit representation. If any bit within a -/// row is set, the entire row is instantiated as `Some(<ChunkedBitSet>)`. +/// row is set, the entire row is instantiated as `Some(<BitSet>)`. /// Furthermore, any previously uninstantiated rows prior to it will be /// instantiated as `None`. Those prior rows may themselves become fully /// instantiated later on if any of their bits are set. @@ -1388,7 +1543,7 @@ where C: Idx, { num_columns: usize, - rows: IndexVec<R, Option<ChunkedBitSet<C>>>, + rows: IndexVec<R, Option<BitSet<C>>>, } impl<R: Idx, C: Idx> SparseBitMatrix<R, C> { @@ -1397,10 +1552,10 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> { Self { num_columns, rows: IndexVec::new() } } - fn ensure_row(&mut self, row: R) -> &mut ChunkedBitSet<C> { - // Instantiate any missing rows up to and including row `row` with an empty ChunkedBitSet. - // Then replace row `row` with a full ChunkedBitSet if necessary. - self.rows.get_or_insert_with(row, || ChunkedBitSet::new_empty(self.num_columns)) + fn ensure_row(&mut self, row: R) -> &mut BitSet<C> { + // Instantiate any missing rows up to and including row `row` with an empty `BitSet`. + // Then replace row `row` with a full `BitSet` if necessary. + self.rows.get_or_insert_with(row, || BitSet::new_empty(self.num_columns)) } /// Sets the cell at `(row, column)` to true. Put another way, insert @@ -1474,7 +1629,7 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> { self.row(row).into_iter().flat_map(|r| r.iter()) } - pub fn row(&self, row: R) -> Option<&ChunkedBitSet<C>> { + pub fn row(&self, row: R) -> Option<&BitSet<C>> { self.rows.get(row)?.as_ref() } @@ -1484,7 +1639,7 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> { /// Returns true if the row was changed. pub fn intersect_row<Set>(&mut self, row: R, set: &Set) -> bool where - ChunkedBitSet<C>: BitRelations<Set>, + BitSet<C>: BitRelations<Set>, { match self.rows.get_mut(row) { Some(Some(row)) => row.intersect(set), @@ -1498,7 +1653,7 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> { /// Returns true if the row was changed. pub fn subtract_row<Set>(&mut self, row: R, set: &Set) -> bool where - ChunkedBitSet<C>: BitRelations<Set>, + BitSet<C>: BitRelations<Set>, { match self.rows.get_mut(row) { Some(Some(row)) => row.subtract(set), @@ -1512,7 +1667,7 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> { /// Returns true if the row was changed. pub fn union_row<Set>(&mut self, row: R, set: &Set) -> bool where - ChunkedBitSet<C>: BitRelations<Set>, + BitSet<C>: BitRelations<Set>, { self.ensure_row(row).union(set) } diff --git a/compiler/rustc_index/src/bit_set/tests.rs b/compiler/rustc_index/src/bit_set/tests.rs index 3f9198ce37f..f6142323979 100644 --- a/compiler/rustc_index/src/bit_set/tests.rs +++ b/compiler/rustc_index/src/bit_set/tests.rs @@ -503,15 +503,15 @@ fn sparse_matrix_operations() { matrix.insert(2, 99); matrix.insert(4, 0); - let mut disjoint: ChunkedBitSet<usize> = ChunkedBitSet::new_empty(100); + let mut disjoint: BitSet<usize> = BitSet::new_empty(100); disjoint.insert(33); - let mut superset = ChunkedBitSet::new_empty(100); + let mut superset = BitSet::new_empty(100); superset.insert(22); superset.insert(75); superset.insert(33); - let mut subset = ChunkedBitSet::new_empty(100); + let mut subset = BitSet::new_empty(100); subset.insert(22); // SparseBitMatrix::remove diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 544f941dda5..f3fa3d3a1c8 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -689,7 +689,7 @@ impl<'tcx> InferCtxt<'tcx> { /// 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( + pub fn add_member_constraint( &self, key: ty::OpaqueTypeKey<'tcx>, definition_span: Span, @@ -697,7 +697,7 @@ impl<'tcx> InferCtxt<'tcx> { region: ty::Region<'tcx>, in_regions: Lrc<Vec<ty::Region<'tcx>>>, ) { - self.inner.borrow_mut().unwrap_region_constraints().member_constraint( + self.inner.borrow_mut().unwrap_region_constraints().add_member_constraint( key, definition_span, hidden_ty, diff --git a/compiler/rustc_infer/src/infer/opaque_types/mod.rs b/compiler/rustc_infer/src/infer/opaque_types/mod.rs index b64686afd23..8650c20559f 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs @@ -364,7 +364,7 @@ impl<'tcx> InferCtxt<'tcx> { concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { tcx: self.tcx, op: |r| { - self.member_constraint( + self.add_member_constraint( opaque_type_key, span, concrete_ty, diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index 270217e26b7..61ce86e7767 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -466,7 +466,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { } } - pub(super) fn member_constraint( + pub(super) fn add_member_constraint( &mut self, key: ty::OpaqueTypeKey<'tcx>, definition_span: Span, 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 3920d3077d3..91f190c6a28 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -5,9 +5,9 @@ use std::sync::Arc; use rustc_ast::{LitKind, MetaItemKind, token}; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::jobserver; use rustc_data_structures::stable_hasher::StableHasher; use rustc_data_structures::sync::Lrc; -use rustc_data_structures::{defer, jobserver}; use rustc_errors::registry::Registry; use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed}; use rustc_lint::LintStore; @@ -371,7 +371,6 @@ pub(crate) fn initialize_checked_jobserver(early_dcx: &EarlyDiagCtxt) { // JUSTIFICATION: before session exists, only config #[allow(rustc::bad_opt_access)] -#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R { trace!("run_compiler"); @@ -425,7 +424,11 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se config.opts.unstable_opts.translate_directionality_markers, ) { Ok(bundle) => bundle, - Err(e) => early_dcx.early_fatal(format!("failed to load fluent bundle: {e}")), + Err(e) => { + // We can't translate anything if we failed to load translations + #[allow(rustc::untranslatable_diagnostic)] + early_dcx.early_fatal(format!("failed to load fluent bundle: {e}")) + } }; let mut locale_resources = config.locale_resources; @@ -441,7 +444,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se temps_dir, }, bundle, - config.registry.clone(), + config.registry, locale_resources, config.lint_caps, target, @@ -479,7 +482,6 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se let mut lint_store = rustc_lint::new_lint_store(sess.enable_internal_lints()); if let Some(register_lints) = config.register_lints.as_deref() { register_lints(&sess, &mut lint_store); - sess.registered_lints = true; } sess.lint_store = Some(Lrc::new(lint_store)); @@ -492,32 +494,34 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se // There are two paths out of `f`. // - Normal exit. - // - Panic, e.g. triggered by `abort_if_errors`. + // - Panic, e.g. triggered by `abort_if_errors` or a fatal error. // // We must run `finish_diagnostics` in both cases. - let res = { - // If `f` panics, `finish_diagnostics` will run during - // unwinding because of the `defer`. - let sess_abort_guard = defer(|| { - compiler.sess.finish_diagnostics(&config.registry); - }); - - let res = f(&compiler); - - // If `f` doesn't panic, `finish_diagnostics` will run - // normally when `sess_abort_guard` is dropped. - drop(sess_abort_guard); - - // If error diagnostics have been emitted, we can't return an - // error directly, because the return type of this function - // is `R`, not `Result<R, E>`. But we need to communicate the - // errors' existence to the caller, otherwise the caller might - // mistakenly think that no errors occurred and return a zero - // exit code. So we abort (panic) instead, similar to if `f` - // had panicked. + let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| f(&compiler))); + + compiler.sess.finish_diagnostics(); + + // If error diagnostics have been emitted, we can't return an + // error directly, because the return type of this function + // is `R`, not `Result<R, E>`. But we need to communicate the + // errors' existence to the caller, otherwise the caller might + // mistakenly think that no errors occurred and return a zero + // exit code. So we abort (panic) instead, similar to if `f` + // had panicked. + if res.is_ok() { compiler.sess.dcx().abort_if_errors(); + } + + // Also make sure to flush delayed bugs as if we panicked, the + // bugs would be flushed by the Drop impl of DiagCtxt while + // unwinding, which would result in an abort with + // "panic in a destructor during cleanup". + compiler.sess.dcx().flush_delayed(); - res + let res = match res { + Ok(res) => res, + // Resume unwinding if a panic happened. + Err(err) => std::panic::resume_unwind(err), }; let prof = compiler.sess.prof.clone(); 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 d42915f4110..2b5271939e5 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -33,15 +33,15 @@ use rustc_session::output::{collect_crate_types, filename_for_input, find_crate_ use rustc_session::search_paths::PathKind; use rustc_session::{Limit, Session}; use rustc_span::symbol::{Symbol, sym}; -use rustc_span::{FileName, SourceFileHash, SourceFileHashAlgorithm}; +use rustc_span::{ErrorGuaranteed, FileName, SourceFileHash, SourceFileHashAlgorithm}; use rustc_target::spec::PanicStrategy; use rustc_trait_selection::traits; use tracing::{info, instrument}; -use crate::interface::{Compiler, Result}; +use crate::interface::Compiler; use crate::{errors, proc_macro_decls, util}; -pub(crate) fn parse<'a>(sess: &'a Session) -> Result<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 { @@ -52,13 +52,16 @@ pub(crate) fn parse<'a>(sess: &'a Session) -> Result<ast::Crate> { }); parser.parse_crate_mod() }) - .map_err(|parse_error| parse_error.emit())?; + .unwrap_or_else(|parse_error| { + let guar: ErrorGuaranteed = parse_error.emit(); + guar.raise_fatal(); + }); if sess.opts.unstable_opts.input_stats { input_stats::print_ast_stats(&krate, "PRE EXPANSION AST STATS", "ast-stats-1"); } - Ok(krate) + krate } fn pre_expansion_lint<'a>( @@ -73,6 +76,7 @@ fn pre_expansion_lint<'a>( || { rustc_lint::check_ast_node( sess, + None, features, true, lint_store, @@ -307,6 +311,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, @@ -706,13 +711,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>>, -) -> Result<&'tcx GlobalCtxt<'tcx>> { + f: F, +) -> T { let sess = &compiler.sess; rustc_builtin_macros::cmdline_attrs::inject( @@ -733,7 +736,7 @@ pub(crate) fn create_global_ctxt<'tcx>( sess.cfg_version, ); let outputs = util::build_output_filenames(&pre_configured_attrs, sess); - let dep_graph = setup_dep_graph(sess)?; + let dep_graph = setup_dep_graph(sess); let cstore = FreezeLock::new(Box::new(CStore::new(compiler.codegen_backend.metadata_loader())) as _); @@ -760,44 +763,64 @@ 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 Compiler, + &'tcx OnceLock<GlobalCtxt<'tcx>>, + &'tcx WorkerLocal<Arena<'tcx>>, + &'tcx WorkerLocal<rustc_hir::Arena<'tcx>>, + F, + ) -> T, + > = Box::new(move |compiler, gcx_cell, arena, hir_arena, f| { + let sess = &compiler.sess; + + 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, + compiler.current_gcx.clone(), + |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( + 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)); - }); - Ok(qcx) - }) + inner(compiler, &gcx_cell, &arena, &hir_arena, f) } /// Runs all analyses that we guarantee to run, even if errors were reported in earlier analyses. @@ -876,7 +899,6 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { || tcx.hir().body_const_context(def_id).is_some() { tcx.ensure().mir_drops_elaborated_and_const_checked(def_id); - tcx.ensure().unused_generic_params(ty::InstanceKind::Item(def_id.to_def_id())); } } }); @@ -895,8 +917,7 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { // If `-Zvalidate-mir` is set, we also want to compute the final MIR for each item // (either its `mir_for_ctfe` or `optimized_mir`) since that helps uncover any bugs // in MIR optimizations that may only be reachable through codegen, or other codepaths - // that requires the optimized/ctfe MIR, such as polymorphization, coroutine bodies, - // or evaluating consts. + // that requires the optimized/ctfe MIR, coroutine bodies, or evaluating consts. if tcx.sess.opts.unstable_opts.validate_mir { sess.time("ensuring_final_MIR_is_computable", || { tcx.hir().par_body_owners(|def_id| { @@ -908,7 +929,7 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { /// Runs the type-checking, region checking and other miscellaneous analysis /// passes on the crate. -fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { +fn analysis(tcx: TyCtxt<'_>, (): ()) { run_required_analyses(tcx); let sess = tcx.sess; @@ -922,7 +943,7 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { // But we exclude lint errors from this, because lint errors are typically // less serious and we're more likely to want to continue (#87337). if let Some(guar) = sess.dcx().has_errors_excluding_lint_errors() { - return Err(guar); + guar.raise_fatal(); } sess.time("misc_checking_3", || { @@ -1050,8 +1071,6 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { }) } } - - Ok(()) } /// Check for the `#[rustc_error]` annotation, which forces an error in codegen. This is used @@ -1093,12 +1112,12 @@ fn check_for_rustc_errors_attr(tcx: TyCtxt<'_>) { pub(crate) fn start_codegen<'tcx>( codegen_backend: &dyn CodegenBackend, tcx: TyCtxt<'tcx>, -) -> Result<Box<dyn Any>> { +) -> Box<dyn Any> { // Don't do code generation if there were any errors. Likewise if // there were any delayed bugs, because codegen will likely cause // more ICEs, obscuring the original problem. if let Some(guar) = tcx.sess.dcx().has_errors_or_delayed_bugs() { - return Err(guar); + guar.raise_fatal(); } // Hook for UI tests. @@ -1126,7 +1145,19 @@ pub(crate) fn start_codegen<'tcx>( } } - Ok(codegen) + // This must run after monomorphization so that all generic types + // have been instantiated. + if tcx.sess.opts.unstable_opts.print_type_sizes { + tcx.sess.code_stats.print_type_sizes(); + } + + if tcx.sess.opts.unstable_opts.print_vtable_sizes { + let crate_name = tcx.crate_name(LOCAL_CRATE); + + tcx.sess.code_stats.print_vtable_sizes(crate_name); + } + + codegen } fn get_recursion_limit(krate_attrs: &[ast::Attribute], sess: &Session) -> Limit { diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index cd3a2fb7049..c8914c9be9c 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -1,119 +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, Result}; 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<Result<Steal<T>>>>, -} - -impl<T> Query<T> { - fn compute<F: FnOnce() -> Result<T>>(&self, f: F) -> Result<QueryResult<'_, T>> { - RefMut::filter_map( - self.result.borrow_mut(), - |r: &mut Option<Result<Steal<T>>>| -> Option<&mut Steal<T>> { - r.get_or_insert_with(|| f().map(Steal::new)).as_mut().ok() - }, - ) - .map_err(|r| *r.as_ref().unwrap().as_ref().map(|_| ()).unwrap_err()) - .map(QueryResult) - } -} - -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) -> Result<QueryResult<'_, ast::Crate>> { - self.parse.compute(|| passes::parse(&self.compiler.sess)) - } - - pub fn global_ctxt(&'tcx self) -> Result<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>, @@ -126,22 +25,10 @@ impl Linker { pub fn codegen_and_build_linker( tcx: TyCtxt<'_>, codegen_backend: &dyn CodegenBackend, - ) -> Result<Linker> { - let ongoing_codegen = passes::start_codegen(codegen_backend, tcx)?; - - // This must run after monomorphization so that all generic types - // have been instantiated. - if tcx.sess.opts.unstable_opts.print_type_sizes { - tcx.sess.code_stats.print_type_sizes(); - } + ) -> Linker { + let ongoing_codegen = passes::start_codegen(codegen_backend, tcx); - if tcx.sess.opts.unstable_opts.print_vtable_sizes { - let crate_name = tcx.crate_name(LOCAL_CRATE); - - tcx.sess.code_stats.print_vtable_sizes(crate_name); - } - - Ok(Linker { + Linker { dep_graph: tcx.dep_graph.clone(), output_filenames: Arc::clone(tcx.output_filenames(())), crate_hash: if tcx.needs_crate_hash() { @@ -150,16 +37,17 @@ impl Linker { None }, ongoing_codegen, - }) + } } - pub fn link(self, sess: &Session, codegen_backend: &dyn CodegenBackend) -> Result<()> { - let (codegen_results, work_products) = - codegen_backend.join_codegen(self.ongoing_codegen, sess, &self.output_filenames); + pub fn link(self, sess: &Session, codegen_backend: &dyn CodegenBackend) { + let (codegen_results, work_products) = sess.time("finish_ongoing_codegen", || { + codegen_backend.join_codegen(self.ongoing_codegen, sess, &self.output_filenames) + }); - if let Some(guar) = sess.dcx().has_errors() { - return Err(guar); - } + sess.dcx().abort_if_errors(); + + let _timer = sess.timer("link"); sess.time("serialize_work_products", || { rustc_incremental::save_work_product_index(sess, &self.dep_graph, work_products) @@ -178,7 +66,7 @@ impl Linker { .keys() .any(|&i| i == OutputType::Exe || i == OutputType::Metadata) { - return Ok(()); + return; } if sess.opts.unstable_opts.no_link { @@ -189,32 +77,13 @@ impl Linker { &codegen_results, &*self.output_filenames, ) - .map_err(|error| { + .unwrap_or_else(|error| { sess.dcx().emit_fatal(FailedWritingFile { path: &rlink_file, error }) - })?; - return Ok(()); + }); + return; } let _timer = sess.prof.verbose_generic_activity("link_crate"); 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 3c4d9c2e928..e76e9ca9f85 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -832,6 +832,7 @@ fn test_unstable_options_tracking_hash() { tracked!(precise_enum_drop_elaboration, false); tracked!(profile_sample_use, Some(PathBuf::from("abc"))); tracked!(profiler_runtime, "abc".to_string()); + tracked!(reg_struct_return, true); tracked!(regparm, Some(3)); tracked!(relax_elf_relocations, Some(true)); tracked!(remap_cwd_prefix, Some(PathBuf::from("abc"))); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index d3213b1263c..54fbb743b93 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -35,10 +35,10 @@ pub type MakeBackendFn = fn() -> Box<dyn CodegenBackend>; pub fn add_configuration(cfg: &mut Cfg, sess: &mut Session, codegen_backend: &dyn CodegenBackend) { let tf = sym::target_feature; - let unstable_target_features = codegen_backend.target_features(sess, true); + let unstable_target_features = codegen_backend.target_features_cfg(sess, true); sess.unstable_target_features.extend(unstable_target_features.iter().cloned()); - let target_features = codegen_backend.target_features(sess, false); + let target_features = codegen_backend.target_features_cfg(sess, false); sess.target_features.extend(target_features.iter().cloned()); cfg.extend(target_features.into_iter().map(|feat| (tf, Some(feat)))); @@ -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_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index 6caeec1b5cc..aa4abf678b9 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -33,6 +33,7 @@ pub mod unescape; mod tests; use unicode_properties::UnicodeEmoji; +pub use unicode_xid::UNICODE_VERSION as UNICODE_XID_VERSION; use self::LiteralKind::*; use self::TokenKind::*; 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 49e6b763590..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,10 +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 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 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/async_closures.rs b/compiler/rustc_lint/src/async_closures.rs index 2a821b71103..5d40b8ab2ee 100644 --- a/compiler/rustc_lint/src/async_closures.rs +++ b/compiler/rustc_lint/src/async_closures.rs @@ -12,7 +12,6 @@ declare_lint! { /// ### Example /// /// ```rust - /// #![feature(async_closure)] /// #![warn(closure_returning_async_block)] /// let c = |x: &str| async {}; /// ``` @@ -40,8 +39,6 @@ declare_lint! { /// But it does work with async closures: /// /// ```rust - /// #![feature(async_closure)] - /// /// async fn callback(x: &str) {} /// /// let captured_str = String::new(); @@ -52,7 +49,6 @@ declare_lint! { pub CLOSURE_RETURNING_ASYNC_BLOCK, Allow, "closure that returns `async {}` could be rewritten as an async closure", - @feature_gate = async_closure; } declare_lint_pass!( diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index ec085198922..d43fe27aa03 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -387,7 +387,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; } @@ -625,6 +625,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations { cx.param_env, ty, traits::ObligationCause::misc(item.span, item.owner_id.def_id), + hir::Safety::Safe, ) .is_ok() { @@ -1011,7 +1012,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| { @@ -1175,7 +1176,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() { @@ -1813,7 +1814,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>", }; } @@ -3037,7 +3038,7 @@ impl EarlyLintPass for SpecialModuleName { for item in &krate.items { if let ast::ItemKind::Mod( _, - ast::ModKind::Unloaded | ast::ModKind::Loaded(_, ast::Inline::No, _), + ast::ModKind::Unloaded | ast::ModKind::Loaded(_, ast::Inline::No, _, _), ) = item.kind { if item.attrs.iter().any(|a| a.has_name(sym::path)) { diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 44c7888a530..d8d901698d6 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -20,7 +20,7 @@ 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; @@ -33,8 +33,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 +509,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 7e298a9a63c..10769b57a76 100644 --- a/compiler/rustc_lint/src/dangling.rs +++ b/compiler/rustc_lint/src/dangling.rs @@ -192,6 +192,8 @@ fn is_temporary_rvalue(expr: &Expr<'_>) -> bool { | ExprKind::DropTemps(..) | ExprKind::Let(..) => false, + ExprKind::UnsafeBinderCast(..) => false, + // Not applicable ExprKind::Type(..) | ExprKind::Err(..) => false, } diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 4f3184f1d7c..a15aab32aee 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -9,35 +9,40 @@ 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 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 +54,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 +72,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 +94,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 +126,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 +152,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 +188,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,30 +250,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); } @@ -308,28 +317,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); @@ -340,16 +339,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); } @@ -357,6 +350,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, @@ -380,22 +374,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..b0fb1e4af76 100644 --- a/compiler/rustc_lint/src/context/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -8,6 +8,7 @@ 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; @@ -18,7 +19,12 @@ 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 +205,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 16994846545..033c0fa4c88 100644 --- a/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs +++ b/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs @@ -1,9 +1,11 @@ +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::{Span, Symbol, sym}; +use rustc_span::{ExpnKind, Span, Symbol, sym}; use crate::lints; @@ -60,8 +62,41 @@ fn cargo_help_sub( } } +fn rustc_macro_help(span: Span) -> Option<lints::UnexpectedCfgRustcMacroHelp> { + 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 + { + Some(lints::UnexpectedCfgRustcMacroHelp { macro_kind: macro_kind.descr(), macro_name }) + } else { + None + } +} + +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, + crate_name: tcx.crate_name(def_id.krate), + }) + } else { + None + } +} + pub(super) fn unexpected_cfg_name( sess: &Session, + tcx: Option<TyCtxt<'_>>, (name, name_span): (Symbol, Span), value: Option<(Symbol, Span)>, ) -> lints::UnexpectedCfgName { @@ -85,6 +120,7 @@ pub(super) fn unexpected_cfg_name( }; let is_from_cargo = rustc_session::utils::was_invoked_from_cargo(); + let is_from_external_macro = rustc_middle::lint::in_external_macro(sess, name_span); let mut is_feature_cfg = name == sym::feature; let code_sugg = if is_feature_cfg && is_from_cargo { @@ -185,12 +221,21 @@ pub(super) fn unexpected_cfg_name( }; let invocation_help = if is_from_cargo { - let sub = if !is_feature_cfg { Some(cargo_help_sub(sess, &inst)) } else { None }; - lints::unexpected_cfg_name::InvocationHelp::Cargo { sub } + let help = if !is_feature_cfg && !is_from_external_macro { + Some(cargo_help_sub(sess, &inst)) + } else { + None + }; + lints::unexpected_cfg_name::InvocationHelp::Cargo { + help, + macro_help: cargo_macro_help(tcx, name_span), + } } else { - lints::unexpected_cfg_name::InvocationHelp::Rustc(lints::UnexpectedCfgRustcHelp::new( - &inst(EscapeQuotes::No), - )) + let help = lints::UnexpectedCfgRustcHelp::new(&inst(EscapeQuotes::No)); + lints::unexpected_cfg_name::InvocationHelp::Rustc { + help, + macro_help: rustc_macro_help(name_span), + } }; lints::UnexpectedCfgName { code_sugg, invocation_help, name } @@ -198,6 +243,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 { @@ -216,7 +262,9 @@ pub(super) fn unexpected_cfg_value( .copied() .flatten() .collect(); + let is_from_cargo = rustc_session::utils::was_invoked_from_cargo(); + let is_from_external_macro = rustc_middle::lint::in_external_macro(sess, name_span); // Show the full list if all possible values for a given name, but don't do it // for names as the possibilities could be very long @@ -284,25 +332,31 @@ pub(super) fn unexpected_cfg_value( }; let invocation_help = if is_from_cargo { - let help = if name == sym::feature { + let help = if name == sym::feature && !is_from_external_macro { if let Some((value, _value_span)) = value { Some(lints::unexpected_cfg_value::CargoHelp::AddFeature { value }) } else { Some(lints::unexpected_cfg_value::CargoHelp::DefineFeatures) } - } else if can_suggest_adding_value { + } else if can_suggest_adding_value && !is_from_external_macro { Some(lints::unexpected_cfg_value::CargoHelp::Other(cargo_help_sub(sess, &inst))) } else { None }; - lints::unexpected_cfg_value::InvocationHelp::Cargo(help) + lints::unexpected_cfg_value::InvocationHelp::Cargo { + help, + macro_help: cargo_macro_help(tcx, name_span), + } } else { let help = if can_suggest_adding_value { Some(lints::UnexpectedCfgRustcHelp::new(&inst(EscapeQuotes::No))) } else { None }; - lints::unexpected_cfg_value::InvocationHelp::Rustc(help) + lints::unexpected_cfg_value::InvocationHelp::Rustc { + help, + macro_help: rustc_macro_help(name_span), + } }; lints::UnexpectedCfgValue { diff --git a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs index 025fd452040..28368e1ab46 100644 --- a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs +++ b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs @@ -9,6 +9,7 @@ use crate::lints::{ use crate::{EarlyContext, EarlyLintPass, LintContext}; declare_lint! { + #[allow(text_direction_codepoint_in_literal)] /// The `text_direction_codepoint_in_literal` lint detects Unicode codepoints that change the /// visual representation of text on screen in a way that does not correspond to their on /// memory representation. diff --git a/compiler/rustc_lint/src/if_let_rescope.rs b/compiler/rustc_lint/src/if_let_rescope.rs index 2db229ed133..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>", }; } @@ -422,6 +422,7 @@ impl<'tcx, 'a> Visitor<'tcx> for FindSignificantDropper<'tcx, 'a> { hir::ExprKind::Unary(_, expr) | hir::ExprKind::Cast(expr, _) | hir::ExprKind::Type(expr, _) + | hir::ExprKind::UnsafeBinderCast(_, expr, _) | hir::ExprKind::Yield(expr, _) | hir::ExprKind::AddrOf(_, _, expr) | hir::ExprKind::Match(expr, _, _) diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 482650e04e8..4dff512f9d6 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -12,11 +12,11 @@ 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::symbol::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, @@ -376,46 +376,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..04099cd9001 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}; @@ -371,7 +372,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 +384,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 +686,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 +915,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, attr.span(), 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 20822f23bf1..62414ab00e5 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -951,13 +951,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 +1809,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 +1826,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> { @@ -2172,6 +2183,24 @@ impl UnexpectedCfgRustcHelp { } } +#[derive(Subdiagnostic)] +#[note(lint_unexpected_cfg_from_external_macro_origin)] +#[help(lint_unexpected_cfg_from_external_macro_refer)] +pub(crate) struct UnexpectedCfgRustcMacroHelp { + pub macro_kind: &'static str, + pub macro_name: Symbol, +} + +#[derive(Subdiagnostic)] +#[note(lint_unexpected_cfg_from_external_macro_origin)] +#[help(lint_unexpected_cfg_from_external_macro_refer)] +#[help(lint_unexpected_cfg_cargo_update)] +pub(crate) struct UnexpectedCfgCargoMacroHelp { + pub macro_kind: &'static str, + pub macro_name: Symbol, + pub crate_name: Symbol, +} + #[derive(LintDiagnostic)] #[diag(lint_unexpected_cfg_name)] pub(crate) struct UnexpectedCfgName { @@ -2276,10 +2305,17 @@ pub(crate) mod unexpected_cfg_name { #[note(lint_unexpected_cfg_doc_cargo)] Cargo { #[subdiagnostic] - sub: Option<super::UnexpectedCfgCargoHelp>, + macro_help: Option<super::UnexpectedCfgCargoMacroHelp>, + #[subdiagnostic] + help: Option<super::UnexpectedCfgCargoHelp>, }, #[note(lint_unexpected_cfg_doc_rustc)] - Rustc(#[subdiagnostic] super::UnexpectedCfgRustcHelp), + Rustc { + #[subdiagnostic] + macro_help: Option<super::UnexpectedCfgRustcMacroHelp>, + #[subdiagnostic] + help: super::UnexpectedCfgRustcHelp, + }, } } @@ -2382,9 +2418,19 @@ pub(crate) mod unexpected_cfg_value { #[derive(Subdiagnostic)] pub(crate) enum InvocationHelp { #[note(lint_unexpected_cfg_doc_cargo)] - Cargo(#[subdiagnostic] Option<CargoHelp>), + Cargo { + #[subdiagnostic] + help: Option<CargoHelp>, + #[subdiagnostic] + macro_help: Option<super::UnexpectedCfgCargoMacroHelp>, + }, #[note(lint_unexpected_cfg_doc_rustc)] - Rustc(#[subdiagnostic] Option<super::UnexpectedCfgRustcHelp>), + Rustc { + #[subdiagnostic] + help: Option<super::UnexpectedCfgRustcHelp>, + #[subdiagnostic] + macro_help: Option<super::UnexpectedCfgRustcMacroHelp>, + }, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_lint/src/non_ascii_idents.rs b/compiler/rustc_lint/src/non_ascii_idents.rs index 9b495c19990..50c64a9c947 100644 --- a/compiler/rustc_lint/src/non_ascii_idents.rs +++ b/compiler/rustc_lint/src/non_ascii_idents.rs @@ -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/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index 4d8ebf2909e..e315307cd45 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -1,14 +1,14 @@ 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_ast as ast, rustc_attr_parsing as attr, rustc_hir as hir}; use crate::lints::{ NonCamelCaseType, NonCamelCaseTypeSub, NonSnakeCaseDiag, NonSnakeCaseDiagSub, @@ -342,35 +342,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 +502,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/passes.rs b/compiler/rustc_lint/src/passes.rs index 9d84d36e779..380cbe650f0 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]); ]); ) } 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/tests.rs b/compiler/rustc_lint/src/tests.rs index 7fbf381a8d3..186dec5904b 100644 --- a/compiler/rustc_lint/src/tests.rs +++ b/compiler/rustc_lint/src/tests.rs @@ -1,3 +1,5 @@ +#![cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] + use rustc_span::{Symbol, create_default_session_globals_then}; use crate::levels::parse_lint_and_tool_name; diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 33650be056d..33d31d27c73 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -483,29 +483,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..d4b39d91a71 100644 --- a/compiler/rustc_lint/src/unqualified_local_imports.rs +++ b/compiler/rustc_lint/src/unqualified_local_imports.rs @@ -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 b775cd37409..9cad5d98562 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1235,7 +1235,7 @@ impl EarlyLintPass for UnusedParens { self.check_unused_parens_pat(cx, &f.pat, false, false, keep_space); }, // Avoid linting on `i @ (p0 | .. | pn)` and `box (p0 | .. | pn)`, #64106. - Ident(.., Some(p)) | Box(p) | Deref(p) => self.check_unused_parens_pat(cx, p, true, false, keep_space), + Ident(.., Some(p)) | Box(p) | Deref(p) | Guard(p, _) => self.check_unused_parens_pat(cx, p, true, false, keep_space), // Avoid linting on `&(mut x)` as `&mut x` has a different meaning, #55342. // Also avoid linting on `& mut? (p0 | .. | pn)`, #64106. Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space), diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index d2b7ae620e2..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; @@ -3929,6 +3929,7 @@ declare_lint! { } declare_lint! { + #[allow(text_direction_codepoint_in_literal)] /// The `text_direction_codepoint_in_comment` lint detects Unicode codepoints in comments that /// change the visual representation of text on screen in a way that does not correspond to /// their on memory representation. @@ -4188,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 @@ -4242,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 } @@ -4789,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>", }; } @@ -4825,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>", }; } @@ -4866,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>", }; } @@ -5068,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>", }; } @@ -5107,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..7edb1d2ffe8 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, @@ -221,8 +222,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. diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 06550728f0f..b79205ff946 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1535,7 +1535,7 @@ extern "C" LLVMTypeKind LLVMRustGetTypeKind(LLVMTypeRef Ty) { DEFINE_SIMPLE_CONVERSION_FUNCTIONS(SMDiagnostic, LLVMSMDiagnosticRef) extern "C" LLVMSMDiagnosticRef LLVMRustGetSMDiagnostic(LLVMDiagnosticInfoRef DI, - unsigned *Cookie) { + uint64_t *Cookie) { llvm::DiagnosticInfoSrcMgr *SM = static_cast<llvm::DiagnosticInfoSrcMgr *>(unwrap(DI)); *Cookie = SM->getLocCookie(); 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..fd1535094c9 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; @@ -97,7 +98,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 641d1d8e798..e8de0acb7c9 100644 --- a/compiler/rustc_metadata/src/dependency_format.rs +++ b/compiler/rustc_metadata/src/dependency_format.rs @@ -77,7 +77,7 @@ pub(crate) fn calculate(tcx: TyCtxt<'_>) -> Dependencies { verify_ok(tcx, &linkage); (ty, linkage) }) - .collect::<Vec<_>>() + .collect() } fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 8bd2281981b..2ccc0d0dc02 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}; @@ -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 f3f5af49412..6eae4f9a8d6 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1104,6 +1104,7 @@ impl<'a> CrateMetadataRef<'a> { name: self.item_name(did.index), vis: self.get_visibility(did.index), safety: self.get_safety(did.index), + value: self.get_default_field(did.index), }) .collect(), adt_kind, @@ -1169,6 +1170,10 @@ impl<'a> CrateMetadataRef<'a> { self.root.tables.safety.get(self, id).unwrap_or_else(|| self.missing("safety", id)) } + fn get_default_field(self, id: DefIndex) -> Option<DefId> { + self.root.tables.default_fields.get(self, id).map(|d| d.decode(self)) + } + fn get_trait_item_def_id(self, id: DefIndex) -> Option<DefId> { self.root.tables.trait_item_def_id.get(self, id).map(|d| d.decode_from_cdata(self)) } @@ -1364,7 +1369,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 diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index a89096beb8c..f8c743d4f27 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}; @@ -269,7 +269,6 @@ provide! { tcx, def_id, other, cdata, lookup_default_body_stability => { table } lookup_deprecation_entry => { table } params_in_repr => { table } - unused_generic_params => { table_direct } def_kind => { cdata.def_kind(def_id.index) } impl_parent => { table } defaultness => { table_direct } @@ -345,7 +344,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 a34ea18f716..88906d71597 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}; @@ -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. @@ -1264,12 +1267,7 @@ fn should_encode_fn_sig(def_kind: DefKind) -> bool { fn should_encode_constness(def_kind: DefKind) -> bool { match def_kind { - DefKind::Fn - | DefKind::AssocFn - | DefKind::Closure - | DefKind::Impl { of_trait: true } - | DefKind::Variant - | DefKind::Ctor(..) => true, + DefKind::Fn | DefKind::AssocFn | DefKind::Closure | DefKind::Ctor(_, CtorKind::Fn) => true, DefKind::Struct | DefKind::Union @@ -1281,7 +1279,7 @@ fn should_encode_constness(def_kind: DefKind) -> bool { | DefKind::Static { .. } | DefKind::TyAlias | DefKind::OpaqueTy - | DefKind::Impl { of_trait: false } + | DefKind::Impl { .. } | DefKind::ForeignTy | DefKind::ConstParam | DefKind::InlineConst @@ -1296,6 +1294,8 @@ fn should_encode_constness(def_kind: DefKind) -> bool { | DefKind::LifetimeParam | DefKind::GlobalAsm | DefKind::ExternCrate + | DefKind::Ctor(_, CtorKind::Const) + | DefKind::Variant | DefKind::SyntheticCoroutineBody => false, } } @@ -1357,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); @@ -1401,6 +1401,13 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { continue; } + if def_kind == DefKind::Field + && let hir::Node::Field(field) = tcx.hir_node_by_def_id(local_id) + && let Some(anon) = field.default + { + record!(self.tables.default_fields[def_id] <- anon.def_id.to_def_id()); + } + if should_encode_span(def_kind) { let def_span = tcx.def_span(local_id); record!(self.tables.def_span[def_id] <- def_span); @@ -1767,10 +1774,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { { record!(self.tables.mir_coroutine_witnesses[def_id.to_def_id()] <- witnesses); } - - let instance = ty::InstanceKind::Item(def_id.to_def_id()); - let unused = tcx.unused_generic_params(instance); - self.tables.unused_generic_params.set(def_id.local_def_index, unused); } // Encode all the deduced parameter attributes for everything that has MIR, even for items @@ -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() @@ -2161,10 +2164,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { fn encode_dylib_dependency_formats(&mut self) -> LazyArray<Option<LinkagePreference>> { empty_proc_macro!(self); let formats = self.tcx.dependency_formats(()); - for (ty, arr) in formats.iter() { - if *ty != CrateType::Dylib { - continue; - } + if let Some(arr) = formats.get(&CrateType::Dylib) { return self.lazy_array(arr.iter().map(|slot| match *slot { Linkage::NotLinked | Linkage::IncludedFromDylib => None, diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index a486ad42482..f1872807aef 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -40,7 +40,7 @@ use rustc_span::symbol::{Ident, Symbol}; use rustc_span::{self, ExpnData, ExpnHash, ExpnId, Span}; 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; @@ -395,7 +395,6 @@ define_tables! { inherent_impls: Table<DefIndex, LazyArray<DefIndex>>, associated_types_for_impl_traits_in_associated_fn: Table<DefIndex, LazyArray<DefId>>, opt_rpitit_info: Table<DefIndex, Option<LazyValue<ty::ImplTraitInTraitData>>>, - unused_generic_params: Table<DefIndex, UnusedGenericParams>, // Reexported names are not associated with individual `DefId`s, // e.g. a glob import can introduce a lot of names, all with the same `DefId`. // That's why the encoded list needs to contain `ModChild` structures describing all the names @@ -404,7 +403,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>>, @@ -451,6 +450,7 @@ define_tables! { trait_def: Table<DefIndex, LazyValue<ty::TraitDef>>, trait_item_def_id: Table<DefIndex, RawDefId>, expn_that_defined: Table<DefIndex, LazyValue<ExpnId>>, + default_fields: Table<DefIndex, LazyValue<DefId>>, params_in_repr: Table<DefIndex, LazyValue<BitSet<u32>>>, repr_options: Table<DefIndex, LazyValue<ReprOptions>>, // `def_keys` and `def_path_hashes` represent a lazy version of a 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..52233d407f2 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -85,7 +85,7 @@ 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, + [decode] attribute: rustc_hir::Attribute, [] name_set: rustc_data_structures::unord::UnordSet<rustc_span::symbol::Symbol>, [] ordered_name_set: rustc_data_structures::fx::FxIndexSet<rustc_span::symbol::Symbol>, [] pats: rustc_middle::ty::PatternKind<'tcx>, diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 0c701c834f2..fc3cbfd7b3e 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -9,11 +9,11 @@ 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 crate::hir::ModuleItems; use crate::middle::debugger_visualizer::DebuggerVisualizerFile; @@ -381,7 +381,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 +792,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/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 44428471a5f..509063c40d7 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -1,5 +1,5 @@ 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_target::spec::SanitizerSet; diff --git a/compiler/rustc_middle/src/middle/dependency_format.rs b/compiler/rustc_middle/src/middle/dependency_format.rs index a3aff9a1101..e3b40b64157 100644 --- a/compiler/rustc_middle/src/middle/dependency_format.rs +++ b/compiler/rustc_middle/src/middle/dependency_format.rs @@ -7,6 +7,7 @@ // FIXME: move this file to rustc_metadata::dependency_format, but // this will introduce circular dependency between rustc_metadata and rustc_middle +use rustc_data_structures::fx::FxIndexMap; use rustc_macros::{Decodable, Encodable, HashStable}; use rustc_session::config::CrateType; @@ -18,7 +19,7 @@ pub type DependencyList = Vec<Linkage>; /// A mapping of all required dependencies for a particular flavor of output. /// /// This is local to the tcx, and is generally relevant to one session. -pub type Dependencies = Vec<(CrateType, DependencyList)>; +pub type Dependencies = FxIndexMap<CrateType, DependencyList>; #[derive(Copy, Clone, PartialEq, Debug, HashStable, Encodable, Decodable)] pub enum Linkage { diff --git a/compiler/rustc_middle/src/middle/limits.rs b/compiler/rustc_middle/src/middle/limits.rs index 270bcabcc86..3a3e84a87af 100644 --- a/compiler/rustc_middle/src/middle/limits.rs +++ b/compiler/rustc_middle/src/middle/limits.rs @@ -10,7 +10,7 @@ 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}; @@ -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/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 94d13021612..ece561c7493 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; @@ -392,7 +392,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 +475,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 7983329b0f7..d8a39191920 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -8,6 +8,7 @@ use rustc_session::config::RemapPathScopeComponents; use rustc_span::{DUMMY_SP, Span}; use rustc_type_ir::visit::TypeVisitableExt; +use super::interpret::ReportedErrorInfo; use crate::mir::interpret::{AllocId, ConstAllocation, ErrorHandled, Scalar, alloc_range}; use crate::mir::{Promoted, pretty_print_const_value}; use crate::ty::print::{pretty_print_const, with_no_trimmed_paths}; @@ -331,7 +332,10 @@ impl<'tcx> Const<'tcx> { ConstKind::Expr(_) => { bug!("Normalization of `ty::ConstKind::Expr` is unimplemented") } - _ => Err(tcx.dcx().delayed_bug("Unevaluated `ty::Const` in MIR body").into()), + _ => Err(ReportedErrorInfo::non_const_eval_error( + tcx.dcx().delayed_bug("Unevaluated `ty::Const` in MIR body"), + ) + .into()), } } Const::Unevaluated(uneval, _) => { @@ -463,6 +467,9 @@ impl<'tcx> Const<'tcx> { let const_val = tcx.valtree_to_const_val((ty, valtree)); Self::Val(const_val, ty) } + ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args }) => { + Self::Unevaluated(UnevaluatedConst { def, args, promoted: None }, ty) + } _ => Self::Ty(ty, c), } } diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index b7410ca5f18..962176290df 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -3,6 +3,7 @@ use std::fmt::{self, Debug, Formatter}; use rustc_index::IndexVec; +use rustc_index::bit_set::BitSet; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_span::Span; @@ -310,3 +311,41 @@ pub struct MCDCDecisionSpan { pub decision_depth: u16, pub num_conditions: usize, } + +/// Summarizes coverage IDs inserted by the `InstrumentCoverage` MIR pass +/// (for compiler option `-Cinstrument-coverage`), after MIR optimizations +/// have had a chance to potentially remove some of them. +/// +/// Used by the `coverage_ids_info` query. +#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable)] +pub struct CoverageIdsInfo { + pub counters_seen: BitSet<CounterId>, + pub zero_expressions: BitSet<ExpressionId>, +} + +impl CoverageIdsInfo { + /// Coverage codegen needs to know how many coverage counters are ever + /// incremented within a function, so that it can set the `num-counters` + /// argument of the `llvm.instrprof.increment` intrinsic. + /// + /// This may be less than the highest counter ID emitted by the + /// InstrumentCoverage MIR pass, if the highest-numbered counter increments + /// were removed by MIR optimizations. + pub fn num_counters_after_mir_opts(&self) -> u32 { + // FIXME(Zalathar): Currently this treats an unused counter as "used" + // if its ID is less than that of the highest counter that really is + // used. Fixing this would require adding a renumbering step somewhere. + self.counters_seen.last_set_in(..).map_or(0, |max| max.as_u32() + 1) + } + + /// Returns `true` if the given term is known to have a value of zero, taking + /// into account knowledge of which counters are unused and which expressions + /// are always zero. + pub fn is_zero_term(&self, term: CovTerm) -> bool { + match term { + CovTerm::Zero => true, + CovTerm::Counter(id) => !self.counters_seen.contains(id), + CovTerm::Expression(id) => self.zero_expressions.contains(id), + } + } +} diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 509f2667b35..d6f8fed755f 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -643,6 +643,28 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes> Ok(()) } + /// Initialize all previously uninitialized bytes in the entire allocation, and set + /// provenance of everything to `Wildcard`. Before calling this, make sure all + /// provenance in this allocation is exposed! + pub fn prepare_for_native_write(&mut self) -> AllocResult { + let full_range = AllocRange { start: Size::ZERO, size: Size::from_bytes(self.len()) }; + // Overwrite uninitialized bytes with 0, to ensure we don't leak whatever their value happens to be. + for chunk in self.init_mask.range_as_init_chunks(full_range) { + if !chunk.is_init() { + let uninit_bytes = &mut self.bytes + [chunk.range().start.bytes_usize()..chunk.range().end.bytes_usize()]; + uninit_bytes.fill(0); + } + } + // Mark everything as initialized now. + self.mark_init(full_range, true); + + // Set provenance of all bytes to wildcard. + self.provenance.write_wildcards(self.len()); + + Ok(()) + } + /// Remove all provenance in the given memory range. pub fn clear_provenance(&mut self, cx: &impl HasDataLayout, range: AllocRange) -> AllocResult { self.provenance.clear(range, cx)?; diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs index 5c47fc6a399..3a83b184d83 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs @@ -195,6 +195,25 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { Ok(()) } + + /// Overwrites all provenance in the allocation with wildcard provenance. + /// + /// Provided for usage in Miri and panics otherwise. + pub fn write_wildcards(&mut self, alloc_size: usize) { + assert!( + Prov::OFFSET_IS_ADDR, + "writing wildcard provenance is not supported when `OFFSET_IS_ADDR` is false" + ); + let wildcard = Prov::WILDCARD.unwrap(); + + // Remove all pointer provenances, then write wildcards into the whole byte range. + self.ptrs.clear(); + let last = Size::from_bytes(alloc_size); + let bytes = self.bytes.get_or_insert_with(Box::default); + for offset in Size::ZERO..last { + bytes.insert(offset, wildcard); + } + } } /// A partial, owned list of provenance to transfer into another allocation. diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index ad5d678178d..fbada6ec405 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -28,10 +28,10 @@ pub enum ErrorHandled { TooGeneric(Span), } -impl From<ErrorGuaranteed> for ErrorHandled { +impl From<ReportedErrorInfo> for ErrorHandled { #[inline] - fn from(error: ErrorGuaranteed) -> ErrorHandled { - ErrorHandled::Reported(error.into(), DUMMY_SP) + fn from(error: ReportedErrorInfo) -> ErrorHandled { + ErrorHandled::Reported(error, DUMMY_SP) } } @@ -65,6 +65,20 @@ pub struct ReportedErrorInfo { impl ReportedErrorInfo { #[inline] + pub fn const_eval_error(error: ErrorGuaranteed) -> ReportedErrorInfo { + ReportedErrorInfo { allowed_in_infallible: false, error } + } + + /// Use this when the error that led to this is *not* a const-eval error + /// (e.g., a layout or type checking error). + #[inline] + pub fn non_const_eval_error(error: ErrorGuaranteed) -> ReportedErrorInfo { + ReportedErrorInfo { allowed_in_infallible: true, error } + } + + /// Use this when the error that led to this *is* a const-eval error, but + /// we do allow it to occur in infallible constants (e.g., resource exhaustion). + #[inline] pub fn allowed_in_infallible(error: ErrorGuaranteed) -> ReportedErrorInfo { ReportedErrorInfo { allowed_in_infallible: true, error } } @@ -74,13 +88,6 @@ impl ReportedErrorInfo { } } -impl From<ErrorGuaranteed> for ReportedErrorInfo { - #[inline] - fn from(error: ErrorGuaranteed) -> ReportedErrorInfo { - ReportedErrorInfo { allowed_in_infallible: false, error } - } -} - impl Into<ErrorGuaranteed> for ReportedErrorInfo { #[inline] fn into(self) -> ErrorGuaranteed { @@ -180,12 +187,6 @@ fn print_backtrace(backtrace: &Backtrace) { eprintln!("\n\nAn error occurred in the MIR interpreter:\n{backtrace}"); } -impl From<ErrorGuaranteed> for InterpErrorInfo<'_> { - fn from(err: ErrorGuaranteed) -> Self { - InterpErrorKind::InvalidProgram(InvalidProgramInfo::AlreadyReported(err.into())).into() - } -} - impl From<ErrorHandled> for InterpErrorInfo<'_> { fn from(err: ErrorHandled) -> Self { InterpErrorKind::InvalidProgram(match err { diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs index 1d5afe22573..25c7c26ddd9 100644 --- a/compiler/rustc_middle/src/mir/interpret/pointer.rs +++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs @@ -66,6 +66,9 @@ pub trait Provenance: Copy + fmt::Debug + 'static { /// pointer, and implement ptr-to-int transmutation by stripping provenance. const OFFSET_IS_ADDR: bool; + /// If wildcard provenance is implemented, contains the unique, general wildcard provenance variant. + const WILDCARD: Option<Self>; + /// Determines how a pointer should be printed. fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result; @@ -168,6 +171,9 @@ impl Provenance for CtfeProvenance { // so ptr-to-int casts are not possible (since we do not know the global physical offset). const OFFSET_IS_ADDR: bool = false; + // `CtfeProvenance` does not implement wildcard provenance. + const WILDCARD: Option<Self> = None; + fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Print AllocId. fmt::Debug::fmt(&ptr.provenance.alloc_id(), f)?; // propagates `alternate` flag @@ -197,6 +203,9 @@ impl Provenance for AllocId { // so ptr-to-int casts are not possible (since we do not know the global physical offset). const OFFSET_IS_ADDR: bool = false; + // `AllocId` does not implement wildcard provenance. + const WILDCARD: Option<Self> = None; + fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Forward `alternate` flag to `alloc_id` printing. if f.alternate() { diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index e540f0194ec..f7f38575bd0 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -6,6 +6,7 @@ use tracing::{debug, instrument}; use super::{ ErrorHandled, EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult, GlobalId, + ReportedErrorInfo, }; use crate::mir; use crate::query::TyCtxtEnsure; @@ -81,7 +82,9 @@ impl<'tcx> TyCtxt<'tcx> { // For errors during resolution, we deliberately do not point at the usage site of the constant, // since for these errors the place the constant is used shouldn't matter. Ok(None) => Err(ErrorHandled::TooGeneric(DUMMY_SP)), - Err(err) => Err(ErrorHandled::Reported(err.into(), DUMMY_SP)), + Err(err) => { + Err(ErrorHandled::Reported(ReportedErrorInfo::non_const_eval_error(err), DUMMY_SP)) + } } } @@ -138,7 +141,9 @@ impl<'tcx> TyCtxt<'tcx> { // For errors during resolution, we deliberately do not point at the usage site of the constant, // since for these errors the place the constant is used shouldn't matter. Ok(None) => Err(ErrorHandled::TooGeneric(DUMMY_SP)), - Err(err) => Err(ErrorHandled::Reported(err.into(), DUMMY_SP)), + Err(err) => { + Err(ErrorHandled::Reported(ReportedErrorInfo::non_const_eval_error(err), DUMMY_SP)) + } } } diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 161716610fe..dcb680c283c 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; @@ -19,6 +19,7 @@ use rustc_target::spec::SymbolVisibility; use tracing::debug; use crate::dep_graph::{DepNode, WorkProduct, WorkProductId}; +use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::ty::{GenericArgs, Instance, InstanceKind, SymbolName, TyCtxt}; /// Describes how a monomorphization will be instantiated in object files. @@ -104,7 +105,9 @@ impl<'tcx> MonoItem<'tcx> { let entry_def_id = tcx.entry_fn(()).map(|(id, _)| id); // If this function isn't inlined or otherwise has an extern // indicator, then we'll be creating a globally shared version. - if tcx.codegen_fn_attrs(instance.def_id()).contains_extern_indicator() + let codegen_fn_attrs = tcx.codegen_fn_attrs(instance.def_id()); + if codegen_fn_attrs.contains_extern_indicator() + || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) || !instance.def.generates_cgu_internal_copy(tcx) || Some(instance.def_id()) == entry_def_id { @@ -291,10 +294,22 @@ pub enum Linkage { Common, } +/// Specifies the symbol visibility with regards to dynamic linking. +/// +/// Visibility doesn't have any effect when linkage is internal. +/// +/// DSO means dynamic shared object, that is a dynamically linked executable or dylib. #[derive(Copy, Clone, PartialEq, Debug, HashStable)] pub enum Visibility { + /// Export the symbol from the DSO and apply overrides of the symbol by outside DSOs to within + /// the DSO if the object file format supports this. Default, + /// Hide the symbol outside of the defining DSO even when external linkage is used to export it + /// from the object file. Hidden, + /// Export the symbol from the DSO, but don't apply overrides of the symbol by outside DSOs to + /// within the DSO. Equivalent to default visibility with object file formats that don't support + /// overriding exported symbols by another DSO. Protected, } diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 80dfcbf2e69..f690359e012 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -8,7 +8,7 @@ use rustc_abi::{FieldIdx, VariantIdx}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::LocalDefId; -use rustc_index::bit_set::{BitMatrix, BitSet}; +use rustc_index::bit_set::BitMatrix; use rustc_index::{Idx, IndexVec}; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_span::Span; @@ -16,7 +16,6 @@ use rustc_span::symbol::Symbol; use smallvec::SmallVec; use super::{ConstValue, SourceInfo}; -use crate::mir; use crate::ty::fold::fold_regions; use crate::ty::{self, CoroutineArgsExt, OpaqueHiddenType, Ty, TyCtxt}; @@ -351,30 +350,3 @@ pub struct DestructuredConstant<'tcx> { pub variant: Option<VariantIdx>, pub fields: &'tcx [(ConstValue<'tcx>, Ty<'tcx>)], } - -/// Summarizes coverage IDs inserted by the `InstrumentCoverage` MIR pass -/// (for compiler option `-Cinstrument-coverage`), after MIR optimizations -/// have had a chance to potentially remove some of them. -/// -/// Used by the `coverage_ids_info` query. -#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable)] -pub struct CoverageIdsInfo { - pub counters_seen: BitSet<mir::coverage::CounterId>, - pub expressions_seen: BitSet<mir::coverage::ExpressionId>, -} - -impl CoverageIdsInfo { - /// Coverage codegen needs to know how many coverage counters are ever - /// incremented within a function, so that it can set the `num-counters` - /// argument of the `llvm.instrprof.increment` intrinsic. - /// - /// This may be less than the highest counter ID emitted by the - /// InstrumentCoverage MIR pass, if the highest-numbered counter increments - /// were removed by MIR optimizations. - pub fn num_counters_after_mir_opts(&self) -> u32 { - // FIXME(Zalathar): Currently this treats an unused counter as "used" - // if its ID is less than that of the highest counter that really is - // used. Fixing this would require adding a renumbering step somewhere. - self.counters_seen.last_set_in(..).map_or(0, |max| max.as_u32() + 1) - } -} diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 013847f0b2d..9afcf9466b6 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, diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index 970dc72e1ff..66fec2dd0f7 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -41,7 +41,8 @@ pub trait Key: Sized { None } - fn ty_def_id(&self) -> Option<DefId> { + /// Used to detect when ADT def ids are used as keys in a cycle for better error reporting. + fn def_id_for_ty_in_cycle(&self) -> Option<DefId> { None } } @@ -423,7 +424,7 @@ impl<'tcx> Key for Ty<'tcx> { DUMMY_SP } - fn ty_def_id(&self) -> Option<DefId> { + fn def_id_for_ty_in_cycle(&self) -> Option<DefId> { match *self.kind() { ty::Adt(adt, _) => Some(adt.did()), ty::Coroutine(def_id, ..) => Some(def_id), @@ -471,8 +472,8 @@ impl<'tcx, T: Key> Key for ty::PseudoCanonicalInput<'tcx, T> { self.value.default_span(tcx) } - fn ty_def_id(&self) -> Option<DefId> { - self.value.ty_def_id() + fn def_id_for_ty_in_cycle(&self) -> Option<DefId> { + self.value.def_id_for_ty_in_cycle() } } @@ -593,7 +594,7 @@ impl<'tcx> Key for (ValidityRequirement, ty::PseudoCanonicalInput<'tcx, Ty<'tcx> DUMMY_SP } - fn ty_def_id(&self) -> Option<DefId> { + fn def_id_for_ty_in_cycle(&self) -> Option<DefId> { match self.1.value.kind() { ty::Adt(adt, _) => Some(adt.did()), _ => None, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 8861f081485..9475e629683 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -44,7 +44,7 @@ use rustc_span::source_map::Spanned; use rustc_span::symbol::Symbol; use rustc_span::{DUMMY_SP, Span}; 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; @@ -82,7 +82,7 @@ use crate::ty::print::{PrintTraitRefExt, describe_as_module}; use crate::ty::util::AlwaysRequiresDrop; use crate::ty::{ self, CrateInherentImpls, GenericArg, GenericArgsRef, PseudoCanonicalInput, Ty, TyCtxt, - TyCtxtFeed, UnusedGenericParams, + TyCtxtFeed, }; use crate::{dep_graph, mir, thir}; @@ -276,7 +276,7 @@ rustc_queries! { } /// The root query triggering all analysis passes like typeck or borrowck. - query analysis(key: ()) -> Result<(), ErrorGuaranteed> { + query analysis(key: ()) { eval_always desc { "running analysis passes on this crate" } } @@ -581,7 +581,7 @@ rustc_queries! { /// Summarizes coverage IDs inserted by the `InstrumentCoverage` MIR pass /// (for compiler option `-Cinstrument-coverage`), after MIR optimizations /// have had a chance to potentially remove some of them. - query coverage_ids_info(key: ty::InstanceKind<'tcx>) -> &'tcx mir::CoverageIdsInfo { + query coverage_ids_info(key: ty::InstanceKind<'tcx>) -> &'tcx mir::coverage::CoverageIdsInfo { desc { |tcx| "retrieving coverage IDs info from MIR for `{}`", tcx.def_path_str(key.def_id()) } arena_cache } @@ -746,7 +746,10 @@ rustc_queries! { desc { |tcx| "computing drop-check constraints for `{}`", tcx.def_path_str(key) } } - /// Returns `true` if this is a const fn / const impl. + /// Returns the constness of function-like things (tuple struct/variant constructors, functions, + /// methods) + /// + /// Will ICE if used on things that are always const or never const. /// /// **Do not call this function manually.** It is only meant to cache the base data for the /// higher-level functions. Consider using `is_const_fn` or `is_const_trait_impl` instead. @@ -1264,7 +1267,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 } @@ -2048,15 +2051,6 @@ rustc_queries! { desc { "getting codegen unit `{sym}`" } } - query unused_generic_params(key: ty::InstanceKind<'tcx>) -> UnusedGenericParams { - cache_on_disk_if { key.def_id().is_local() } - desc { - |tcx| "determining which generic parameters are unused by `{}`", - tcx.def_path_str(key.def_id()) - } - separate_provide_extern - } - query backend_optimization_level(_: ()) -> OptLevel { desc { "optimization level used by backend" } } @@ -2239,7 +2233,7 @@ rustc_queries! { } /// Returns the Rust target features for the current target. These are not always the same as LLVM target features! - query rust_target_features(_: CrateNum) -> &'tcx UnordMap<String, rustc_target::target_features::Stability> { + query rust_target_features(_: CrateNum) -> &'tcx UnordMap<String, rustc_target::target_features::StabilityComputed> { arena_cache eval_always desc { "looking up Rust target features" } diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index 3849cb72668..2c22f7b8f49 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -23,7 +23,7 @@ use rustc_session::Session; use rustc_span::hygiene::{ ExpnId, HygieneDecodeContext, HygieneEncodeContext, SyntaxContext, SyntaxContextData, }; -use rustc_span::source_map::{SourceMap, Spanned}; +use rustc_span::source_map::Spanned; use rustc_span::{ BytePos, CachingSourceMapView, ExpnData, ExpnHash, Pos, RelativeBytePos, SourceFile, Span, SpanDecoder, SpanEncoder, StableSourceFileId, Symbol, @@ -49,7 +49,7 @@ const SYMBOL_PREINTERNED: u8 = 2; /// previous compilation session. This data will eventually include the results /// of a few selected queries (like `typeck` and `mir_optimized`) and /// any side effects that have been emitted during a query. -pub struct OnDiskCache<'sess> { +pub struct OnDiskCache { // The complete cache data in serialized form. serialized_data: RwLock<Option<Mmap>>, @@ -57,7 +57,6 @@ pub struct OnDiskCache<'sess> { // session. current_side_effects: Lock<FxHashMap<DepNodeIndex, QuerySideEffects>>, - source_map: &'sess SourceMap, file_index_to_stable_id: FxHashMap<SourceFileIndex, EncodedSourceFileId>, // Caches that are populated lazily during decoding. @@ -151,12 +150,12 @@ impl EncodedSourceFileId { } } -impl<'sess> OnDiskCache<'sess> { +impl OnDiskCache { /// Creates a new `OnDiskCache` instance from the serialized data in `data`. /// /// The serialized cache has some basic integrity checks, if those checks indicate that the /// on-disk data is corrupt, an error is returned. - pub fn new(sess: &'sess Session, data: Mmap, start_pos: usize) -> Result<Self, ()> { + pub fn new(sess: &Session, data: Mmap, start_pos: usize) -> Result<Self, ()> { assert!(sess.opts.incremental.is_some()); let mut decoder = MemDecoder::new(&data, start_pos)?; @@ -175,7 +174,6 @@ impl<'sess> OnDiskCache<'sess> { serialized_data: RwLock::new(Some(data)), file_index_to_stable_id: footer.file_index_to_stable_id, file_index_to_file: Default::default(), - source_map: sess.source_map(), current_side_effects: Default::default(), query_result_index: footer.query_result_index.into_iter().collect(), prev_side_effects_index: footer.side_effects_index.into_iter().collect(), @@ -187,12 +185,11 @@ impl<'sess> OnDiskCache<'sess> { }) } - pub fn new_empty(source_map: &'sess SourceMap) -> Self { + pub fn new_empty() -> Self { Self { serialized_data: RwLock::new(None), file_index_to_stable_id: Default::default(), file_index_to_file: Default::default(), - source_map, current_side_effects: Default::default(), query_result_index: Default::default(), prev_side_effects_index: Default::default(), @@ -423,7 +420,7 @@ impl<'sess> OnDiskCache<'sess> { } fn with_decoder<'a, 'tcx, T, F: for<'s> FnOnce(&mut CacheDecoder<'s, 'tcx>) -> T>( - &'sess self, + &self, tcx: TyCtxt<'tcx>, pos: AbsoluteBytePos, f: F, @@ -436,7 +433,6 @@ impl<'sess> OnDiskCache<'sess> { tcx, opaque: MemDecoder::new(serialized_data.as_deref().unwrap_or(&[]), pos.to_usize()) .unwrap(), - source_map: self.source_map, file_index_to_file: &self.file_index_to_file, file_index_to_stable_id: &self.file_index_to_stable_id, alloc_decoding_session: self.alloc_decoding_state.new_decoding_session(), @@ -457,7 +453,6 @@ impl<'sess> OnDiskCache<'sess> { pub struct CacheDecoder<'a, 'tcx> { tcx: TyCtxt<'tcx>, opaque: MemDecoder<'a>, - source_map: &'a SourceMap, file_index_to_file: &'a Lock<FxHashMap<SourceFileIndex, Lrc<SourceFile>>>, file_index_to_stable_id: &'a FxHashMap<SourceFileIndex, EncodedSourceFileId>, alloc_decoding_session: AllocDecodingSession<'a>, @@ -470,8 +465,7 @@ pub struct CacheDecoder<'a, 'tcx> { impl<'a, 'tcx> CacheDecoder<'a, 'tcx> { #[inline] fn file_index_to_file(&self, index: SourceFileIndex) -> Lrc<SourceFile> { - let CacheDecoder { tcx, file_index_to_file, file_index_to_stable_id, source_map, .. } = - *self; + let CacheDecoder { tcx, file_index_to_file, file_index_to_stable_id, .. } = *self; Lrc::clone(file_index_to_file.borrow_mut().entry(index).or_insert_with(|| { let source_file_id = &file_index_to_stable_id[&index]; @@ -490,7 +484,8 @@ impl<'a, 'tcx> CacheDecoder<'a, 'tcx> { self.tcx.import_source_files(source_file_cnum); } - source_map + tcx.sess + .source_map() .source_file_by_stable_id(source_file_id.stable_source_file_id) .expect("failed to lookup `SourceFile` in new context") })) @@ -803,7 +798,7 @@ macro_rules! impl_ref_decoder { impl_ref_decoder! {<'tcx> Span, - rustc_ast::Attribute, + rustc_hir::Attribute, rustc_span::symbol::Ident, ty::Variance, rustc_span::def_id::DefId, diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 20ba1b27c0e..3337f7ceee7 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -44,16 +44,16 @@ pub struct DynamicQuery<'tcx, C: QueryCache> { pub format_value: fn(&C::Value) -> String, } -pub struct QuerySystemFns<'tcx> { +pub struct QuerySystemFns { pub engine: QueryEngine, pub local_providers: Providers, pub extern_providers: ExternProviders, - pub encode_query_results: fn( + pub encode_query_results: for<'tcx> fn( tcx: TyCtxt<'tcx>, encoder: &mut CacheEncoder<'_, 'tcx>, query_result_index: &mut EncodedDepNodeIndex, ), - pub try_mark_green: fn(tcx: TyCtxt<'tcx>, dep_node: &dep_graph::DepNode) -> bool, + pub try_mark_green: for<'tcx> fn(tcx: TyCtxt<'tcx>, dep_node: &dep_graph::DepNode) -> bool, } pub struct QuerySystem<'tcx> { @@ -66,9 +66,9 @@ pub struct QuerySystem<'tcx> { /// Do not access this directly. It is only meant to be used by /// `DepGraph::try_mark_green()` and the query infrastructure. /// This is `None` if we are not incremental compilation mode - pub on_disk_cache: Option<OnDiskCache<'tcx>>, + pub on_disk_cache: Option<OnDiskCache>, - pub fns: QuerySystemFns<'tcx>, + pub fns: QuerySystemFns, pub jobs: AtomicU64, } @@ -323,7 +323,7 @@ macro_rules! define_callbacks { // Increase this limit if necessary, but do try to keep the size low if possible #[cfg(target_pointer_width = "64")] const _: () = { - if mem::size_of::<Key<'static>>() > 80 { + if mem::size_of::<Key<'static>>() > 88 { panic!("{}", concat!( "the query `", stringify!($name), diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 9cf6bc1b777..86014c34b45 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -158,8 +158,21 @@ pub struct AdtExpr<'tcx> { pub user_ty: UserTy<'tcx>, pub fields: Box<[FieldExpr]>, - /// The base, e.g. `Foo {x: 1, .. base}`. - pub base: Option<FruInfo<'tcx>>, + /// The base, e.g. `Foo {x: 1, ..base}`. + pub base: AdtExprBase<'tcx>, +} + +#[derive(Clone, Debug, HashStable)] +pub enum AdtExprBase<'tcx> { + /// A struct expression where all the fields are explicitly enumerated: `Foo { a, b }`. + None, + /// A struct expression with a "base", an expression of the same type as the outer struct that + /// will be used to populate any fields not explicitly mentioned: `Foo { ..base }` + Base(FruInfo<'tcx>), + /// A struct expression with a `..` tail but no "base" expression. The values from the struct + /// fields' default values will be used to populate any fields not explicitly mentioned: + /// `Foo { .. }`. + DefaultFields(Box<[Ty<'tcx>]>), } #[derive(Clone, Debug, HashStable)] diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index 81202a6eaad..64bac12b266 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -2,6 +2,7 @@ use super::{ AdtExpr, Arm, Block, ClosureExpr, Expr, ExprKind, InlineAsmExpr, InlineAsmOperand, Pat, PatKind, Stmt, StmtKind, Thir, }; +use crate::thir::AdtExprBase; pub trait Visitor<'thir, 'tcx: 'thir>: Sized { fn thir(&self) -> &'thir Thir<'tcx>; @@ -127,7 +128,7 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( for field in &**fields { visitor.visit_expr(&visitor.thir()[field.expr]); } - if let Some(base) = base { + if let AdtExprBase::Base(base) = base { visitor.visit_expr(&visitor.thir()[base.base]); } } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 0a77c3bc42f..8c434265d27 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -316,8 +316,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 +530,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/query.rs b/compiler/rustc_middle/src/traits/query.rs index eeed5118592..f049da95f29 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -149,11 +149,21 @@ pub struct CandidateStep<'tcx> { /// `foo.by_raw_ptr()` will work and `foo.by_ref()` won't. pub from_unsafe_deref: bool, pub unsize: bool, + /// We will generate CandidateSteps which are reachable via a chain + /// of following `Receiver`. The first 'n' of those will be reachable + /// by following a chain of 'Deref' instead (since there's a blanket + /// implementation of Receiver for Deref). + /// We use the entire set of steps when identifying method candidates + /// (e.g. identifying relevant `impl` blocks) but only those that are + /// reachable via Deref when examining what the receiver type can + /// be converted into by autodereffing. + pub reachable_via_deref: bool, } #[derive(Copy, Clone, Debug, HashStable)] pub struct MethodAutoderefStepsResult<'tcx> { - /// The valid autoderef steps that could be found. + /// The valid autoderef steps that could be found by following a chain + /// of `Receiver<Target=T>` or `Deref<Target=T>` trait implementations. pub steps: &'tcx [CandidateStep<'tcx>], /// If Some(T), a type autoderef reported an error on. pub opt_bad_ty: Option<&'tcx MethodAutoderefBadTy<'tcx>>, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 3cbb2a3acf5..24e44e5ba10 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; @@ -585,6 +585,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.trait_def(trait_def_id).implement_via_object } + fn trait_is_unsafe(self, trait_def_id: Self::DefId) -> bool { + self.trait_def(trait_def_id).safety.is_unsafe() + } + fn is_impl_trait_in_trait(self, def_id: DefId) -> bool { self.is_impl_trait_in_trait(def_id) } @@ -718,7 +722,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 { @@ -1115,10 +1119,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, } @@ -1343,46 +1347,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. @@ -1475,7 +1439,7 @@ impl<'tcx> TyCtxt<'tcx> { self.mk_adt_def_from_data(ty::AdtDefData::new(self, did, kind, variants, repr)) } - /// Allocates a read-only byte or string literal for `mir::interpret`. + /// Allocates a read-only byte or string literal for `mir::interpret` with alignment 1. /// Returns the same `AllocId` if called again with the same bytes. pub fn allocate_bytes_dedup(self, bytes: &[u8], salt: usize) -> interpret::AllocId { // Create an allocation that just contains these bytes. @@ -1526,7 +1490,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, @@ -1538,7 +1503,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); }); @@ -1547,7 +1513,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, @@ -1571,8 +1537,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) @@ -1979,7 +1960,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); @@ -1989,10 +1970,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) { @@ -2001,7 +1979,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. @@ -2116,6 +2094,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 { @@ -2517,7 +2508,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 })) } @@ -3137,7 +3128,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Whether the trait impl is marked const. This does not consider stability or feature gates. pub fn is_const_trait_impl(self, def_id: DefId) -> bool { self.def_kind(def_id) == DefKind::Impl { of_trait: true } - && self.constness(def_id) == hir::Constness::Const + && self.impl_trait_header(def_id).unwrap().constness == hir::Constness::Const } pub fn intrinsic(self, def_id: impl IntoQueryParam<DefId> + Copy) -> Option<ty::IntrinsicDef> { @@ -3235,12 +3226,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 fd807882e0f..406e732744b 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -1,11 +1,12 @@ //! Diagnostics related methods for `Ty`. -use std::borrow::Cow; use std::fmt::Write; use std::ops::ControlFlow; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{Applicability, Diag, DiagArgValue, IntoDiagArg, into_diag_arg_using_display}; +use rustc_errors::{ + Applicability, Diag, DiagArgValue, IntoDiagArg, into_diag_arg_using_display, pluralize, +}; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, LangItem, PredicateOrigin, WherePredicateKind}; @@ -161,7 +162,7 @@ pub fn suggest_arbitrary_trait_bound<'tcx>( true } -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] enum SuggestChangingConstraintsMessage<'a> { RestrictBoundFurther, RestrictType { ty: &'a str }, @@ -172,7 +173,7 @@ enum SuggestChangingConstraintsMessage<'a> { fn suggest_changing_unsized_bound( generics: &hir::Generics<'_>, - suggestions: &mut Vec<(Span, String, SuggestChangingConstraintsMessage<'_>)>, + suggestions: &mut Vec<(Span, String, String, SuggestChangingConstraintsMessage<'_>)>, param: &hir::GenericParam<'_>, def_id: Option<DefId>, ) { @@ -207,7 +208,8 @@ fn suggest_changing_unsized_bound( continue; } - let mut push_suggestion = |sp, msg| suggestions.push((sp, String::new(), msg)); + let mut push_suggestion = + |sp, msg| suggestions.push((sp, "Sized".to_string(), String::new(), msg)); if predicate.bounds.len() == unsized_bounds.len() { // All the bounds are unsized bounds, e.g. @@ -278,8 +280,25 @@ pub fn suggest_constraining_type_params<'a>( span_to_replace: Option<Span>, ) -> bool { let mut grouped = FxHashMap::default(); + let mut unstable_suggestion = false; param_names_and_constraints.for_each(|(param_name, constraint, def_id)| { - grouped.entry(param_name).or_insert(Vec::new()).push((constraint, def_id)) + let stable = match def_id { + Some(def_id) => match tcx.lookup_stability(def_id) { + Some(s) => s.level.is_stable(), + None => true, + }, + None => true, + }; + if stable || tcx.sess.is_nightly_build() { + grouped.entry(param_name).or_insert(Vec::new()).push(( + constraint, + def_id, + if stable { "" } else { "unstable " }, + )); + if !stable { + unstable_suggestion = true; + } + } }); let mut applicability = Applicability::MachineApplicable; @@ -290,16 +309,21 @@ 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() { + if let Some((_, def_id, _)) = sized_constraints.next() { applicability = Applicability::MaybeIncorrect; err.span_label(param.span, "this type parameter needs to be `Sized`"); suggest_changing_unsized_bound(generics, &mut suggestions, param, def_id); } } + let bound_message = if constraints.iter().any(|(_, def_id, _)| def_id.is_none()) { + SuggestChangingConstraintsMessage::RestrictBoundFurther + } else { + SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name } + }; // in the scenario like impl has stricter requirements than trait, // we should not suggest restrict bound on the impl, here we double check @@ -312,15 +336,54 @@ pub fn suggest_constraining_type_params<'a>( .collect(); constraints - .retain(|(_, def_id)| def_id.map_or(true, |def| !bound_trait_defs.contains(&def))); + .retain(|(_, def_id, _)| def_id.map_or(true, |def| !bound_trait_defs.contains(&def))); if constraints.is_empty() { continue; } - let mut constraint = constraints.iter().map(|&(c, _)| c).collect::<Vec<_>>(); + let mut constraint = constraints.iter().map(|&(c, _, _)| c).collect::<Vec<_>>(); constraint.sort(); constraint.dedup(); + let all_known = constraints.iter().all(|&(_, def_id, _)| def_id.is_some()); + let all_stable = constraints.iter().all(|&(_, _, stable)| stable.is_empty()); + let all_unstable = constraints.iter().all(|&(_, _, stable)| !stable.is_empty()); + let post = if all_stable || all_unstable { + // Don't redundantly say "trait `X`, trait `Y`", instead "traits `X` and `Y`" + let mut trait_names = constraints + .iter() + .map(|&(c, def_id, _)| match def_id { + None => format!("`{c}`"), + Some(def_id) => format!("`{}`", tcx.item_name(def_id)), + }) + .collect::<Vec<_>>(); + trait_names.sort(); + trait_names.dedup(); + let n = trait_names.len(); + let stable = if all_stable { "" } else { "unstable " }; + let trait_ = if all_known { format!("trait{}", pluralize!(n)) } else { String::new() }; + format!("{stable}{trait_}{}", match &trait_names[..] { + [t] => format!(" {t}"), + [ts @ .., last] => format!(" {} and {last}", ts.join(", ")), + [] => return false, + },) + } else { + // We're more explicit when there's a mix of stable and unstable traits. + let mut trait_names = constraints + .iter() + .map(|&(c, def_id, stable)| match def_id { + None => format!("`{c}`"), + Some(def_id) => format!("{stable}trait `{}`", tcx.item_name(def_id)), + }) + .collect::<Vec<_>>(); + trait_names.sort(); + trait_names.dedup(); + match &trait_names[..] { + [t] => t.to_string(), + [ts @ .., last] => format!("{} and {last}", ts.join(", ")), + [] => return false, + } + }; let constraint = constraint.join(" + "); let mut suggest_restrict = |span, bound_list_non_empty, open_paren_sp| { let suggestion = if span_to_replace.is_some() { @@ -333,13 +396,11 @@ pub fn suggest_constraining_type_params<'a>( format!(" {constraint}") }; - use SuggestChangingConstraintsMessage::RestrictBoundFurther; - if let Some(open_paren_sp) = open_paren_sp { - suggestions.push((open_paren_sp, "(".to_string(), RestrictBoundFurther)); - suggestions.push((span, format!("){suggestion}"), RestrictBoundFurther)); + suggestions.push((open_paren_sp, post.clone(), "(".to_string(), bound_message)); + suggestions.push((span, post.clone(), format!("){suggestion}"), bound_message)); } else { - suggestions.push((span, suggestion, RestrictBoundFurther)); + suggestions.push((span, post.clone(), suggestion, bound_message)); } }; @@ -397,7 +458,8 @@ pub fn suggest_constraining_type_params<'a>( // - insert: `, X: Bar` suggestions.push(( generics.tail_span_for_predicate_suggestion(), - constraints.iter().fold(String::new(), |mut string, &(constraint, _)| { + post, + constraints.iter().fold(String::new(), |mut string, &(constraint, _, _)| { write!(string, ", {param_name}: {constraint}").unwrap(); string }), @@ -426,6 +488,7 @@ pub fn suggest_constraining_type_params<'a>( // default (`<T=Foo>`), so we suggest adding `where T: Bar`. suggestions.push(( generics.tail_span_for_predicate_suggestion(), + post, format!("{where_prefix} {param_name}: {constraint}"), SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name }, )); @@ -439,6 +502,7 @@ pub fn suggest_constraining_type_params<'a>( if let Some(colon_span) = param.colon_span { suggestions.push(( colon_span.shrink_to_hi(), + post, format!(" {constraint}"), SuggestChangingConstraintsMessage::RestrictType { ty: param_name }, )); @@ -451,6 +515,7 @@ pub fn suggest_constraining_type_params<'a>( // - help: consider restricting this type parameter with `T: Foo` suggestions.push(( param.span.shrink_to_hi(), + post, format!(": {constraint}"), SuggestChangingConstraintsMessage::RestrictType { ty: param_name }, )); @@ -459,39 +524,46 @@ pub fn suggest_constraining_type_params<'a>( // FIXME: remove the suggestions that are from derive, as the span is not correct suggestions = suggestions .into_iter() - .filter(|(span, _, _)| !span.in_derive_expansion()) + .filter(|(span, _, _, _)| !span.in_derive_expansion()) .collect::<Vec<_>>(); - + let suggested = !suggestions.is_empty(); if suggestions.len() == 1 { - let (span, suggestion, msg) = suggestions.pop().unwrap(); + let (span, post, suggestion, msg) = suggestions.pop().unwrap(); let msg = match msg { SuggestChangingConstraintsMessage::RestrictBoundFurther => { - Cow::from("consider further restricting this bound") + format!("consider further restricting this bound") + } + SuggestChangingConstraintsMessage::RestrictTypeFurther { ty } + | SuggestChangingConstraintsMessage::RestrictType { ty } + if ty.starts_with("impl ") => + { + format!("consider restricting opaque type `{ty}` with {post}") } SuggestChangingConstraintsMessage::RestrictType { ty } => { - Cow::from(format!("consider restricting type parameter `{ty}`")) + format!("consider restricting type parameter `{ty}` with {post}") } SuggestChangingConstraintsMessage::RestrictTypeFurther { ty } => { - Cow::from(format!("consider further restricting type parameter `{ty}`")) + format!("consider further restricting type parameter `{ty}` with {post}") } SuggestChangingConstraintsMessage::RemoveMaybeUnsized => { - Cow::from("consider removing the `?Sized` bound to make the type parameter `Sized`") + format!("consider removing the `?Sized` bound to make the type parameter `Sized`") } SuggestChangingConstraintsMessage::ReplaceMaybeUnsizedWithSized => { - Cow::from("consider replacing `?Sized` with `Sized`") + format!("consider replacing `?Sized` with `Sized`") } }; err.span_suggestion_verbose(span, msg, suggestion, applicability); } else if suggestions.len() > 1 { + let post = if unstable_suggestion { " (some of them are unstable traits)" } else { "" }; err.multipart_suggestion_verbose( - "consider restricting type parameters", - suggestions.into_iter().map(|(span, suggestion, _)| (span, suggestion)).collect(), + format!("consider restricting type parameters{post}"), + suggestions.into_iter().map(|(span, _, suggestion, _)| (span, suggestion)).collect(), applicability, ); } - true + suggested } /// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for. diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 3d4ce112a64..49b5588e261 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -19,8 +19,8 @@ use crate::error; use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::ty::print::{FmtPrinter, Printer, shrunk_instance_name}; use crate::ty::{ - self, EarlyBinder, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, - TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, + self, EarlyBinder, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, + TypeVisitable, TypeVisitableExt, TypeVisitor, }; /// An `InstanceKind` along with the args that are needed to substitute the instance. @@ -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) } @@ -677,23 +677,26 @@ impl<'tcx> Instance<'tcx> { // // 1) The underlying method expects a caller location parameter // in the ABI - if resolved.def.requires_caller_location(tcx) - // 2) The caller location parameter comes from having `#[track_caller]` - // on the implementation, and *not* on the trait method. - && !tcx.should_inherit_track_caller(def) - // If the method implementation comes from the trait definition itself - // (e.g. `trait Foo { #[track_caller] my_fn() { /* impl */ } }`), - // then we don't need to generate a shim. This check is needed because - // `should_inherit_track_caller` returns `false` if our method - // implementation comes from the trait block, and not an impl block - && !matches!( - tcx.opt_associated_item(def), - Some(ty::AssocItem { - container: ty::AssocItemContainer::Trait, - .. - }) - ) - { + let needs_track_caller_shim = resolved.def.requires_caller_location(tcx) + // 2) The caller location parameter comes from having `#[track_caller]` + // on the implementation, and *not* on the trait method. + && !tcx.should_inherit_track_caller(def) + // If the method implementation comes from the trait definition itself + // (e.g. `trait Foo { #[track_caller] my_fn() { /* impl */ } }`), + // then we don't need to generate a shim. This check is needed because + // `should_inherit_track_caller` returns `false` if our method + // implementation comes from the trait block, and not an impl block + && !matches!( + tcx.opt_associated_item(def), + Some(ty::AssocItem { + container: ty::AssocItemContainer::Trait, + .. + }) + ); + // We also need to generate a shim if this is an AFIT. + let needs_rpitit_shim = + tcx.return_position_impl_trait_in_trait_shim_data(def).is_some(); + if needs_track_caller_shim || needs_rpitit_shim { if tcx.is_closure_like(def) { debug!( " => vtable fn pointer created for closure with #[track_caller]: {:?} for method {:?} {:?}", @@ -917,116 +920,6 @@ impl<'tcx> Instance<'tcx> { tcx.try_normalize_erasing_regions(typing_env, v.instantiate_identity()) } } - - /// Returns a new `Instance` where generic parameters in `instance.args` are replaced by - /// identity parameters if they are determined to be unused in `instance.def`. - pub fn polymorphize(self, tcx: TyCtxt<'tcx>) -> Self { - debug!("polymorphize: running polymorphization analysis"); - if !tcx.sess.opts.unstable_opts.polymorphize { - return self; - } - - let polymorphized_args = polymorphize(tcx, self.def, self.args); - debug!("polymorphize: self={:?} polymorphized_args={:?}", self, polymorphized_args); - Self { def: self.def, args: polymorphized_args } - } -} - -fn polymorphize<'tcx>( - tcx: TyCtxt<'tcx>, - instance: ty::InstanceKind<'tcx>, - args: GenericArgsRef<'tcx>, -) -> GenericArgsRef<'tcx> { - debug!("polymorphize({:?}, {:?})", instance, args); - let unused = tcx.unused_generic_params(instance); - debug!("polymorphize: unused={:?}", unused); - - // If this is a closure or coroutine then we need to handle the case where another closure - // from the function is captured as an upvar and hasn't been polymorphized. In this case, - // the unpolymorphized upvar closure would result in a polymorphized closure producing - // multiple mono items (and eventually symbol clashes). - let def_id = instance.def_id(); - let upvars_ty = match tcx.type_of(def_id).skip_binder().kind() { - ty::Closure(..) => Some(args.as_closure().tupled_upvars_ty()), - ty::Coroutine(..) => { - assert_eq!( - args.as_coroutine().kind_ty(), - tcx.types.unit, - "polymorphization does not support coroutines from async closures" - ); - Some(args.as_coroutine().tupled_upvars_ty()) - } - _ => None, - }; - let has_upvars = upvars_ty.is_some_and(|ty| !ty.tuple_fields().is_empty()); - debug!("polymorphize: upvars_ty={:?} has_upvars={:?}", upvars_ty, has_upvars); - - struct PolymorphizationFolder<'tcx> { - tcx: TyCtxt<'tcx>, - } - - impl<'tcx> ty::TypeFolder<TyCtxt<'tcx>> for PolymorphizationFolder<'tcx> { - fn cx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - debug!("fold_ty: ty={:?}", ty); - match *ty.kind() { - ty::Closure(def_id, args) => { - let polymorphized_args = - polymorphize(self.tcx, ty::InstanceKind::Item(def_id), args); - if args == polymorphized_args { - ty - } else { - Ty::new_closure(self.tcx, def_id, polymorphized_args) - } - } - ty::Coroutine(def_id, args) => { - let polymorphized_args = - polymorphize(self.tcx, ty::InstanceKind::Item(def_id), args); - if args == polymorphized_args { - ty - } else { - Ty::new_coroutine(self.tcx, def_id, polymorphized_args) - } - } - _ => ty.super_fold_with(self), - } - } - } - - GenericArgs::for_item(tcx, def_id, |param, _| { - let is_unused = unused.is_unused(param.index); - debug!("polymorphize: param={:?} is_unused={:?}", param, is_unused); - match param.kind { - // Upvar case: If parameter is a type parameter.. - ty::GenericParamDefKind::Type { .. } if - // ..and has upvars.. - has_upvars && - // ..and this param has the same type as the tupled upvars.. - upvars_ty == Some(args[param.index as usize].expect_ty()) => { - // ..then double-check that polymorphization marked it used.. - debug_assert!(!is_unused); - // ..and polymorphize any closures/coroutines captured as upvars. - let upvars_ty = upvars_ty.unwrap(); - let polymorphized_upvars_ty = upvars_ty.fold_with( - &mut PolymorphizationFolder { tcx }); - debug!("polymorphize: polymorphized_upvars_ty={:?}", polymorphized_upvars_ty); - ty::GenericArg::from(polymorphized_upvars_ty) - }, - - // Simple case: If parameter is a const or type parameter.. - ty::GenericParamDefKind::Const { .. } | ty::GenericParamDefKind::Type { .. } if - // ..and is within range and unused.. - unused.is_unused(param.index) => - // ..then use the identity for this parameter. - tcx.mk_param_from_def(param), - - // Otherwise, use the parameter as before. - _ => args[param.index as usize], - } - }) } fn needs_fn_once_adapter_shim( diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 01ad76aedc3..07573a79260 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -552,7 +552,10 @@ impl<'tcx> HasWasmCAbiOpt for TyCtxt<'tcx> { impl<'tcx> HasX86AbiOpt for TyCtxt<'tcx> { fn x86_abi_opt(&self) -> X86Abi { - X86Abi { regparm: self.sess.opts.unstable_opts.regparm } + X86Abi { + regparm: self.sess.opts.unstable_opts.regparm, + reg_struct_return: self.sess.opts.unstable_opts.reg_struct_return, + } } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index c7a2223ecd7..061b4e806b2 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, @@ -52,7 +52,7 @@ 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, @@ -95,7 +95,7 @@ pub use self::sty::{ pub use self::trait_def::TraitDef; pub use self::typeck_results::{ CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, IsIdentity, - TypeckResults, UserType, UserTypeAnnotationIndex, + TypeckResults, UserType, UserTypeAnnotationIndex, UserTypeKind, }; pub use self::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; use crate::error::{OpaqueHiddenTypeMismatch, TypeMismatchReason}; @@ -146,6 +146,7 @@ mod opaque_types; mod parameterized; mod predicate; mod region; +mod return_position_impl_trait_in_trait; mod rvalue_scopes; mod structural_impls; #[allow(hidden_glob_reexports)] @@ -253,6 +254,7 @@ pub struct ImplTraitHeader<'tcx> { pub trait_ref: ty::EarlyBinder<'tcx, ty::TraitRef<'tcx>>, pub polarity: ImplPolarity, pub safety: hir::Safety, + pub constness: hir::Constness, } #[derive(Copy, Clone, PartialEq, Eq, Debug, TypeFoldable, TypeVisitable)] @@ -1279,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()) } } @@ -1364,6 +1366,7 @@ pub struct FieldDef { pub name: Symbol, pub vis: Visibility<DefId>, pub safety: hir::Safety, + pub value: Option<DefId>, } impl PartialEq for FieldDef { @@ -1376,9 +1379,9 @@ impl PartialEq for FieldDef { // of `FieldDef` changes, a compile-error will be produced, reminding // us to revisit this assumption. - let Self { did: lhs_did, name: _, vis: _, safety: _ } = &self; + let Self { did: lhs_did, name: _, vis: _, safety: _, value: _ } = &self; - let Self { did: rhs_did, name: _, vis: _, safety: _ } = other; + let Self { did: rhs_did, name: _, vis: _, safety: _, value: _ } = other; let res = lhs_did == rhs_did; @@ -1405,7 +1408,7 @@ impl Hash for FieldDef { // of `FieldDef` changes, a compile-error will be produced, reminding // us to revisit this assumption. - let Self { did, name: _, vis: _, safety: _ } = &self; + let Self { did, name: _, vis: _, safety: _, value: _ } = &self; did.hash(s) } @@ -1742,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) } } @@ -1755,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) } } @@ -1778,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 @@ -1791,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)) } @@ -1801,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); @@ -2003,17 +2006,25 @@ impl<'tcx> TyCtxt<'tcx> { let def_id: DefId = def_id.into(); match self.def_kind(def_id) { DefKind::Impl { of_trait: true } => { - self.constness(def_id) == hir::Constness::Const - && self.is_const_trait( - self.trait_id_of_impl(def_id) - .expect("expected trait for trait implementation"), - ) + let header = self.impl_trait_header(def_id).unwrap(); + header.constness == hir::Constness::Const + && self.is_const_trait(header.trait_ref.skip_binder().def_id) } DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) => { self.constness(def_id) == hir::Constness::Const } DefKind::Trait => self.is_const_trait(def_id), - DefKind::AssocTy | DefKind::AssocFn => { + DefKind::AssocTy => { + let parent_def_id = self.parent(def_id); + match self.def_kind(parent_def_id) { + DefKind::Impl { of_trait: false } => false, + DefKind::Impl { of_trait: true } | DefKind::Trait => { + self.is_conditionally_const(parent_def_id) + } + _ => bug!("unexpected parent item of associated type: {parent_def_id:?}"), + } + } + DefKind::AssocFn => { let parent_def_id = self.parent(def_id); match self.def_kind(parent_def_id) { DefKind::Impl { of_trait: false } => { @@ -2022,7 +2033,7 @@ impl<'tcx> TyCtxt<'tcx> { DefKind::Impl { of_trait: true } | DefKind::Trait => { self.is_conditionally_const(parent_def_id) } - _ => bug!("unexpected parent item of associated item: {parent_def_id:?}"), + _ => bug!("unexpected parent item of associated fn: {parent_def_id:?}"), } } DefKind::OpaqueTy => match self.opaque_ty_origin(def_id) { diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index f7322217aa3..59f2555be95 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, @@ -111,6 +111,7 @@ trivially_parameterized_over_tcx! { rustc_span::hygiene::SyntaxContextData, rustc_span::symbol::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 cd4123f0a3f..40e0fb0087f 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1020,7 +1020,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { self.insert_trait_and_projection( trait_ref, - Some((proj.projection_def_id(), proj.term())), + Some((proj.item_def_id(), proj.term())), &mut traits, &mut fn_traits, ); diff --git a/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs b/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs new file mode 100644 index 00000000000..21c605f8296 --- /dev/null +++ b/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs @@ -0,0 +1,95 @@ +use rustc_hir::def_id::DefId; + +use crate::ty::{self, ExistentialPredicateStableCmpExt, TyCtxt}; + +impl<'tcx> TyCtxt<'tcx> { + /// Given a `def_id` of a trait or impl method, compute whether that method needs to + /// have an RPITIT shim applied to it for it to be object safe. If so, return the + /// `def_id` of the RPITIT, and also the args of trait method that returns the RPITIT. + /// + /// NOTE that these args are not, in general, the same as than the RPITIT's args. They + /// are a subset of those args, since they do not include the late-bound lifetimes of + /// the RPITIT. Depending on the context, these will need to be dealt with in different + /// ways -- in codegen, it's okay to fill them with ReErased. + pub fn return_position_impl_trait_in_trait_shim_data( + self, + def_id: DefId, + ) -> Option<(DefId, ty::EarlyBinder<'tcx, ty::GenericArgsRef<'tcx>>)> { + let assoc_item = self.opt_associated_item(def_id)?; + + let (trait_item_def_id, opt_impl_def_id) = match assoc_item.container { + ty::AssocItemContainer::Impl => { + (assoc_item.trait_item_def_id?, Some(self.parent(def_id))) + } + ty::AssocItemContainer::Trait => (def_id, None), + }; + + let sig = self.fn_sig(trait_item_def_id); + + // Check if the trait returns an RPITIT. + let ty::Alias(ty::Projection, ty::AliasTy { def_id, .. }) = + *sig.skip_binder().skip_binder().output().kind() + else { + return None; + }; + if !self.is_impl_trait_in_trait(def_id) { + return None; + } + + let args = if let Some(impl_def_id) = opt_impl_def_id { + // Rebase the args from the RPITIT onto the impl trait ref, so we can later + // substitute them with the method args of the *impl* method, since that's + // the instance we're building a vtable shim for. + ty::GenericArgs::identity_for_item(self, trait_item_def_id).rebase_onto( + self, + self.parent(trait_item_def_id), + self.impl_trait_ref(impl_def_id) + .expect("expected impl trait ref from parent of impl item") + .instantiate_identity() + .args, + ) + } else { + // This is when we have a default trait implementation. + ty::GenericArgs::identity_for_item(self, trait_item_def_id) + }; + + Some((def_id, ty::EarlyBinder::bind(args))) + } + + /// Given a `DefId` of an RPITIT and its args, return the existential predicates + /// that corresponds to the RPITIT's bounds with the self type erased. + pub fn item_bounds_to_existential_predicates( + self, + def_id: DefId, + args: ty::GenericArgsRef<'tcx>, + ) -> &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>> { + let mut bounds: Vec<_> = self + .item_super_predicates(def_id) + .iter_instantiated(self, args) + .filter_map(|clause| { + clause + .kind() + .map_bound(|clause| match clause { + ty::ClauseKind::Trait(trait_pred) => Some(ty::ExistentialPredicate::Trait( + ty::ExistentialTraitRef::erase_self_ty(self, trait_pred.trait_ref), + )), + ty::ClauseKind::Projection(projection_pred) => { + Some(ty::ExistentialPredicate::Projection( + ty::ExistentialProjection::erase_self_ty(self, projection_pred), + )) + } + ty::ClauseKind::TypeOutlives(_) => { + // Type outlives bounds don't really turn into anything, + // since we must use an intersection region for the `dyn*`'s + // region anyways. + None + } + _ => unreachable!("unexpected clause in item bounds: {clause:?}"), + }) + .transpose() + }) + .collect(); + bounds.sort_by(|a, b| a.skip_binder().stable_cmp(self, &b.skip_binder())); + self.mk_poly_existential_predicates(&bounds) + } +} diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 142db8a17f0..3fbc23924f5 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -978,6 +978,10 @@ impl<'tcx> rustc_type_ir::inherent::Ty<TyCtxt<'tcx>> for Ty<'tcx> { fn async_destructor_ty(self, interner: TyCtxt<'tcx>) -> Ty<'tcx> { self.async_destructor_ty(interner) } + + fn has_unsafe_fields(self) -> bool { + Ty::has_unsafe_fields(self) + } } /// Type utilities diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 8cba5f33278..551c113aa59 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -700,12 +700,31 @@ pub struct CanonicalUserTypeAnnotation<'tcx> { /// Canonical user type annotation. pub type CanonicalUserType<'tcx> = Canonical<'tcx, UserType<'tcx>>; +#[derive(Copy, Clone, Debug, PartialEq, TyEncodable, TyDecodable)] +#[derive(Eq, Hash, HashStable, TypeFoldable, TypeVisitable)] +pub struct UserType<'tcx> { + pub kind: UserTypeKind<'tcx>, + pub bounds: ty::Clauses<'tcx>, +} + +impl<'tcx> UserType<'tcx> { + pub fn new(kind: UserTypeKind<'tcx>) -> UserType<'tcx> { + UserType { kind, bounds: ty::ListWithCachedTypeInfo::empty() } + } + + /// A user type annotation with additional bounds that need to be enforced. + /// These bounds are lowered from `impl Trait` in bindings. + pub fn new_with_bounds(kind: UserTypeKind<'tcx>, bounds: ty::Clauses<'tcx>) -> UserType<'tcx> { + UserType { kind, bounds } + } +} + /// A user-given type annotation attached to a constant. These arise /// from constants that are named via paths, like `Foo::<A>::new` and /// so forth. #[derive(Copy, Clone, Debug, PartialEq, TyEncodable, TyDecodable)] #[derive(Eq, Hash, HashStable, TypeFoldable, TypeVisitable)] -pub enum UserType<'tcx> { +pub enum UserTypeKind<'tcx> { Ty(Ty<'tcx>), /// The canonical type is the result of `type_of(def_id)` with the @@ -721,9 +740,13 @@ impl<'tcx> IsIdentity for CanonicalUserType<'tcx> { /// Returns `true` if this represents the generic parameters of the form `[?0, ?1, ?2]`, /// i.e., each thing is mapped to a canonical variable with the same index. fn is_identity(&self) -> bool { - match self.value { - UserType::Ty(_) => false, - UserType::TypeOf(_, user_args) => { + if !self.value.bounds.is_empty() { + return false; + } + + match self.value.kind { + UserTypeKind::Ty(_) => false, + UserTypeKind::TypeOf(_, user_args) => { if user_args.user_self_ty.is_some() { return false; } @@ -765,6 +788,18 @@ impl<'tcx> IsIdentity for CanonicalUserType<'tcx> { impl<'tcx> std::fmt::Display for UserType<'tcx> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.bounds.is_empty() { + self.kind.fmt(f) + } else { + self.kind.fmt(f)?; + write!(f, " + ")?; + std::fmt::Debug::fmt(&self.bounds, f) + } + } +} + +impl<'tcx> std::fmt::Display for UserTypeKind<'tcx> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Ty(arg0) => { ty::print::with_no_trimmed_paths!(write!(f, "Ty({})", arg0)) diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 57054bd1a0b..fc3530e3dde 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -389,7 +389,7 @@ impl<'tcx> TyCtxt<'tcx> { .delay_as_bug(); } - dtor_candidate = Some((*item_id, self.constness(impl_did))); + dtor_candidate = Some((*item_id, self.impl_trait_header(impl_did).unwrap().constness)); }); let (did, constness) = dtor_candidate?; @@ -1288,6 +1288,15 @@ 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.is_unsafe()) + } else { + false + } + } + /// Get morphology of the async drop glue, needed for types which do not /// use async drop. To get async drop glue morphology for a definition see /// [`TyCtxt::async_drop_glue_morphology`]. Used for `AsyncDestruct::Destructor` diff --git a/compiler/rustc_middle/src/ty/vtable.rs b/compiler/rustc_middle/src/ty/vtable.rs index e5eeb23be25..09a05104e49 100644 --- a/compiler/rustc_middle/src/ty/vtable.rs +++ b/compiler/rustc_middle/src/ty/vtable.rs @@ -131,8 +131,7 @@ pub(super) fn vtable_allocation_provider<'tcx>( VtblEntry::Vacant => continue, VtblEntry::Method(instance) => { // Prepare the fn ptr we write into the vtable. - let instance = instance.polymorphize(tcx); - let fn_alloc_id = tcx.reserve_and_set_fn_alloc(instance, CTFE_ALLOC_SALT); + let fn_alloc_id = tcx.reserve_and_set_fn_alloc(*instance, CTFE_ALLOC_SALT); let fn_ptr = Pointer::from(fn_alloc_id); Scalar::from_pointer(fn_ptr, &tcx) } diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index 48d744a9ef6..390909bb0ab 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -100,7 +100,7 @@ impl<'tcx> Value<TyCtxt<'tcx>> for Representability { } for info in &cycle_error.cycle { if info.query.dep_kind == dep_kinds::representability_adt_ty - && let Some(def_id) = info.query.ty_def_id + && let Some(def_id) = info.query.def_id_for_ty_in_cycle && let Some(def_id) = def_id.as_local() && !item_and_field_ids.iter().any(|&(id, _)| id == def_id) { @@ -182,7 +182,7 @@ impl<'tcx, T> Value<TyCtxt<'tcx>> for Result<T, &'_ ty::layout::LayoutError<'_>> &cycle_error.cycle, |cycle| { if cycle[0].query.dep_kind == dep_kinds::layout_of - && let Some(def_id) = cycle[0].query.ty_def_id + && let Some(def_id) = cycle[0].query.def_id_for_ty_in_cycle && let Some(def_id) = def_id.as_local() && let def_kind = tcx.def_kind(def_id) && matches!(def_kind, DefKind::Closure) @@ -209,7 +209,7 @@ impl<'tcx, T> Value<TyCtxt<'tcx>> for Result<T, &'_ ty::layout::LayoutError<'_>> if frame.query.dep_kind != dep_kinds::layout_of { continue; } - let Some(frame_def_id) = frame.query.ty_def_id else { + let Some(frame_def_id) = frame.query.def_id_for_ty_in_cycle else { continue; }; let Some(frame_coroutine_kind) = tcx.coroutine_kind(frame_def_id) else { 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..cca309115ba 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> { 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..34cdc288f0b 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; diff --git a/compiler/rustc_mir_build/src/build/custom/parse.rs b/compiler/rustc_mir_build/src/builder/custom/parse.rs index 538068e1fac..538068e1fac 100644 --- a/compiler/rustc_mir_build/src/build/custom/parse.rs +++ b/compiler/rustc_mir_build/src/builder/custom/parse.rs 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 c3e9bd302de..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>> { @@ -283,7 +283,7 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { fields.iter().map(|e| self.parse_operand(*e)).collect::<Result<_, _>>()? )) }, - ExprKind::Adt(box AdtExpr{ adt_def, variant_index, args, fields, .. }) => { + ExprKind::Adt(box AdtExpr { adt_def, variant_index, args, fields, .. }) => { let is_union = adt_def.is_union(); let active_field_index = is_union.then(|| fields[0].name); 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 6ce88cdc39d..01aec70f437 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs @@ -11,12 +11,12 @@ use rustc_middle::mir::*; use rustc_middle::thir::*; use rustc_middle::ty::{self, AdtDef, CanonicalUserTypeAnnotation, Ty, Variance}; use rustc_middle::{bug, span_bug}; -use rustc_span::Span; +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>> { @@ -630,6 +630,98 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block.and(base_place.index(idx)) } + /// Given a place that's either an array or a slice, returns an operand + /// with the length of the array/slice. + /// + /// For arrays it'll be `Operand::Constant` with the actual length; + /// For slices it'll be `Operand::Move` of a local using `PtrMetadata`. + fn len_of_slice_or_array( + &mut self, + block: BasicBlock, + place: Place<'tcx>, + span: Span, + source_info: SourceInfo, + ) -> Operand<'tcx> { + let place_ty = place.ty(&self.local_decls, self.tcx).ty; + let usize_ty = self.tcx.types.usize; + + match place_ty.kind() { + ty::Array(_elem_ty, len_const) => { + 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) => { + let ptr_or_ref = if let [PlaceElem::Deref] = place.projection[..] + && let local_ty = self.local_decls[place.local].ty + && local_ty.is_trivially_pure_clone_copy() + { + // It's extremely common that we have something that can be + // directly passed to `PtrMetadata`, so avoid an unnecessary + // temporary and statement in those cases. Note that we can + // only do that for `Copy` types -- not `&mut [_]` -- because + // the MIR we're building here needs to pass NLL later. + Operand::Copy(Place::from(place.local)) + } else { + let len_span = self.tcx.with_stable_hashing_context(|hcx| { + let span = source_info.span; + span.mark_with_reason( + None, + DesugaringKind::IndexBoundsCheckReborrow, + span.edition(), + hcx, + ) + }); + let ptr_ty = Ty::new_imm_ptr(self.tcx, place_ty); + let slice_ptr = self.temp(ptr_ty, span); + self.cfg.push_assign( + block, + SourceInfo { span: len_span, ..source_info }, + slice_ptr, + Rvalue::RawPtr(Mutability::Not, place), + ); + Operand::Move(slice_ptr) + }; + + let len = self.temp(usize_ty, span); + self.cfg.push_assign( + block, + source_info, + len, + Rvalue::UnaryOp(UnOp::PtrMetadata, ptr_or_ref), + ); + + Operand::Move(len) + } + _ => { + span_bug!(span, "len called on place of type {place_ty:?}") + } + } + } + fn bounds_check( &mut self, block: BasicBlock, @@ -638,25 +730,25 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { expr_span: Span, source_info: SourceInfo, ) -> BasicBlock { - let usize_ty = self.tcx.types.usize; - let bool_ty = self.tcx.types.bool; - // bounds check: - let len = self.temp(usize_ty, expr_span); - let lt = self.temp(bool_ty, expr_span); + let slice = slice.to_place(self); // len = len(slice) - self.cfg.push_assign(block, source_info, len, Rvalue::Len(slice.to_place(self))); + let len = self.len_of_slice_or_array(block, slice, expr_span, source_info); + // lt = idx < len + let bool_ty = self.tcx.types.bool; + let lt = self.temp(bool_ty, expr_span); self.cfg.push_assign( block, source_info, lt, Rvalue::BinaryOp( BinOp::Lt, - Box::new((Operand::Copy(Place::from(index)), Operand::Copy(len))), + Box::new((Operand::Copy(Place::from(index)), len.to_copy())), ), ); - let msg = BoundsCheck { len: Operand::Move(len), index: Operand::Copy(Place::from(index)) }; + let msg = BoundsCheck { len, index: Operand::Copy(Place::from(index)) }; + // assert!(lt, "...") self.assert(block, Operand::Move(lt), true, msg, expr_span) } 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..5e3a24e18fb 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 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 bebb44faba6..88f63d4e22c 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -1,7 +1,5 @@ //! See docs in build/expr/mod.rs -use std::iter; - use rustc_ast::{AsmMacro, InlineAsmOptions}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -13,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 @@ -344,25 +342,57 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }) .collect(); - let field_names = adt_def.variant(variant_index).fields.indices(); - - let fields = if let Some(FruInfo { base, field_types }) = base { - let place_builder = unpack!(block = this.as_place_builder(block, *base)); + let variant = adt_def.variant(variant_index); + let field_names = variant.fields.indices(); - // MIR does not natively support FRU, so for each - // base-supplied field, generate an operand that - // reads it from the base. - iter::zip(field_names, &**field_types) - .map(|(n, ty)| match fields_map.get(&n) { - Some(v) => v.clone(), - None => { - let place = place_builder.clone_project(PlaceElem::Field(n, *ty)); - this.consume_by_copy_or_move(place.to_place(this)) - } - }) - .collect() - } else { - field_names.filter_map(|n| fields_map.get(&n).cloned()).collect() + let fields = match base { + AdtExprBase::None => { + field_names.filter_map(|n| fields_map.get(&n).cloned()).collect() + } + AdtExprBase::Base(FruInfo { base, field_types }) => { + let place_builder = unpack!(block = this.as_place_builder(block, *base)); + + // We desugar FRU as we lower to MIR, so for each + // base-supplied field, generate an operand that + // reads it from the base. + itertools::zip_eq(field_names, &**field_types) + .map(|(n, ty)| match fields_map.get(&n) { + Some(v) => v.clone(), + None => { + let place = + place_builder.clone_project(PlaceElem::Field(n, *ty)); + this.consume_by_copy_or_move(place.to_place(this)) + } + }) + .collect() + } + AdtExprBase::DefaultFields(field_types) => { + 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::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; + span_bug!( + expr_span, + "missing mandatory field `{name}` of type `{ty}`", + ); + } + }, + }) + .collect() + } }; let inferred_ty = expr.ty; 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 2815b390375..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>, @@ -176,9 +176,8 @@ impl<'pat, 'tcx> MatchPairTree<'pat, 'tcx> { ty: cx.infcx.next_ty_var(span), }) .args; - let user_ty = cx.infcx.canonicalize_user_type_annotation(ty::UserType::TypeOf( - def_id, - ty::UserArgs { args, user_self_ty: None }, + let user_ty = cx.infcx.canonicalize_user_type_annotation(ty::UserType::new( + ty::UserTypeKind::TypeOf(def_id, ty::UserArgs { args, user_self_ty: None }), )); let annotation = ty::CanonicalUserTypeAnnotation { inferred_ty: pattern.ty, diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 5791460a6b1..edbe9c69109 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -18,10 +18,10 @@ use rustc_span::symbol::Symbol; use rustc_span::{BytePos, Pos, Span}; 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..596d525f5d8 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/builder/matches/test.rs @@ -20,8 +20,8 @@ use rustc_span::symbol::{Symbol, sym}; use rustc_span::{DUMMY_SP, Span}; 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..a53ae05e84f 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 diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index f43c29d8f5d..96e4ca3493d 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; @@ -23,8 +28,8 @@ use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; 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>, diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index 636e47b7ad2..882e29de46d 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -95,7 +95,7 @@ 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> { diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index f37b3f977fa..fc0031616aa 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; @@ -18,7 +18,7 @@ use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::symbol::Symbol; use rustc_span::{Span, sym}; -use crate::build::ExprCategory; +use crate::builder::ExprCategory; use crate::errors::*; struct UnsafetyVisitor<'a, 'tcx> { @@ -342,7 +342,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 +367,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 +479,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 +623,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 +1112,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/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 3fa0e4def82..ce1c635d1b9 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -2,10 +2,10 @@ use rustc_ast as ast; use rustc_hir::LangItem; use rustc_middle::bug; use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; -use rustc_middle::ty::{self, ScalarInt, TyCtxt}; +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>, @@ -13,6 +13,10 @@ pub(crate) fn lit_to_const<'tcx>( ) -> Result<ty::Const<'tcx>, LitToConstError> { let LitToConstInput { lit, ty, neg } = lit_input; + if let Err(guar) = ty.error_reported() { + return Ok(ty::Const::new_error(tcx, guar)); + } + let trunc = |n| { let width = match tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty)) { Ok(layout) => layout.size, diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index ee9bcce104e..ae49b266153 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -15,7 +15,6 @@ use rustc_middle::ty::adjustment::{ }; use rustc_middle::ty::{ self, AdtKind, GenericArgs, InlineConstArgs, InlineConstArgsParts, ScalarInt, Ty, UpvarArgs, - UserType, }; use rustc_middle::{bug, span_bug}; use rustc_span::{Span, sym}; @@ -26,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)) @@ -222,7 +229,7 @@ impl<'tcx> Cx<'tcx> { args, fields: Box::new([FieldExpr { name: FieldIdx::from(0u32), expr }]), user_ty: None, - base: None, + base: AdtExprBase::None, })); debug!(?kind); @@ -443,7 +450,9 @@ impl<'tcx> Cx<'tcx> { let user_provided_types = self.typeck_results().user_provided_types(); let user_ty = user_provided_types.get(fun.hir_id).copied().map(|mut u_ty| { - if let UserType::TypeOf(ref mut did, _) = &mut u_ty.value { + if let ty::UserTypeKind::TypeOf(ref mut did, _) = + &mut u_ty.value.kind + { *did = adt_def.did(); } Box::new(u_ty) @@ -464,7 +473,7 @@ impl<'tcx> Cx<'tcx> { variant_index: index, fields: field_refs, user_ty, - base: None, + base: AdtExprBase::None, })) } else { ExprKind::Call { @@ -594,20 +603,36 @@ impl<'tcx> Cx<'tcx> { args, user_ty, fields: self.field_refs(fields), - base: base.map(|base| FruInfo { - base: self.mirror_expr(base), - field_types: self.typeck_results().fru_field_types()[expr.hir_id] - .iter() - .copied() - .collect(), - }), + base: match base { + hir::StructTailExpr::Base(base) => AdtExprBase::Base(FruInfo { + base: self.mirror_expr(base), + field_types: self.typeck_results().fru_field_types() + [expr.hir_id] + .iter() + .copied() + .collect(), + }), + hir::StructTailExpr::DefaultFields(_) => { + AdtExprBase::DefaultFields( + self.typeck_results().fru_field_types()[expr.hir_id] + .iter() + .copied() + .collect(), + ) + } + hir::StructTailExpr::None => AdtExprBase::None, + }, })) } AdtKind::Enum => { let res = self.typeck_results().qpath_res(qpath, expr.hir_id); match res { Res::Def(DefKind::Variant, variant_id) => { - assert!(base.is_none()); + assert!(matches!( + base, + hir::StructTailExpr::None + | hir::StructTailExpr::DefaultFields(_) + )); let index = adt.variant_index_with_id(variant_id); let user_provided_types = @@ -621,7 +646,21 @@ impl<'tcx> Cx<'tcx> { args, user_ty, fields: self.field_refs(fields), - base: None, + base: match base { + hir::StructTailExpr::DefaultFields(_) => { + AdtExprBase::DefaultFields( + self.typeck_results().fru_field_types() + [expr.hir_id] + .iter() + .copied() + .collect(), + ) + } + hir::StructTailExpr::Base(base) => { + span_bug!(base.span, "unexpected res: {:?}", res); + } + hir::StructTailExpr::None => AdtExprBase::None, + }, })) } _ => { @@ -885,6 +924,11 @@ impl<'tcx> Cx<'tcx> { } } } + + hir::ExprKind::UnsafeBinderCast(_kind, _source, _ty) => { + unreachable!("unsafe binders are not yet implemented") + } + hir::ExprKind::DropTemps(source) => ExprKind::Use { source: self.mirror_expr(source) }, hir::ExprKind::Array(fields) => ExprKind::Array { fields: self.mirror_exprs(fields) }, hir::ExprKind::Tup(fields) => ExprKind::Tuple { fields: self.mirror_exprs(fields) }, @@ -1029,7 +1073,7 @@ impl<'tcx> Cx<'tcx> { args, user_ty, fields: Box::new([]), - base: None, + base: AdtExprBase::None, })), _ => bug!("unexpected ty: {:?}", ty), } @@ -1153,7 +1197,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 3ac53fa6272..2dbc8b7b573 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -626,32 +626,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { ) -> PatKind<'tcx> { let tcx = self.tcx; let def_id = block.def_id; - let body_id = block.body; - let expr = &tcx.hir().body(body_id).value; let ty = tcx.typeck(def_id).node_type(block.hir_id); - // Special case inline consts that are just literals. This is solely - // a performance optimization, as we could also just go through the regular - // const eval path below. - // FIXME: investigate the performance impact of removing this. - let lit_input = match expr.kind { - hir::ExprKind::Lit(lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: false }), - hir::ExprKind::Unary(hir::UnOp::Neg, expr) => match expr.kind { - hir::ExprKind::Lit(lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: true }), - _ => None, - }, - _ => None, - }; - if let Some(lit_input) = lit_input { - match tcx.at(expr.span).lit_to_const(lit_input) { - Ok(c) => return self.const_to_pat(c, ty, id, span).kind, - // If an error occurred, ignore that it's a literal - // and leave reporting the error up to const eval of - // the unevaluated constant below. - Err(_) => {} - } - } - let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id()); let parent_args = tcx.erase_regions(ty::GenericArgs::identity_for_item(tcx, typeck_root_def_id)); diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index 6be0ed5fb31..2bcdb67c58a 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -566,11 +566,17 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { self.print_expr(field_expr.expr, depth_lvl + 2); } - if let Some(ref base) = adt_expr.base { - print_indented!(self, "base:", depth_lvl + 1); - self.print_fru_info(base, depth_lvl + 2); - } else { - print_indented!(self, "base: None", depth_lvl + 1); + match adt_expr.base { + AdtExprBase::Base(ref base) => { + print_indented!(self, "base:", depth_lvl + 1); + self.print_fru_info(base, depth_lvl + 2); + } + AdtExprBase::DefaultFields(_) => { + print_indented!(self, "base: {{ defaulted fields }}", depth_lvl + 1); + } + AdtExprBase::None => { + print_indented!(self, "base: None", depth_lvl + 1); + } } } diff --git a/compiler/rustc_mir_build/src/thir/util.rs b/compiler/rustc_mir_build/src/thir/util.rs index 53a2a0852eb..ed7c7e40993 100644 --- a/compiler/rustc_mir_build/src/thir/util.rs +++ b/compiler/rustc_mir_build/src/thir/util.rs @@ -1,6 +1,6 @@ use rustc_hir as hir; use rustc_middle::bug; -use rustc_middle::ty::{self, CanonicalUserType, TyCtxt, UserType}; +use rustc_middle::ty::{self, CanonicalUserType, TyCtxt}; use tracing::debug; pub(crate) trait UserAnnotatedTyHelpers<'tcx> { @@ -21,7 +21,7 @@ pub(crate) trait UserAnnotatedTyHelpers<'tcx> { let ty = self.typeck_results().node_type(hir_id); match ty.kind() { ty::Adt(adt_def, ..) => { - if let UserType::TypeOf(ref mut did, _) = &mut user_ty.value { + if let ty::UserTypeKind::TypeOf(ref mut did, _) = &mut user_ty.value.kind { *did = adt_def.did(); } Some(user_ty) diff --git a/compiler/rustc_mir_dataflow/src/framework/cursor.rs b/compiler/rustc_mir_dataflow/src/framework/cursor.rs index 4a9bcdaddb3..89ff93d9943 100644 --- a/compiler/rustc_mir_dataflow/src/framework/cursor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/cursor.rs @@ -179,15 +179,15 @@ where /// Advances the cursor to hold the dataflow state at `target` before its "primary" effect is /// applied. /// - /// The "before" effect at the target location *will be* applied. + /// The "early" effect at the target location *will be* applied. pub fn seek_before_primary_effect(&mut self, target: Location) { - self.seek_after(target, Effect::Before) + self.seek_after(target, Effect::Early) } /// Advances the cursor to hold the dataflow state at `target` after its "primary" effect is /// applied. /// - /// The "before" effect at the target location will be applied as well. + /// The "early" effect at the target location will be applied as well. pub fn seek_after_primary_effect(&mut self, target: Location) { self.seek_after(target, Effect::Primary) } @@ -222,12 +222,12 @@ where #[rustfmt::skip] let next_effect = if A::Direction::IS_FORWARD { self.pos.curr_effect_index.map_or_else( - || Effect::Before.at_index(0), + || Effect::Early.at_index(0), EffectIndex::next_in_forward_order, ) } else { self.pos.curr_effect_index.map_or_else( - || Effect::Before.at_index(block_data.statements.len()), + || Effect::Early.at_index(block_data.statements.len()), EffectIndex::next_in_backward_order, ) }; diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs index 566a6b09b2b..9d943ebe327 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -66,12 +66,12 @@ impl Direction for Backward { { let terminator = block_data.terminator(); let location = Location { block, statement_index: block_data.statements.len() }; - analysis.apply_before_terminator_effect(state, terminator, location); - analysis.apply_terminator_effect(state, terminator, location); + analysis.apply_early_terminator_effect(state, terminator, location); + analysis.apply_primary_terminator_effect(state, terminator, location); for (statement_index, statement) in block_data.statements.iter().enumerate().rev() { let location = Location { block, statement_index }; - analysis.apply_before_statement_effect(state, statement, location); - analysis.apply_statement_effect(state, statement, location); + analysis.apply_early_statement_effect(state, statement, location); + analysis.apply_primary_statement_effect(state, statement, location); } let exit_state = state; @@ -159,14 +159,14 @@ impl Direction for Backward { let location = Location { block, statement_index: from.statement_index }; let terminator = block_data.terminator(); - if from.effect == Effect::Before { - analysis.apply_before_terminator_effect(state, terminator, location); - if to == Effect::Before.at_index(terminator_index) { + if from.effect == Effect::Early { + analysis.apply_early_terminator_effect(state, terminator, location); + if to == Effect::Early.at_index(terminator_index) { return; } } - analysis.apply_terminator_effect(state, terminator, location); + analysis.apply_primary_terminator_effect(state, terminator, location); if to == Effect::Primary.at_index(terminator_index) { return; } @@ -180,7 +180,7 @@ impl Direction for Backward { let location = Location { block, statement_index: from.statement_index }; let statement = &block_data.statements[from.statement_index]; - analysis.apply_statement_effect(state, statement, location); + analysis.apply_primary_statement_effect(state, statement, location); if to == Effect::Primary.at_index(from.statement_index) { return; } @@ -188,7 +188,7 @@ impl Direction for Backward { from.statement_index - 1 } - Effect::Before => from.statement_index, + Effect::Early => from.statement_index, }; // Handle all statements between `first_unapplied_idx` and `to.statement_index`. @@ -196,21 +196,21 @@ impl Direction for Backward { for statement_index in (to.statement_index..next_effect).rev().map(|i| i + 1) { let location = Location { block, statement_index }; let statement = &block_data.statements[statement_index]; - analysis.apply_before_statement_effect(state, statement, location); - analysis.apply_statement_effect(state, statement, location); + analysis.apply_early_statement_effect(state, statement, location); + analysis.apply_primary_statement_effect(state, statement, location); } // Handle the statement at `to`. let location = Location { block, statement_index: to.statement_index }; let statement = &block_data.statements[to.statement_index]; - analysis.apply_before_statement_effect(state, statement, location); + analysis.apply_early_statement_effect(state, statement, location); - if to.effect == Effect::Before { + if to.effect == Effect::Early { return; } - analysis.apply_statement_effect(state, statement, location); + analysis.apply_primary_statement_effect(state, statement, location); } fn visit_results_in_block<'mir, 'tcx, A>( @@ -228,17 +228,17 @@ impl Direction for Backward { let loc = Location { block, statement_index: block_data.statements.len() }; let term = block_data.terminator(); - results.analysis.apply_before_terminator_effect(state, term, loc); - vis.visit_terminator_before_primary_effect(results, state, term, loc); - results.analysis.apply_terminator_effect(state, term, loc); - vis.visit_terminator_after_primary_effect(results, state, term, loc); + results.analysis.apply_early_terminator_effect(state, term, loc); + vis.visit_after_early_terminator_effect(results, state, term, loc); + results.analysis.apply_primary_terminator_effect(state, term, loc); + vis.visit_after_primary_terminator_effect(results, state, term, loc); for (statement_index, stmt) in block_data.statements.iter().enumerate().rev() { let loc = Location { block, statement_index }; - results.analysis.apply_before_statement_effect(state, stmt, loc); - vis.visit_statement_before_primary_effect(results, state, stmt, loc); - results.analysis.apply_statement_effect(state, stmt, loc); - vis.visit_statement_after_primary_effect(results, state, stmt, loc); + results.analysis.apply_early_statement_effect(state, stmt, loc); + vis.visit_after_early_statement_effect(results, state, stmt, loc); + results.analysis.apply_primary_statement_effect(state, stmt, loc); + vis.visit_after_primary_statement_effect(results, state, stmt, loc); } vis.visit_block_start(state); @@ -294,13 +294,13 @@ impl Direction for Forward { { for (statement_index, statement) in block_data.statements.iter().enumerate() { let location = Location { block, statement_index }; - analysis.apply_before_statement_effect(state, statement, location); - analysis.apply_statement_effect(state, statement, location); + analysis.apply_early_statement_effect(state, statement, location); + analysis.apply_primary_statement_effect(state, statement, location); } let terminator = block_data.terminator(); let location = Location { block, statement_index: block_data.statements.len() }; - analysis.apply_before_terminator_effect(state, terminator, location); - let edges = analysis.apply_terminator_effect(state, terminator, location); + analysis.apply_early_terminator_effect(state, terminator, location); + let edges = analysis.apply_primary_terminator_effect(state, terminator, location); let exit_state = state; match edges { @@ -368,21 +368,21 @@ impl Direction for Forward { // after effect, do so now and start the loop below from the next statement. let first_unapplied_index = match from.effect { - Effect::Before => from.statement_index, + Effect::Early => from.statement_index, Effect::Primary if from.statement_index == terminator_index => { debug_assert_eq!(from, to); let location = Location { block, statement_index: terminator_index }; let terminator = block_data.terminator(); - analysis.apply_terminator_effect(state, terminator, location); + analysis.apply_primary_terminator_effect(state, terminator, location); return; } Effect::Primary => { let location = Location { block, statement_index: from.statement_index }; let statement = &block_data.statements[from.statement_index]; - analysis.apply_statement_effect(state, statement, location); + analysis.apply_primary_statement_effect(state, statement, location); // If we only needed to apply the after effect of the statement at `idx`, we are // done. @@ -399,8 +399,8 @@ impl Direction for Forward { for statement_index in first_unapplied_index..to.statement_index { let location = Location { block, statement_index }; let statement = &block_data.statements[statement_index]; - analysis.apply_before_statement_effect(state, statement, location); - analysis.apply_statement_effect(state, statement, location); + analysis.apply_early_statement_effect(state, statement, location); + analysis.apply_primary_statement_effect(state, statement, location); } // Handle the statement or terminator at `to`. @@ -408,17 +408,17 @@ impl Direction for Forward { let location = Location { block, statement_index: to.statement_index }; if to.statement_index == terminator_index { let terminator = block_data.terminator(); - analysis.apply_before_terminator_effect(state, terminator, location); + analysis.apply_early_terminator_effect(state, terminator, location); if to.effect == Effect::Primary { - analysis.apply_terminator_effect(state, terminator, location); + analysis.apply_primary_terminator_effect(state, terminator, location); } } else { let statement = &block_data.statements[to.statement_index]; - analysis.apply_before_statement_effect(state, statement, location); + analysis.apply_early_statement_effect(state, statement, location); if to.effect == Effect::Primary { - analysis.apply_statement_effect(state, statement, location); + analysis.apply_primary_statement_effect(state, statement, location); } } } @@ -438,18 +438,18 @@ impl Direction for Forward { for (statement_index, stmt) in block_data.statements.iter().enumerate() { let loc = Location { block, statement_index }; - results.analysis.apply_before_statement_effect(state, stmt, loc); - vis.visit_statement_before_primary_effect(results, state, stmt, loc); - results.analysis.apply_statement_effect(state, stmt, loc); - vis.visit_statement_after_primary_effect(results, state, stmt, loc); + results.analysis.apply_early_statement_effect(state, stmt, loc); + vis.visit_after_early_statement_effect(results, state, stmt, loc); + results.analysis.apply_primary_statement_effect(state, stmt, loc); + vis.visit_after_primary_statement_effect(results, state, stmt, loc); } let loc = Location { block, statement_index: block_data.statements.len() }; let term = block_data.terminator(); - results.analysis.apply_before_terminator_effect(state, term, loc); - vis.visit_terminator_before_primary_effect(results, state, term, loc); - results.analysis.apply_terminator_effect(state, term, loc); - vis.visit_terminator_after_primary_effect(results, state, term, loc); + results.analysis.apply_early_terminator_effect(state, term, loc); + vis.visit_after_early_terminator_effect(results, state, term, loc); + results.analysis.apply_primary_terminator_effect(state, term, loc); + vis.visit_after_primary_terminator_effect(results, state, term, loc); vis.visit_block_end(state); } diff --git a/compiler/rustc_mir_dataflow/src/framework/fmt.rs b/compiler/rustc_mir_dataflow/src/framework/fmt.rs index dc176ba2d03..faf2c411dde 100644 --- a/compiler/rustc_mir_dataflow/src/framework/fmt.rs +++ b/compiler/rustc_mir_dataflow/src/framework/fmt.rs @@ -4,7 +4,7 @@ use std::fmt; use rustc_index::Idx; -use rustc_index::bit_set::{BitSet, ChunkedBitSet}; +use rustc_index::bit_set::{BitSet, ChunkedBitSet, MixedBitSet}; use super::lattice::MaybeReachable; @@ -85,8 +85,8 @@ where let size = self.domain_size(); assert_eq!(size, old.domain_size()); - let mut set_in_self = ChunkedBitSet::new_empty(size); - let mut cleared_in_self = ChunkedBitSet::new_empty(size); + let mut set_in_self = MixedBitSet::new_empty(size); + let mut cleared_in_self = MixedBitSet::new_empty(size); for i in (0..size).map(T::new) { match (self.contains(i), old.contains(i)) { @@ -112,8 +112,8 @@ where let size = self.domain_size(); assert_eq!(size, old.domain_size()); - let mut set_in_self = ChunkedBitSet::new_empty(size); - let mut cleared_in_self = ChunkedBitSet::new_empty(size); + let mut set_in_self = MixedBitSet::new_empty(size); + let mut cleared_in_self = MixedBitSet::new_empty(size); for i in (0..size).map(T::new) { match (self.contains(i), old.contains(i)) { @@ -127,6 +127,26 @@ where } } +impl<T, C> DebugWithContext<C> for MixedBitSet<T> +where + T: Idx + DebugWithContext<C>, +{ + fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + MixedBitSet::Small(set) => set.fmt_with(ctxt, f), + MixedBitSet::Large(set) => set.fmt_with(ctxt, f), + } + } + + fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match (self, old) { + (MixedBitSet::Small(set), MixedBitSet::Small(old)) => set.fmt_diff_with(old, ctxt, f), + (MixedBitSet::Large(set), MixedBitSet::Large(old)) => set.fmt_diff_with(old, ctxt, f), + _ => panic!("MixedBitSet size mismatch"), + } + } +} + impl<S, C> DebugWithContext<C> for MaybeReachable<S> where S: DebugWithContext<C>, @@ -159,8 +179,8 @@ where } fn fmt_diff<T, C>( - inserted: &ChunkedBitSet<T>, - removed: &ChunkedBitSet<T>, + inserted: &MixedBitSet<T>, + removed: &MixedBitSet<T>, ctxt: &C, f: &mut fmt::Formatter<'_>, ) -> fmt::Result diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index 561229cf725..5b2b128e035 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -2,19 +2,186 @@ use std::borrow::Cow; use std::cell::RefCell; +use std::ffi::OsString; +use std::path::PathBuf; use std::sync::OnceLock; use std::{io, ops, str}; use regex::Regex; -use rustc_graphviz as dot; +use rustc_hir::def_id::DefId; use rustc_index::bit_set::BitSet; -use rustc_middle::mir::{self, BasicBlock, Body, Location, graphviz_safe_def_name}; +use rustc_middle::mir::{ + self, BasicBlock, Body, Location, create_dump_file, dump_enabled, graphviz_safe_def_name, + traversal, +}; +use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_span::symbol::{Symbol, sym}; +use tracing::debug; +use {rustc_ast as ast, rustc_graphviz as dot}; use super::fmt::{DebugDiffWithAdapter, DebugWithAdapter, DebugWithContext}; use super::{Analysis, CallReturnPlaces, Direction, Results, ResultsCursor, ResultsVisitor}; +use crate::errors::{ + DuplicateValuesFor, PathMustEndInFilename, RequiresAnArgument, UnknownFormatter, +}; + +/// Writes a DOT file containing the results of a dataflow analysis if the user requested it via +/// `rustc_mir` attributes and `-Z dump-mir-dataflow`. The `Result` in and the `Results` out are +/// the same. +pub(super) fn write_graphviz_results<'tcx, A>( + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + results: &mut Results<'tcx, A>, + pass_name: Option<&'static str>, +) -> std::io::Result<()> +where + A: Analysis<'tcx>, + A::Domain: DebugWithContext<A>, +{ + use std::fs; + use std::io::Write; + + let def_id = body.source.def_id(); + let Ok(attrs) = RustcMirAttrs::parse(tcx, def_id) else { + // Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse` + return Ok(()); + }; + + let file = try { + match attrs.output_path(A::NAME) { + Some(path) => { + debug!("printing dataflow results for {:?} to {}", def_id, path.display()); + if let Some(parent) = path.parent() { + fs::create_dir_all(parent)?; + } + fs::File::create_buffered(&path)? + } + + None if dump_enabled(tcx, A::NAME, def_id) => { + create_dump_file(tcx, "dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)? + } + + _ => return Ok(()), + } + }; + let mut file = match file { + Ok(f) => f, + Err(e) => return Err(e), + }; + + let style = match attrs.formatter { + Some(sym::two_phase) => OutputStyle::BeforeAndAfter, + _ => OutputStyle::AfterOnly, + }; + + let mut buf = Vec::new(); + + let graphviz = Formatter::new(body, results, style); + let mut render_opts = + vec![dot::RenderOption::Fontname(tcx.sess.opts.unstable_opts.graphviz_font.clone())]; + if tcx.sess.opts.unstable_opts.graphviz_dark_mode { + render_opts.push(dot::RenderOption::DarkTheme); + } + let r = with_no_trimmed_paths!(dot::render_opts(&graphviz, &mut buf, &render_opts)); + + let lhs = try { + r?; + file.write_all(&buf)?; + }; + + lhs +} + +#[derive(Default)] +struct RustcMirAttrs { + basename_and_suffix: Option<PathBuf>, + formatter: Option<Symbol>, +} + +impl RustcMirAttrs { + fn parse(tcx: TyCtxt<'_>, def_id: DefId) -> Result<Self, ()> { + let mut result = Ok(()); + let mut ret = RustcMirAttrs::default(); + + let rustc_mir_attrs = tcx + .get_attrs(def_id, sym::rustc_mir) + .flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter())); + + for attr in rustc_mir_attrs { + let attr_result = if attr.has_name(sym::borrowck_graphviz_postflow) { + Self::set_field(&mut ret.basename_and_suffix, tcx, &attr, |s| { + let path = PathBuf::from(s.to_string()); + match path.file_name() { + Some(_) => Ok(path), + None => { + tcx.dcx().emit_err(PathMustEndInFilename { span: attr.span() }); + Err(()) + } + } + }) + } else if attr.has_name(sym::borrowck_graphviz_format) { + Self::set_field(&mut ret.formatter, tcx, &attr, |s| match s { + sym::gen_kill | sym::two_phase => Ok(s), + _ => { + tcx.dcx().emit_err(UnknownFormatter { span: attr.span() }); + Err(()) + } + }) + } else { + Ok(()) + }; + + result = result.and(attr_result); + } + + result.map(|()| ret) + } + + fn set_field<T>( + field: &mut Option<T>, + tcx: TyCtxt<'_>, + attr: &ast::MetaItemInner, + mapper: impl FnOnce(Symbol) -> Result<T, ()>, + ) -> Result<(), ()> { + if field.is_some() { + tcx.dcx() + .emit_err(DuplicateValuesFor { span: attr.span(), name: attr.name_or_empty() }); + + return Err(()); + } + + if let Some(s) = attr.value_str() { + *field = Some(mapper(s)?); + Ok(()) + } else { + tcx.dcx() + .emit_err(RequiresAnArgument { span: attr.span(), name: attr.name_or_empty() }); + Err(()) + } + } + + /// Returns the path where dataflow results should be written, or `None` + /// `borrowck_graphviz_postflow` was not specified. + /// + /// This performs the following transformation to the argument of `borrowck_graphviz_postflow`: + /// + /// "path/suffix.dot" -> "path/analysis_name_suffix.dot" + fn output_path(&self, analysis_name: &str) -> Option<PathBuf> { + let mut ret = self.basename_and_suffix.as_ref().cloned()?; + let suffix = ret.file_name().unwrap(); // Checked when parsing attrs + + let mut file_name: OsString = analysis_name.into(); + file_name.push("_"); + file_name.push(suffix); + ret.set_file_name(file_name); + + Some(ret) + } +} #[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub(crate) enum OutputStyle { +enum OutputStyle { AfterOnly, BeforeAndAfter, } @@ -28,7 +195,7 @@ impl OutputStyle { } } -pub(crate) struct Formatter<'mir, 'tcx, A> +struct Formatter<'mir, 'tcx, A> where A: Analysis<'tcx>, { @@ -45,12 +212,12 @@ impl<'mir, 'tcx, A> Formatter<'mir, 'tcx, A> where A: Analysis<'tcx>, { - pub(crate) fn new( + fn new( body: &'mir Body<'tcx>, results: &'mir mut Results<'tcx, A>, style: OutputStyle, ) -> Self { - let reachable = mir::traversal::reachable_as_bitset(body); + let reachable = traversal::reachable_as_bitset(body); Formatter { cursor: results.as_results_cursor(body).into(), style, reachable } } @@ -61,7 +228,7 @@ where /// A pair of a basic block and an index into that basic blocks `successors`. #[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub(crate) struct CfgEdge { +struct CfgEdge { source: BasicBlock, index: usize, } @@ -520,7 +687,7 @@ struct StateDiffCollector<D> { impl<D> StateDiffCollector<D> { fn run<'tcx, A>( - body: &mir::Body<'tcx>, + body: &Body<'tcx>, block: BasicBlock, results: &mut Results<'tcx, A>, style: OutputStyle, @@ -557,7 +724,7 @@ where } } - fn visit_statement_before_primary_effect( + fn visit_after_early_statement_effect( &mut self, results: &mut Results<'tcx, A>, state: &A::Domain, @@ -570,7 +737,7 @@ where } } - fn visit_statement_after_primary_effect( + fn visit_after_primary_statement_effect( &mut self, results: &mut Results<'tcx, A>, state: &A::Domain, @@ -581,7 +748,7 @@ where self.prev_state.clone_from(state) } - fn visit_terminator_before_primary_effect( + fn visit_after_early_terminator_effect( &mut self, results: &mut Results<'tcx, A>, state: &A::Domain, @@ -594,7 +761,7 @@ where } } - fn visit_terminator_after_primary_effect( + fn visit_after_primary_terminator_effect( &mut self, results: &mut Results<'tcx, A>, state: &A::Domain, diff --git a/compiler/rustc_mir_dataflow/src/framework/lattice.rs b/compiler/rustc_mir_dataflow/src/framework/lattice.rs index e2b56aedca3..cb8159ce37b 100644 --- a/compiler/rustc_mir_dataflow/src/framework/lattice.rs +++ b/compiler/rustc_mir_dataflow/src/framework/lattice.rs @@ -38,10 +38,8 @@ //! [Hasse diagram]: https://en.wikipedia.org/wiki/Hasse_diagram //! [poset]: https://en.wikipedia.org/wiki/Partially_ordered_set -use std::iter; - -use rustc_index::bit_set::{BitSet, ChunkedBitSet}; -use rustc_index::{Idx, IndexVec}; +use rustc_index::Idx; +use rustc_index::bit_set::{BitSet, MixedBitSet}; use crate::framework::BitSetExt; @@ -70,53 +68,6 @@ pub trait HasTop { const TOP: Self; } -/// A `bool` is a "two-point" lattice with `true` as the top element and `false` as the bottom: -/// -/// ```text -/// true -/// | -/// false -/// ``` -impl JoinSemiLattice for bool { - fn join(&mut self, other: &Self) -> bool { - if let (false, true) = (*self, *other) { - *self = true; - return true; - } - - false - } -} - -impl HasBottom for bool { - const BOTTOM: Self = false; - - fn is_bottom(&self) -> bool { - !self - } -} - -impl HasTop for bool { - const TOP: Self = true; -} - -/// A tuple (or list) of lattices is itself a lattice whose least upper bound is the concatenation -/// of the least upper bounds of each element of the tuple (or list). -/// -/// In other words: -/// (A₀, A₁, ..., Aₙ) ∨ (B₀, B₁, ..., Bₙ) = (A₀∨B₀, A₁∨B₁, ..., Aₙ∨Bₙ) -impl<I: Idx, T: JoinSemiLattice> JoinSemiLattice for IndexVec<I, T> { - fn join(&mut self, other: &Self) -> bool { - assert_eq!(self.len(), other.len()); - - let mut changed = false; - for (a, b) in iter::zip(self, other) { - changed |= a.join(b); - } - changed - } -} - /// A `BitSet` represents the lattice formed by the powerset of all possible values of /// the index type `T` ordered by inclusion. Equivalently, it is a tuple of "two-point" lattices, /// one for each possible value of `T`. @@ -126,7 +77,7 @@ impl<T: Idx> JoinSemiLattice for BitSet<T> { } } -impl<T: Idx> JoinSemiLattice for ChunkedBitSet<T> { +impl<T: Idx> JoinSemiLattice for MixedBitSet<T> { fn join(&mut self, other: &Self) -> bool { self.union(other) } @@ -197,18 +148,6 @@ impl<T> MaybeReachable<T> { } } -impl<T> HasBottom for MaybeReachable<T> { - const BOTTOM: Self = MaybeReachable::Unreachable; - - fn is_bottom(&self) -> bool { - matches!(self, Self::Unreachable) - } -} - -impl<T: HasTop> HasTop for MaybeReachable<T> { - const TOP: Self = MaybeReachable::Reachable(T::TOP); -} - impl<S> MaybeReachable<S> { /// Return whether the current state contains the given element. If the state is unreachable, /// it does no contain anything. diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs index b9407882ec5..41df5fae0de 100644 --- a/compiler/rustc_mir_dataflow/src/framework/mod.rs +++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs @@ -35,14 +35,14 @@ use std::cmp::Ordering; use rustc_data_structures::work_queue::WorkQueue; -use rustc_index::bit_set::{BitSet, ChunkedBitSet}; +use rustc_index::bit_set::{BitSet, MixedBitSet}; use rustc_index::{Idx, IndexVec}; use rustc_middle::bug; use rustc_middle::mir::{self, BasicBlock, CallReturnPlaces, Location, TerminatorEdges, traversal}; use rustc_middle::ty::TyCtxt; use tracing::error; -use self::results::write_graphviz_results; +use self::graphviz::write_graphviz_results; use super::fmt::DebugWithContext; mod cursor; @@ -56,7 +56,7 @@ mod visitor; pub use self::cursor::ResultsCursor; pub use self::direction::{Backward, Direction, Forward}; pub use self::lattice::{JoinSemiLattice, MaybeReachable}; -pub use self::results::{EntrySets, Results}; +pub use self::results::{EntryStates, Results}; pub use self::visitor::{ResultsVisitor, visit_results}; /// Analysis domains are all bitsets of various kinds. This trait holds @@ -71,7 +71,7 @@ impl<T: Idx> BitSetExt<T> for BitSet<T> { } } -impl<T: Idx> BitSetExt<T> for ChunkedBitSet<T> { +impl<T: Idx> BitSetExt<T> for MixedBitSet<T> { fn contains(&self, elem: T) -> bool { self.contains(elem) } @@ -122,8 +122,23 @@ pub trait Analysis<'tcx> { // `resume`). It's not obvious how to handle `yield` points in coroutines, however. fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain); + /// Updates the current dataflow state with an "early" effect, i.e. one + /// that occurs immediately before the given statement. + /// + /// This method is useful if the consumer of the results of this analysis only needs to observe + /// *part* of the effect of a statement (e.g. for two-phase borrows). As a general rule, + /// analyses should not implement this without also implementing + /// `apply_primary_statement_effect`. + fn apply_early_statement_effect( + &mut self, + _state: &mut Self::Domain, + _statement: &mir::Statement<'tcx>, + _location: Location, + ) { + } + /// Updates the current dataflow state with the effect of evaluating a statement. - fn apply_statement_effect( + fn apply_primary_statement_effect( &mut self, state: &mut Self::Domain, statement: &mir::Statement<'tcx>, @@ -131,15 +146,16 @@ pub trait Analysis<'tcx> { ); /// Updates the current dataflow state with an effect that occurs immediately *before* the - /// given statement. + /// given terminator. /// - /// This method is useful if the consumer of the results of this analysis only needs to observe - /// *part* of the effect of a statement (e.g. for two-phase borrows). As a general rule, - /// analyses should not implement this without also implementing `apply_statement_effect`. - fn apply_before_statement_effect( + /// This method is useful if the consumer of the results of this analysis needs only to observe + /// *part* of the effect of a terminator (e.g. for two-phase borrows). As a general rule, + /// analyses should not implement this without also implementing + /// `apply_primary_terminator_effect`. + fn apply_early_terminator_effect( &mut self, _state: &mut Self::Domain, - _statement: &mir::Statement<'tcx>, + _terminator: &mir::Terminator<'tcx>, _location: Location, ) { } @@ -150,7 +166,7 @@ pub trait Analysis<'tcx> { /// in this function. That should go in `apply_call_return_effect`. For example, in the /// `InitializedPlaces` analyses, the return place for a function call is not marked as /// initialized here. - fn apply_terminator_effect<'mir>( + fn apply_primary_terminator_effect<'mir>( &mut self, _state: &mut Self::Domain, terminator: &'mir mir::Terminator<'tcx>, @@ -159,27 +175,13 @@ pub trait Analysis<'tcx> { terminator.edges() } - /// Updates the current dataflow state with an effect that occurs immediately *before* the - /// given terminator. - /// - /// This method is useful if the consumer of the results of this analysis needs only to observe - /// *part* of the effect of a terminator (e.g. for two-phase borrows). As a general rule, - /// analyses should not implement this without also implementing `apply_terminator_effect`. - fn apply_before_terminator_effect( - &mut self, - _state: &mut Self::Domain, - _terminator: &mir::Terminator<'tcx>, - _location: Location, - ) { - } - /* Edge-specific effects */ /// Updates the current dataflow state with the effect of a successful return from a `Call` /// terminator. /// - /// This is separate from `apply_terminator_effect` to properly track state across unwind - /// edges. + /// This is separate from `apply_primary_terminator_effect` to properly track state across + /// unwind edges. fn apply_call_return_effect( &mut self, _state: &mut Self::Domain, @@ -234,11 +236,12 @@ pub trait Analysis<'tcx> { Self: Sized, Self::Domain: DebugWithContext<Self>, { - let mut entry_sets = + let mut entry_states = IndexVec::from_fn_n(|_| self.bottom_value(body), body.basic_blocks.len()); - self.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]); + self.initialize_start_block(body, &mut entry_states[mir::START_BLOCK]); - if Self::Direction::IS_BACKWARD && entry_sets[mir::START_BLOCK] != self.bottom_value(body) { + if Self::Direction::IS_BACKWARD && entry_states[mir::START_BLOCK] != self.bottom_value(body) + { bug!("`initialize_start_block` is not yet supported for backward dataflow analyses"); } @@ -262,9 +265,9 @@ pub trait Analysis<'tcx> { let mut state = self.bottom_value(body); while let Some(bb) = dirty_queue.pop() { // Set the state to the entry state of the block. - // This is equivalent to `state = entry_sets[bb].clone()`, + // This is equivalent to `state = entry_states[bb].clone()`, // but it saves an allocation, thus improving compile times. - state.clone_from(&entry_sets[bb]); + state.clone_from(&entry_states[bb]); Self::Direction::apply_effects_in_block( &mut self, @@ -273,7 +276,7 @@ pub trait Analysis<'tcx> { bb, &body[bb], |target: BasicBlock, state: &Self::Domain| { - let set_changed = entry_sets[target].join(state); + let set_changed = entry_states[target].join(state); if set_changed { dirty_queue.insert(target); } @@ -281,7 +284,7 @@ pub trait Analysis<'tcx> { ); } - let mut results = Results { analysis: self, entry_sets }; + let mut results = Results { analysis: self, entry_states }; if tcx.sess.opts.unstable_opts.dump_mir_dataflow { let res = write_graphviz_results(tcx, body, &mut results, pass_name); @@ -327,7 +330,7 @@ impl<T: Idx> GenKill<T> for BitSet<T> { } } -impl<T: Idx> GenKill<T> for ChunkedBitSet<T> { +impl<T: Idx> GenKill<T> for MixedBitSet<T> { fn gen_(&mut self, elem: T) { self.insert(elem); } @@ -358,11 +361,10 @@ impl<T, S: GenKill<T>> GenKill<T> for MaybeReachable<S> { // NOTE: DO NOT CHANGE VARIANT ORDER. The derived `Ord` impls rely on the current order. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] enum Effect { - /// The "before" effect (e.g., `apply_before_statement_effect`) for a statement (or - /// terminator). - Before, + /// The "early" effect (e.g., `apply_early_statement_effect`) for a statement/terminator. + Early, - /// The "primary" effect (e.g., `apply_statement_effect`) for a statement (or terminator). + /// The "primary" effect (e.g., `apply_primary_statement_effect`) for a statement/terminator. Primary, } @@ -381,15 +383,15 @@ pub struct EffectIndex { impl EffectIndex { fn next_in_forward_order(self) -> Self { match self.effect { - Effect::Before => Effect::Primary.at_index(self.statement_index), - Effect::Primary => Effect::Before.at_index(self.statement_index + 1), + Effect::Early => Effect::Primary.at_index(self.statement_index), + Effect::Primary => Effect::Early.at_index(self.statement_index + 1), } } fn next_in_backward_order(self) -> Self { match self.effect { - Effect::Before => Effect::Primary.at_index(self.statement_index), - Effect::Primary => Effect::Before.at_index(self.statement_index - 1), + Effect::Early => Effect::Primary.at_index(self.statement_index), + Effect::Primary => Effect::Early.at_index(self.statement_index - 1), } } diff --git a/compiler/rustc_mir_dataflow/src/framework/results.rs b/compiler/rustc_mir_dataflow/src/framework/results.rs index c4321c454e6..8e2c3afddb3 100644 --- a/compiler/rustc_mir_dataflow/src/framework/results.rs +++ b/compiler/rustc_mir_dataflow/src/framework/results.rs @@ -1,25 +1,12 @@ //! Dataflow analysis results. -use std::ffi::OsString; -use std::path::PathBuf; - -use rustc_hir::def_id::DefId; use rustc_index::IndexVec; -use rustc_middle::mir::{self, BasicBlock, create_dump_file, dump_enabled, traversal}; -use rustc_middle::ty::TyCtxt; -use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_span::symbol::{Symbol, sym}; -use tracing::debug; -use {rustc_ast as ast, rustc_graphviz as dot}; +use rustc_middle::mir::{BasicBlock, Body, traversal}; -use super::fmt::DebugWithContext; -use super::{Analysis, ResultsCursor, ResultsVisitor, graphviz, visit_results}; -use crate::errors::{ - DuplicateValuesFor, PathMustEndInFilename, RequiresAnArgument, UnknownFormatter, -}; +use super::{Analysis, ResultsCursor, ResultsVisitor, visit_results}; use crate::framework::cursor::ResultsHandle; -pub type EntrySets<'tcx, A> = IndexVec<BasicBlock, <A as Analysis<'tcx>>::Domain>; +pub type EntryStates<'tcx, A> = IndexVec<BasicBlock, <A as Analysis<'tcx>>::Domain>; /// A dataflow analysis that has converged to fixpoint. It only holds the domain values at the /// entry of each basic block. Domain values in other parts of the block are recomputed on the fly @@ -30,7 +17,7 @@ where A: Analysis<'tcx>, { pub analysis: A, - pub entry_sets: EntrySets<'tcx, A>, + pub entry_states: EntryStates<'tcx, A>, } impl<'tcx, A> Results<'tcx, A> @@ -41,27 +28,24 @@ where /// `Results` is also used outside the cursor. pub fn as_results_cursor<'mir>( &'mir mut self, - body: &'mir mir::Body<'tcx>, + body: &'mir Body<'tcx>, ) -> ResultsCursor<'mir, 'tcx, A> { ResultsCursor::new(body, ResultsHandle::BorrowedMut(self)) } /// Creates a `ResultsCursor` that takes ownership of the `Results`. - pub fn into_results_cursor<'mir>( - self, - body: &'mir mir::Body<'tcx>, - ) -> ResultsCursor<'mir, 'tcx, A> { + pub fn into_results_cursor<'mir>(self, body: &'mir Body<'tcx>) -> ResultsCursor<'mir, 'tcx, A> { ResultsCursor::new(body, ResultsHandle::Owned(self)) } /// Gets the dataflow state for the given block. pub fn entry_set_for_block(&self, block: BasicBlock) -> &A::Domain { - &self.entry_sets[block] + &self.entry_states[block] } pub fn visit_with<'mir>( &mut self, - body: &'mir mir::Body<'tcx>, + body: &'mir Body<'tcx>, blocks: impl IntoIterator<Item = BasicBlock>, vis: &mut impl ResultsVisitor<'mir, 'tcx, A>, ) { @@ -70,166 +54,10 @@ where pub fn visit_reachable_with<'mir>( &mut self, - body: &'mir mir::Body<'tcx>, + body: &'mir Body<'tcx>, vis: &mut impl ResultsVisitor<'mir, 'tcx, A>, ) { let blocks = traversal::reachable(body); visit_results(body, blocks.map(|(bb, _)| bb), self, vis) } } - -// Graphviz - -/// Writes a DOT file containing the results of a dataflow analysis if the user requested it via -/// `rustc_mir` attributes and `-Z dump-mir-dataflow`. The `Result` in and the `Results` out are -/// the same. -pub(super) fn write_graphviz_results<'tcx, A>( - tcx: TyCtxt<'tcx>, - body: &mir::Body<'tcx>, - results: &mut Results<'tcx, A>, - pass_name: Option<&'static str>, -) -> std::io::Result<()> -where - A: Analysis<'tcx>, - A::Domain: DebugWithContext<A>, -{ - use std::fs; - use std::io::Write; - - let def_id = body.source.def_id(); - let Ok(attrs) = RustcMirAttrs::parse(tcx, def_id) else { - // Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse` - return Ok(()); - }; - - let file = try { - match attrs.output_path(A::NAME) { - Some(path) => { - debug!("printing dataflow results for {:?} to {}", def_id, path.display()); - if let Some(parent) = path.parent() { - fs::create_dir_all(parent)?; - } - fs::File::create_buffered(&path)? - } - - None if dump_enabled(tcx, A::NAME, def_id) => { - create_dump_file(tcx, "dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)? - } - - _ => return Ok(()), - } - }; - let mut file = match file { - Ok(f) => f, - Err(e) => return Err(e), - }; - - let style = match attrs.formatter { - Some(sym::two_phase) => graphviz::OutputStyle::BeforeAndAfter, - _ => graphviz::OutputStyle::AfterOnly, - }; - - let mut buf = Vec::new(); - - let graphviz = graphviz::Formatter::new(body, results, style); - let mut render_opts = - vec![dot::RenderOption::Fontname(tcx.sess.opts.unstable_opts.graphviz_font.clone())]; - if tcx.sess.opts.unstable_opts.graphviz_dark_mode { - render_opts.push(dot::RenderOption::DarkTheme); - } - let r = with_no_trimmed_paths!(dot::render_opts(&graphviz, &mut buf, &render_opts)); - - let lhs = try { - r?; - file.write_all(&buf)?; - }; - - lhs -} - -#[derive(Default)] -struct RustcMirAttrs { - basename_and_suffix: Option<PathBuf>, - formatter: Option<Symbol>, -} - -impl RustcMirAttrs { - fn parse(tcx: TyCtxt<'_>, def_id: DefId) -> Result<Self, ()> { - let mut result = Ok(()); - let mut ret = RustcMirAttrs::default(); - - let rustc_mir_attrs = tcx - .get_attrs(def_id, sym::rustc_mir) - .flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter())); - - for attr in rustc_mir_attrs { - let attr_result = if attr.has_name(sym::borrowck_graphviz_postflow) { - Self::set_field(&mut ret.basename_and_suffix, tcx, &attr, |s| { - let path = PathBuf::from(s.to_string()); - match path.file_name() { - Some(_) => Ok(path), - None => { - tcx.dcx().emit_err(PathMustEndInFilename { span: attr.span() }); - Err(()) - } - } - }) - } else if attr.has_name(sym::borrowck_graphviz_format) { - Self::set_field(&mut ret.formatter, tcx, &attr, |s| match s { - sym::gen_kill | sym::two_phase => Ok(s), - _ => { - tcx.dcx().emit_err(UnknownFormatter { span: attr.span() }); - Err(()) - } - }) - } else { - Ok(()) - }; - - result = result.and(attr_result); - } - - result.map(|()| ret) - } - - fn set_field<T>( - field: &mut Option<T>, - tcx: TyCtxt<'_>, - attr: &ast::MetaItemInner, - mapper: impl FnOnce(Symbol) -> Result<T, ()>, - ) -> Result<(), ()> { - if field.is_some() { - tcx.dcx() - .emit_err(DuplicateValuesFor { span: attr.span(), name: attr.name_or_empty() }); - - return Err(()); - } - - if let Some(s) = attr.value_str() { - *field = Some(mapper(s)?); - Ok(()) - } else { - tcx.dcx() - .emit_err(RequiresAnArgument { span: attr.span(), name: attr.name_or_empty() }); - Err(()) - } - } - - /// Returns the path where dataflow results should be written, or `None` - /// `borrowck_graphviz_postflow` was not specified. - /// - /// This performs the following transformation to the argument of `borrowck_graphviz_postflow`: - /// - /// "path/suffix.dot" -> "path/analysis_name_suffix.dot" - fn output_path(&self, analysis_name: &str) -> Option<PathBuf> { - let mut ret = self.basename_and_suffix.as_ref().cloned()?; - let suffix = ret.file_name().unwrap(); // Checked when parsing attrs - - let mut file_name: OsString = analysis_name.into(); - file_name.push("_"); - file_name.push(suffix); - ret.set_file_name(file_name); - - Some(ret) - } -} diff --git a/compiler/rustc_mir_dataflow/src/framework/tests.rs b/compiler/rustc_mir_dataflow/src/framework/tests.rs index 14fb6dfb50c..8e7d4ab0fa3 100644 --- a/compiler/rustc_mir_dataflow/src/framework/tests.rs +++ b/compiler/rustc_mir_dataflow/src/framework/tests.rs @@ -73,7 +73,7 @@ fn mock_body<'tcx>() -> mir::Body<'tcx> { /// /// The `102` in the block's entry set is derived from the basic block index and ensures that the /// expected state is unique across all basic blocks. Remember, it is generated by -/// `mock_entry_sets`, not from actually running `MockAnalysis` to fixpoint. +/// `mock_entry_states`, not from actually running `MockAnalysis` to fixpoint. struct MockAnalysis<'tcx, D> { body: &'tcx mir::Body<'tcx>, dir: PhantomData<D>, @@ -90,7 +90,7 @@ impl<D: Direction> MockAnalysis<'_, D> { ret } - fn mock_entry_sets(&self) -> IndexVec<BasicBlock, BitSet<usize>> { + fn mock_entry_states(&self) -> IndexVec<BasicBlock, BitSet<usize>> { let empty = self.bottom_value(self.body); let mut ret = IndexVec::from_elem(empty, &self.body.basic_blocks); @@ -104,7 +104,7 @@ impl<D: Direction> MockAnalysis<'_, D> { /// Returns the index that should be added to the dataflow state at the given target. fn effect(&self, loc: EffectIndex) -> usize { let idx = match loc.effect { - Effect::Before => loc.statement_index * 2, + Effect::Early => loc.statement_index * 2, Effect::Primary => loc.statement_index * 2 + 1, }; @@ -128,14 +128,14 @@ impl<D: Direction> MockAnalysis<'_, D> { let target = match target { SeekTarget::BlockEntry { .. } => return ret, - SeekTarget::Before(loc) => Effect::Before.at_index(loc.statement_index), + SeekTarget::Early(loc) => Effect::Early.at_index(loc.statement_index), SeekTarget::After(loc) => Effect::Primary.at_index(loc.statement_index), }; let mut pos = if D::IS_FORWARD { - Effect::Before.at_index(0) + Effect::Early.at_index(0) } else { - Effect::Before.at_index(self.body[block].statements.len()) + Effect::Early.at_index(self.body[block].statements.len()) }; loop { @@ -168,52 +168,52 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { unimplemented!("This is never called since `MockAnalysis` is never iterated to fixpoint"); } - fn apply_statement_effect( + fn apply_early_statement_effect( &mut self, state: &mut Self::Domain, _statement: &mir::Statement<'tcx>, location: Location, ) { - let idx = self.effect(Effect::Primary.at_index(location.statement_index)); + let idx = self.effect(Effect::Early.at_index(location.statement_index)); assert!(state.insert(idx)); } - fn apply_before_statement_effect( + fn apply_primary_statement_effect( &mut self, state: &mut Self::Domain, _statement: &mir::Statement<'tcx>, location: Location, ) { - let idx = self.effect(Effect::Before.at_index(location.statement_index)); + let idx = self.effect(Effect::Primary.at_index(location.statement_index)); assert!(state.insert(idx)); } - fn apply_terminator_effect<'mir>( + fn apply_early_terminator_effect( &mut self, state: &mut Self::Domain, - terminator: &'mir mir::Terminator<'tcx>, + _terminator: &mir::Terminator<'tcx>, location: Location, - ) -> TerminatorEdges<'mir, 'tcx> { - let idx = self.effect(Effect::Primary.at_index(location.statement_index)); + ) { + let idx = self.effect(Effect::Early.at_index(location.statement_index)); assert!(state.insert(idx)); - terminator.edges() } - fn apply_before_terminator_effect( + fn apply_primary_terminator_effect<'mir>( &mut self, state: &mut Self::Domain, - _terminator: &mir::Terminator<'tcx>, + terminator: &'mir mir::Terminator<'tcx>, location: Location, - ) { - let idx = self.effect(Effect::Before.at_index(location.statement_index)); + ) -> TerminatorEdges<'mir, 'tcx> { + let idx = self.effect(Effect::Primary.at_index(location.statement_index)); assert!(state.insert(idx)); + terminator.edges() } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] enum SeekTarget { BlockEntry(BasicBlock), - Before(Location), + Early(Location), After(Location), } @@ -223,7 +223,7 @@ impl SeekTarget { match *self { BlockEntry(block) => block, - Before(loc) | After(loc) => loc.block, + Early(loc) | After(loc) => loc.block, } } @@ -235,7 +235,7 @@ impl SeekTarget { .map(move |(i, kind)| { let loc = Location { block, statement_index: i }; match kind { - 0 => SeekTarget::Before(loc), + 0 => SeekTarget::Early(loc), 1 => SeekTarget::After(loc), _ => unreachable!(), } @@ -249,7 +249,7 @@ fn test_cursor<D: Direction>(analysis: MockAnalysis<'_, D>) { let body = analysis.body; let mut cursor = - Results { entry_sets: analysis.mock_entry_sets(), analysis }.into_results_cursor(body); + Results { entry_states: analysis.mock_entry_states(), analysis }.into_results_cursor(body); cursor.allow_unreachable(); @@ -262,7 +262,7 @@ fn test_cursor<D: Direction>(analysis: MockAnalysis<'_, D>) { match targ { BlockEntry(block) => cursor.seek_to_block_entry(block), - Before(loc) => cursor.seek_before_primary_effect(loc), + Early(loc) => cursor.seek_before_primary_effect(loc), After(loc) => cursor.seek_after_primary_effect(loc), } diff --git a/compiler/rustc_mir_dataflow/src/framework/visitor.rs b/compiler/rustc_mir_dataflow/src/framework/visitor.rs index bde41974d47..d18e9fa33f0 100644 --- a/compiler/rustc_mir_dataflow/src/framework/visitor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/visitor.rs @@ -35,9 +35,9 @@ where { fn visit_block_start(&mut self, _state: &A::Domain) {} - /// Called with the `before_statement_effect` of the given statement applied to `state` but not - /// its `statement_effect`. - fn visit_statement_before_primary_effect( + /// // 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, _results: &mut Results<'tcx, A>, _state: &A::Domain, @@ -46,9 +46,8 @@ where ) { } - /// Called with both the `before_statement_effect` and the `statement_effect` of the given - /// statement applied to `state`. - fn visit_statement_after_primary_effect( + /// Called after the "primary" effect of the given statement is applied to `state`. + fn visit_after_primary_statement_effect( &mut self, _results: &mut Results<'tcx, A>, _state: &A::Domain, @@ -57,9 +56,8 @@ where ) { } - /// Called with the `before_terminator_effect` of the given terminator applied to `state` but - /// not its `terminator_effect`. - fn visit_terminator_before_primary_effect( + /// Called after the "early" effect of the given terminator is applied to `state`. + fn visit_after_early_terminator_effect( &mut self, _results: &mut Results<'tcx, A>, _state: &A::Domain, @@ -68,11 +66,10 @@ where ) { } - /// Called with both the `before_terminator_effect` and the `terminator_effect` of the given - /// terminator applied to `state`. + /// Called after the "primary" effect of the given terminator is applied to `state`. /// /// The `call_return_effect` (if one exists) will *not* be applied to `state`. - fn visit_terminator_after_primary_effect( + fn visit_after_primary_terminator_effect( &mut self, _results: &mut Results<'tcx, A>, _state: &A::Domain, diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index cec654cac72..568d8a5acaf 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -33,22 +33,22 @@ impl<'tcx> Analysis<'tcx> for MaybeBorrowedLocals { // No locals are aliased on function entry } - fn apply_statement_effect( + fn apply_primary_statement_effect( &mut self, - trans: &mut Self::Domain, + state: &mut Self::Domain, statement: &Statement<'tcx>, location: Location, ) { - Self::transfer_function(trans).visit_statement(statement, location); + Self::transfer_function(state).visit_statement(statement, location); } - fn apply_terminator_effect<'mir>( + fn apply_primary_terminator_effect<'mir>( &mut self, - trans: &mut Self::Domain, + state: &mut Self::Domain, terminator: &'mir Terminator<'tcx>, location: Location, ) -> TerminatorEdges<'mir, 'tcx> { - Self::transfer_function(trans).visit_terminator(terminator, location); + Self::transfer_function(state).visit_terminator(terminator, location); terminator.edges() } } diff --git a/compiler/rustc_mir_dataflow/src/impls/initialized.rs b/compiler/rustc_mir_dataflow/src/impls/initialized.rs index 2c10d4b1cd3..fb02408e17d 100644 --- a/compiler/rustc_mir_dataflow/src/impls/initialized.rs +++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs @@ -1,7 +1,7 @@ use std::assert_matches::assert_matches; use rustc_index::Idx; -use rustc_index::bit_set::{BitSet, ChunkedBitSet}; +use rustc_index::bit_set::{BitSet, MixedBitSet}; use rustc_middle::bug; use rustc_middle::mir::{self, Body, CallReturnPlaces, Location, TerminatorEdges}; use rustc_middle::ty::{self, TyCtxt}; @@ -70,7 +70,7 @@ impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> { pub fn is_unwind_dead( &self, place: mir::Place<'tcx>, - state: &MaybeReachable<ChunkedBitSet<MovePathIndex>>, + state: &<Self as Analysis<'tcx>>::Domain, ) -> bool { if let LookupResult::Exact(path) = self.move_data().rev_lookup.find(place.as_ref()) { let mut maybe_live = false; @@ -218,34 +218,34 @@ impl<'tcx> HasMoveData<'tcx> for EverInitializedPlaces<'_, 'tcx> { impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> { fn update_bits( - trans: &mut <Self as Analysis<'tcx>>::Domain, + state: &mut <Self as Analysis<'tcx>>::Domain, path: MovePathIndex, - state: DropFlagState, + dfstate: DropFlagState, ) { - match state { - DropFlagState::Absent => trans.kill(path), - DropFlagState::Present => trans.gen_(path), + match dfstate { + DropFlagState::Absent => state.kill(path), + DropFlagState::Present => state.gen_(path), } } } impl<'tcx> MaybeUninitializedPlaces<'_, 'tcx> { fn update_bits( - trans: &mut <Self as Analysis<'tcx>>::Domain, + state: &mut <Self as Analysis<'tcx>>::Domain, path: MovePathIndex, - state: DropFlagState, + dfstate: DropFlagState, ) { - match state { - DropFlagState::Absent => trans.gen_(path), - DropFlagState::Present => trans.kill(path), + match dfstate { + DropFlagState::Absent => state.gen_(path), + DropFlagState::Present => state.kill(path), } } } impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { /// There can be many more `MovePathIndex` than there are locals in a MIR body. - /// We use a chunked bitset to avoid paying too high a memory footprint. - type Domain = MaybeReachable<ChunkedBitSet<MovePathIndex>>; + /// We use a mixed bitset to avoid paying too high a memory footprint. + type Domain = MaybeReachable<MixedBitSet<MovePathIndex>>; const NAME: &'static str = "maybe_init"; @@ -256,21 +256,21 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) { *state = - MaybeReachable::Reachable(ChunkedBitSet::new_empty(self.move_data().move_paths.len())); + MaybeReachable::Reachable(MixedBitSet::new_empty(self.move_data().move_paths.len())); drop_flag_effects_for_function_entry(self.body, self.move_data, |path, s| { assert!(s == DropFlagState::Present); state.gen_(path); }); } - fn apply_statement_effect( + fn apply_primary_statement_effect( &mut self, - trans: &mut Self::Domain, + state: &mut Self::Domain, statement: &mir::Statement<'tcx>, location: Location, ) { drop_flag_effects_for_location(self.body, self.move_data, location, |path, s| { - Self::update_bits(trans, path, s) + Self::update_bits(state, path, s) }); // Mark all places as "maybe init" if they are mutably borrowed. See #90752. @@ -282,12 +282,12 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { && let LookupResult::Exact(mpi) = self.move_data().rev_lookup.find(place.as_ref()) { on_all_children_bits(self.move_data(), mpi, |child| { - trans.gen_(child); + state.gen_(child); }) } } - fn apply_terminator_effect<'mir>( + fn apply_primary_terminator_effect<'mir>( &mut self, state: &mut Self::Domain, terminator: &'mir mir::Terminator<'tcx>, @@ -309,7 +309,7 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { fn apply_call_return_effect( &mut self, - trans: &mut Self::Domain, + state: &mut Self::Domain, _block: mir::BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, ) { @@ -320,7 +320,7 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { self.move_data(), self.move_data().rev_lookup.find(place.as_ref()), |mpi| { - trans.gen_(mpi); + state.gen_(mpi); }, ); }); @@ -345,7 +345,7 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { }; let mut discriminants = enum_def.discriminants(self.tcx); - edge_effects.apply(|trans, edge| { + edge_effects.apply(|state, edge| { let Some(value) = edge.value else { return; }; @@ -363,25 +363,27 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { self.move_data(), enum_place, variant, - |mpi| trans.kill(mpi), + |mpi| state.kill(mpi), ); }); } } +/// There can be many more `MovePathIndex` than there are locals in a MIR body. +/// We use a mixed bitset to avoid paying too high a memory footprint. +pub type MaybeUninitializedPlacesDomain = MixedBitSet<MovePathIndex>; + impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { - /// There can be many more `MovePathIndex` than there are locals in a MIR body. - /// We use a chunked bitset to avoid paying too high a memory footprint. - type Domain = ChunkedBitSet<MovePathIndex>; + type Domain = MaybeUninitializedPlacesDomain; const NAME: &'static str = "maybe_uninit"; fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { - // bottom = initialized (start_block_effect counters this at outset) - ChunkedBitSet::new_empty(self.move_data().move_paths.len()) + // bottom = initialized (`initialize_start_block` overwrites this on first entry) + MixedBitSet::new_empty(self.move_data().move_paths.len()) } - // sets on_entry bits for Arg places + // sets state bits for Arg places fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) { // set all bits to 1 (uninit) before gathering counter-evidence state.insert_all(); @@ -392,28 +394,28 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { }); } - fn apply_statement_effect( + fn apply_primary_statement_effect( &mut self, - trans: &mut Self::Domain, + state: &mut Self::Domain, _statement: &mir::Statement<'tcx>, location: Location, ) { drop_flag_effects_for_location(self.body, self.move_data, location, |path, s| { - Self::update_bits(trans, path, s) + Self::update_bits(state, path, s) }); // Unlike in `MaybeInitializedPlaces` above, we don't need to change the state when a // mutable borrow occurs. Places cannot become uninitialized through a mutable reference. } - fn apply_terminator_effect<'mir>( + fn apply_primary_terminator_effect<'mir>( &mut self, - trans: &mut Self::Domain, + state: &mut Self::Domain, terminator: &'mir mir::Terminator<'tcx>, location: Location, ) -> TerminatorEdges<'mir, 'tcx> { drop_flag_effects_for_location(self.body, self.move_data, location, |path, s| { - Self::update_bits(trans, path, s) + Self::update_bits(state, path, s) }); if self.skip_unreachable_unwind.contains(location.block) { let mir::TerminatorKind::Drop { target, unwind, .. } = terminator.kind else { bug!() }; @@ -426,7 +428,7 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { fn apply_call_return_effect( &mut self, - trans: &mut Self::Domain, + state: &mut Self::Domain, _block: mir::BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, ) { @@ -437,7 +439,7 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { self.move_data(), self.move_data().rev_lookup.find(place.as_ref()), |mpi| { - trans.kill(mpi); + state.kill(mpi); }, ); }); @@ -466,7 +468,7 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { }; let mut discriminants = enum_def.discriminants(self.tcx); - edge_effects.apply(|trans, edge| { + edge_effects.apply(|state, edge| { let Some(value) = edge.value else { return; }; @@ -484,22 +486,24 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { self.move_data(), enum_place, variant, - |mpi| trans.gen_(mpi), + |mpi| state.gen_(mpi), ); }); } } +/// There can be many more `InitIndex` than there are locals in a MIR body. +/// We use a mixed bitset to avoid paying too high a memory footprint. +pub type EverInitializedPlacesDomain = MixedBitSet<InitIndex>; + impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { - /// There can be many more `InitIndex` than there are locals in a MIR body. - /// We use a chunked bitset to avoid paying too high a memory footprint. - type Domain = ChunkedBitSet<InitIndex>; + type Domain = EverInitializedPlacesDomain; const NAME: &'static str = "ever_init"; fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { // bottom = no initialized variables by default - ChunkedBitSet::new_empty(self.move_data().inits.len()) + MixedBitSet::new_empty(self.move_data().inits.len()) } fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) { @@ -508,10 +512,10 @@ impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { } } - #[instrument(skip(self, trans), level = "debug")] - fn apply_statement_effect( + #[instrument(skip(self, state), level = "debug")] + fn apply_primary_statement_effect( &mut self, - trans: &mut Self::Domain, + state: &mut Self::Domain, stmt: &mir::Statement<'tcx>, location: Location, ) { @@ -521,7 +525,7 @@ impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { let rev_lookup = &move_data.rev_lookup; debug!("initializes move_indexes {:?}", init_loc_map[location]); - trans.gen_all(init_loc_map[location].iter().copied()); + state.gen_all(init_loc_map[location].iter().copied()); if let mir::StatementKind::StorageDead(local) = stmt.kind { // End inits for StorageDead, so that an immutable variable can @@ -531,15 +535,15 @@ impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { "clears the ever initialized status of {:?}", init_path_map[move_path_index] ); - trans.kill_all(init_path_map[move_path_index].iter().copied()); + state.kill_all(init_path_map[move_path_index].iter().copied()); } } } - #[instrument(skip(self, trans, terminator), level = "debug")] - fn apply_terminator_effect<'mir>( + #[instrument(skip(self, state, terminator), level = "debug")] + fn apply_primary_terminator_effect<'mir>( &mut self, - trans: &mut Self::Domain, + state: &mut Self::Domain, terminator: &'mir mir::Terminator<'tcx>, location: Location, ) -> TerminatorEdges<'mir, 'tcx> { @@ -548,7 +552,7 @@ impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { let init_loc_map = &move_data.init_loc_map; debug!(?term); debug!("initializes move_indexes {:?}", init_loc_map[location]); - trans.gen_all( + state.gen_all( init_loc_map[location] .iter() .filter(|init_index| { @@ -561,7 +565,7 @@ impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { fn apply_call_return_effect( &mut self, - trans: &mut Self::Domain, + state: &mut Self::Domain, block: mir::BasicBlock, _return_places: CallReturnPlaces<'_, 'tcx>, ) { @@ -570,7 +574,7 @@ impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { let call_loc = self.body.terminator_loc(block); for init_index in &init_loc_map[call_loc] { - trans.gen_(*init_index); + state.gen_(*init_index); } } } diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index fd7254a0210..b2050a6adf9 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -40,33 +40,33 @@ impl<'tcx> Analysis<'tcx> for MaybeLiveLocals { // No variables are live until we observe a use } - fn apply_statement_effect( + fn apply_primary_statement_effect( &mut self, - trans: &mut Self::Domain, + state: &mut Self::Domain, statement: &mir::Statement<'tcx>, location: Location, ) { - TransferFunction(trans).visit_statement(statement, location); + TransferFunction(state).visit_statement(statement, location); } - fn apply_terminator_effect<'mir>( + fn apply_primary_terminator_effect<'mir>( &mut self, - trans: &mut Self::Domain, + state: &mut Self::Domain, terminator: &'mir mir::Terminator<'tcx>, location: Location, ) -> TerminatorEdges<'mir, 'tcx> { - TransferFunction(trans).visit_terminator(terminator, location); + TransferFunction(state).visit_terminator(terminator, location); terminator.edges() } fn apply_call_return_effect( &mut self, - trans: &mut Self::Domain, + state: &mut Self::Domain, _block: mir::BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, ) { if let CallReturnPlaces::Yield(resume_place) = return_places { - YieldResumeEffect(trans).visit_place( + YieldResumeEffect(state).visit_place( &resume_place, PlaceContext::MutatingUse(MutatingUseContext::Yield), Location::START, @@ -74,7 +74,7 @@ impl<'tcx> Analysis<'tcx> for MaybeLiveLocals { } else { return_places.for_each(|place| { if let Some(local) = place.as_local() { - trans.kill(local); + state.kill(local); } }); } @@ -137,10 +137,10 @@ enum DefUse { } impl DefUse { - fn apply(trans: &mut BitSet<Local>, place: Place<'_>, context: PlaceContext) { + fn apply(state: &mut BitSet<Local>, place: Place<'_>, context: PlaceContext) { match DefUse::for_place(place, context) { - Some(DefUse::Def) => trans.kill(place.local), - Some(DefUse::Use) => trans.gen_(place.local), + Some(DefUse::Def) => state.kill(place.local), + Some(DefUse::Use) => state.gen_(place.local), None => {} } } @@ -232,9 +232,9 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { // No variables are live until we observe a use } - fn apply_statement_effect( + fn apply_primary_statement_effect( &mut self, - trans: &mut Self::Domain, + state: &mut Self::Domain, statement: &mir::Statement<'tcx>, location: Location, ) { @@ -258,34 +258,34 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { }; if let Some(destination) = destination { if !destination.is_indirect() - && !trans.contains(destination.local) + && !state.contains(destination.local) && !self.always_live.contains(destination.local) { // This store is dead return; } } - TransferFunction(trans).visit_statement(statement, location); + TransferFunction(state).visit_statement(statement, location); } - fn apply_terminator_effect<'mir>( + fn apply_primary_terminator_effect<'mir>( &mut self, - trans: &mut Self::Domain, + state: &mut Self::Domain, terminator: &'mir mir::Terminator<'tcx>, location: Location, ) -> TerminatorEdges<'mir, 'tcx> { - TransferFunction(trans).visit_terminator(terminator, location); + TransferFunction(state).visit_terminator(terminator, location); terminator.edges() } fn apply_call_return_effect( &mut self, - trans: &mut Self::Domain, + state: &mut Self::Domain, _block: mir::BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, ) { if let CallReturnPlaces::Yield(resume_place) = return_places { - YieldResumeEffect(trans).visit_place( + YieldResumeEffect(state).visit_place( &resume_place, PlaceContext::MutatingUse(MutatingUseContext::Yield), Location::START, @@ -293,7 +293,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { } else { return_places.for_each(|place| { if let Some(local) = place.as_local() { - trans.remove(local); + state.remove(local); } }); } diff --git a/compiler/rustc_mir_dataflow/src/impls/mod.rs b/compiler/rustc_mir_dataflow/src/impls/mod.rs index b9e194b00c5..3f29b819a6d 100644 --- a/compiler/rustc_mir_dataflow/src/impls/mod.rs +++ b/compiler/rustc_mir_dataflow/src/impls/mod.rs @@ -1,7 +1,3 @@ -//! Dataflow analyses are built upon some interpretation of the -//! bitvectors attached to each basic block, represented via a -//! zero-sized structure. - mod borrowed_locals; mod initialized; mod liveness; @@ -9,7 +5,8 @@ mod storage_liveness; pub use self::borrowed_locals::{MaybeBorrowedLocals, borrowed_locals}; pub use self::initialized::{ - EverInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces, + EverInitializedPlaces, EverInitializedPlacesDomain, MaybeInitializedPlaces, + MaybeUninitializedPlaces, MaybeUninitializedPlacesDomain, }; pub use self::liveness::{ MaybeLiveLocals, MaybeTransitiveLiveLocals, TransferFunction as LivenessTransferFunction, diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs index 1aae06d79d3..65b480d3a5e 100644 --- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs @@ -44,23 +44,23 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeStorageLive<'a> { BitSet::new_empty(body.local_decls.len()) } - fn initialize_start_block(&self, body: &Body<'tcx>, on_entry: &mut Self::Domain) { - on_entry.union(&*self.always_live_locals); + fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) { + state.union(&*self.always_live_locals); for arg in body.args_iter() { - on_entry.insert(arg); + state.insert(arg); } } - fn apply_statement_effect( + fn apply_primary_statement_effect( &mut self, - trans: &mut Self::Domain, + state: &mut Self::Domain, stmt: &Statement<'tcx>, _: Location, ) { match stmt.kind { - StatementKind::StorageLive(l) => trans.gen_(l), - StatementKind::StorageDead(l) => trans.kill(l), + StatementKind::StorageLive(l) => state.gen_(l), + StatementKind::StorageDead(l) => state.kill(l), _ => (), } } @@ -86,25 +86,25 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeStorageDead<'a> { BitSet::new_empty(body.local_decls.len()) } - fn initialize_start_block(&self, body: &Body<'tcx>, on_entry: &mut Self::Domain) { + fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) { assert_eq!(body.local_decls.len(), self.always_live_locals.domain_size()); // Do not iterate on return place and args, as they are trivially always live. for local in body.vars_and_temps_iter() { if !self.always_live_locals.contains(local) { - on_entry.insert(local); + state.insert(local); } } } - fn apply_statement_effect( + fn apply_primary_statement_effect( &mut self, - trans: &mut Self::Domain, + state: &mut Self::Domain, stmt: &Statement<'tcx>, _: Location, ) { match stmt.kind { - StatementKind::StorageLive(l) => trans.kill(l), - StatementKind::StorageDead(l) => trans.gen_(l), + StatementKind::StorageLive(l) => state.kill(l), + StatementKind::StorageDead(l) => state.gen_(l), _ => (), } } @@ -134,31 +134,31 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { BitSet::new_empty(body.local_decls.len()) } - fn initialize_start_block(&self, body: &Body<'tcx>, on_entry: &mut Self::Domain) { + fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) { // The resume argument is live on function entry (we don't care about // the `self` argument) for arg in body.args_iter().skip(1) { - on_entry.insert(arg); + state.insert(arg); } } - fn apply_before_statement_effect( + fn apply_early_statement_effect( &mut self, - trans: &mut Self::Domain, + state: &mut Self::Domain, stmt: &Statement<'tcx>, loc: Location, ) { // If a place is borrowed in a statement, it needs storage for that statement. - MaybeBorrowedLocals::transfer_function(trans).visit_statement(stmt, loc); + MaybeBorrowedLocals::transfer_function(state).visit_statement(stmt, loc); match &stmt.kind { - StatementKind::StorageDead(l) => trans.kill(*l), + StatementKind::StorageDead(l) => state.kill(*l), // If a place is assigned to in a statement, it needs storage for that statement. StatementKind::Assign(box (place, _)) | StatementKind::SetDiscriminant { box place, .. } | StatementKind::Deinit(box place) => { - trans.gen_(place.local); + state.gen_(place.local); } // Nothing to do for these. Match exhaustively so this fails to compile when new @@ -176,29 +176,29 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { } } - fn apply_statement_effect( + fn apply_primary_statement_effect( &mut self, - trans: &mut Self::Domain, + state: &mut Self::Domain, _: &Statement<'tcx>, loc: Location, ) { // If we move from a place then it only stops needing storage *after* // that statement. - self.check_for_move(trans, loc); + self.check_for_move(state, loc); } - fn apply_before_terminator_effect( + fn apply_early_terminator_effect( &mut self, - trans: &mut Self::Domain, + state: &mut Self::Domain, terminator: &Terminator<'tcx>, loc: Location, ) { // If a place is borrowed in a terminator, it needs storage for that terminator. - MaybeBorrowedLocals::transfer_function(trans).visit_terminator(terminator, loc); + MaybeBorrowedLocals::transfer_function(state).visit_terminator(terminator, loc); match &terminator.kind { TerminatorKind::Call { destination, .. } => { - trans.gen_(destination.local); + state.gen_(destination.local); } // Note that we do *not* gen the `resume_arg` of `Yield` terminators. The reason for @@ -213,7 +213,7 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { InlineAsmOperand::Out { place, .. } | InlineAsmOperand::InOut { out_place: place, .. } => { if let Some(place) = place { - trans.gen_(place.local); + state.gen_(place.local); } } InlineAsmOperand::In { .. } @@ -242,9 +242,9 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { } } - fn apply_terminator_effect<'t>( + fn apply_primary_terminator_effect<'t>( &mut self, - trans: &mut Self::Domain, + state: &mut Self::Domain, terminator: &'t Terminator<'tcx>, loc: Location, ) -> TerminatorEdges<'t, 'tcx> { @@ -254,12 +254,12 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { // Since `propagate_call_unwind` doesn't exist, we have to kill the // destination here, and then gen it again in `call_return_effect`. TerminatorKind::Call { destination, .. } => { - trans.kill(destination.local); + state.kill(destination.local); } // The same applies to InlineAsm outputs. TerminatorKind::InlineAsm { ref operands, .. } => { - CallReturnPlaces::InlineAsm(operands).for_each(|place| trans.kill(place.local)); + CallReturnPlaces::InlineAsm(operands).for_each(|place| state.kill(place.local)); } // Nothing to do for these. Match exhaustively so this fails to compile when new @@ -279,32 +279,32 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { | TerminatorKind::Unreachable => {} } - self.check_for_move(trans, loc); + self.check_for_move(state, loc); terminator.edges() } fn apply_call_return_effect( &mut self, - trans: &mut Self::Domain, + state: &mut Self::Domain, _block: BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, ) { - return_places.for_each(|place| trans.gen_(place.local)); + return_places.for_each(|place| state.gen_(place.local)); } } impl<'tcx> MaybeRequiresStorage<'_, 'tcx> { /// Kill locals that are fully moved and have not been borrowed. - fn check_for_move(&mut self, trans: &mut <Self as Analysis<'tcx>>::Domain, loc: Location) { + fn check_for_move(&mut self, state: &mut <Self as Analysis<'tcx>>::Domain, loc: Location) { let body = self.borrowed_locals.body(); - let mut visitor = MoveVisitor { trans, borrowed_locals: &mut self.borrowed_locals }; + let mut visitor = MoveVisitor { state, borrowed_locals: &mut self.borrowed_locals }; visitor.visit_location(body, loc); } } struct MoveVisitor<'a, 'mir, 'tcx> { borrowed_locals: &'a mut BorrowedLocalsResults<'mir, 'tcx>, - trans: &'a mut BitSet<Local>, + state: &'a mut BitSet<Local>, } impl<'tcx> Visitor<'tcx> for MoveVisitor<'_, '_, 'tcx> { @@ -312,7 +312,7 @@ impl<'tcx> Visitor<'tcx> for MoveVisitor<'_, '_, 'tcx> { if PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) == context { self.borrowed_locals.seek_before_primary_effect(loc); if !self.borrowed_locals.get().contains(local) { - self.trans.kill(local); + self.state.kill(local); } } } diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index 2248972cecc..85255db5d9a 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -18,7 +18,7 @@ pub use self::drop_flag_effects::{ move_path_children_matching, on_all_children_bits, on_lookup_result_bits, }; pub use self::framework::{ - Analysis, Backward, Direction, EntrySets, Forward, GenKill, JoinSemiLattice, MaybeReachable, + Analysis, Backward, Direction, EntryStates, Forward, GenKill, JoinSemiLattice, MaybeReachable, Results, ResultsCursor, ResultsVisitor, SwitchIntEdgeEffects, fmt, graphviz, lattice, visit_results, }; diff --git a/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs b/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs index df4c0593246..d79d2c316ee 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs @@ -4,52 +4,26 @@ //! field-deref on a local variable, `x.field`, has the same meaning //! in both domains). Indexed projections are the exception: `a[x]` //! needs to be treated as mapping to the same move path as `a[y]` as -//! well as `a[13]`, etc. +//! well as `a[13]`, etc. So we map these `x`/`y` values to `()`. //! //! (In theory, the analysis could be extended to work with sets of //! paths, so that `a[0]` and `a[13]` could be kept distinct, while //! `a[x]` would still overlap them both. But that is not this //! representation does today.) -use rustc_middle::mir::{Local, Operand, PlaceElem, ProjectionElem}; -use rustc_middle::ty::Ty; - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub(crate) struct AbstractOperand; -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub(crate) struct AbstractType; -pub(crate) type AbstractElem = ProjectionElem<AbstractOperand, AbstractType>; +use rustc_middle::mir::{PlaceElem, ProjectionElem, ProjectionKind}; pub(crate) trait Lift { - type Abstract; - fn lift(&self) -> Self::Abstract; -} -impl<'tcx> Lift for Operand<'tcx> { - type Abstract = AbstractOperand; - fn lift(&self) -> Self::Abstract { - AbstractOperand - } -} -impl Lift for Local { - type Abstract = AbstractOperand; - fn lift(&self) -> Self::Abstract { - AbstractOperand - } -} -impl<'tcx> Lift for Ty<'tcx> { - type Abstract = AbstractType; - fn lift(&self) -> Self::Abstract { - AbstractType - } + fn lift(&self) -> ProjectionKind; } + impl<'tcx> Lift for PlaceElem<'tcx> { - type Abstract = AbstractElem; - fn lift(&self) -> Self::Abstract { + fn lift(&self) -> ProjectionKind { match *self { ProjectionElem::Deref => ProjectionElem::Deref, - ProjectionElem::Field(f, ty) => ProjectionElem::Field(f, ty.lift()), - ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(ty.lift()), - ProjectionElem::Index(ref i) => ProjectionElem::Index(i.lift()), + ProjectionElem::Field(f, _ty) => ProjectionElem::Field(f, ()), + ProjectionElem::OpaqueCast(_ty) => ProjectionElem::OpaqueCast(()), + ProjectionElem::Index(_i) => ProjectionElem::Index(()), ProjectionElem::Subslice { from, to, from_end } => { ProjectionElem::Subslice { from, to, from_end } } @@ -57,7 +31,7 @@ impl<'tcx> Lift for PlaceElem<'tcx> { ProjectionElem::ConstantIndex { offset, min_length, from_end } } ProjectionElem::Downcast(a, u) => ProjectionElem::Downcast(a, u), - ProjectionElem::Subtype(ty) => ProjectionElem::Subtype(ty.lift()), + ProjectionElem::Subtype(_ty) => ProjectionElem::Subtype(()), } } } diff --git a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs index 926bd187431..8aea8d2ae3c 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs @@ -8,7 +8,7 @@ use rustc_middle::ty::{Ty, TyCtxt}; use rustc_span::Span; use smallvec::SmallVec; -use self::abs_domain::{AbstractElem, Lift}; +use self::abs_domain::Lift; use crate::un_derefer::UnDerefer; mod abs_domain; @@ -300,7 +300,7 @@ pub struct MovePathLookup<'tcx> { /// subsequent search so that it is solely relative to that /// base-place). For the remaining lookup, we map the projection /// elem to the associated MovePathIndex. - projections: FxHashMap<(MovePathIndex, AbstractElem), MovePathIndex>, + projections: FxHashMap<(MovePathIndex, ProjectionKind), MovePathIndex>, un_derefer: UnDerefer<'tcx>, } diff --git a/compiler/rustc_mir_dataflow/src/points.rs b/compiler/rustc_mir_dataflow/src/points.rs index 10f1e009855..74209da876a 100644 --- a/compiler/rustc_mir_dataflow/src/points.rs +++ b/compiler/rustc_mir_dataflow/src/points.rs @@ -125,7 +125,7 @@ where A: Analysis<'tcx, Domain = BitSet<N>>, N: Idx, { - fn visit_statement_after_primary_effect( + fn visit_after_primary_statement_effect( &mut self, _results: &mut Results<'tcx, A>, state: &A::Domain, @@ -139,7 +139,7 @@ where }); } - fn visit_terminator_after_primary_effect( + fn visit_after_primary_terminator_effect( &mut self, _results: &mut Results<'tcx, A>, state: &A::Domain, diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs index 34ef8afdde3..85cf8ca2104 100644 --- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs +++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs @@ -1,6 +1,5 @@ use rustc_ast::MetaItem; use rustc_hir::def_id::DefId; -use rustc_index::bit_set::BitSet; use rustc_middle::mir::{self, Body, Local, Location}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Span; @@ -254,7 +253,7 @@ impl<'tcx> RustcPeekAt<'tcx> for MaybeLiveLocals { &self, tcx: TyCtxt<'tcx>, place: mir::Place<'tcx>, - state: &BitSet<Local>, + state: &Self::Domain, call: PeekCall, ) { info!(?place, "peek_at"); diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index ed8678de1eb..9328870c7ae 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -67,7 +67,7 @@ impl<V: Clone> Clone for StateData<V> { } } -impl<V: JoinSemiLattice + Clone + HasBottom> JoinSemiLattice for StateData<V> { +impl<V: JoinSemiLattice + Clone> JoinSemiLattice for StateData<V> { fn join(&mut self, other: &Self) -> bool { let mut changed = false; #[allow(rustc::potential_query_instability)] @@ -342,7 +342,7 @@ impl<V: Clone + HasBottom> State<V> { } } -impl<V: JoinSemiLattice + Clone + HasBottom> JoinSemiLattice for State<V> { +impl<V: JoinSemiLattice + Clone> JoinSemiLattice for State<V> { fn join(&mut self, other: &Self) -> bool { match (&mut *self, other) { (_, State::Unreachable) => false, 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 858752a3f01..31d5245fb5c 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -878,7 +878,7 @@ struct StorageConflictVisitor<'a, 'tcx> { impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, MaybeRequiresStorage<'a, 'tcx>> for StorageConflictVisitor<'a, 'tcx> { - fn visit_statement_before_primary_effect( + fn visit_after_early_statement_effect( &mut self, _results: &mut Results<'tcx, MaybeRequiresStorage<'a, 'tcx>>, state: &BitSet<Local>, @@ -888,7 +888,7 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, MaybeRequiresStorage<'a, 'tcx>> self.apply_state(state, loc); } - fn visit_terminator_before_primary_effect( + fn visit_after_early_terminator_effect( &mut self, _results: &mut Results<'tcx, MaybeRequiresStorage<'a, 'tcx>>, state: &BitSet<Local>, 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 36eb435c63a..ef61866e902 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -3,8 +3,6 @@ //! //! Consider an async closure like: //! ```rust -//! #![feature(async_closure)] -//! //! let x = vec![1, 2, 3]; //! //! let closure = async move || { diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs index 46efdd16ee8..9e80f1f1c4a 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters.rs @@ -9,7 +9,7 @@ use rustc_index::bit_set::BitSet; use rustc_middle::mir::coverage::{CounterId, CovTerm, Expression, ExpressionId, Op}; use tracing::{debug, debug_span, instrument}; -use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, TraverseCoverageGraphWithLoops}; +use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, ReadyFirstTraversal}; #[cfg(test)] mod tests; @@ -236,23 +236,12 @@ impl<'a> CountersBuilder<'a> { // Traverse the coverage graph, ensuring that every node that needs a // coverage counter has one. - // - // The traversal tries to ensure that, when a loop is encountered, all - // nodes within the loop are visited before visiting any nodes outside - // the loop. - let mut traversal = TraverseCoverageGraphWithLoops::new(self.graph); - while let Some(bcb) = traversal.next() { + for bcb in ReadyFirstTraversal::new(self.graph) { let _span = debug_span!("traversal", ?bcb).entered(); if self.bcb_needs_counter.contains(bcb) { self.make_node_counter_and_out_edge_counters(bcb); } } - - assert!( - traversal.is_complete(), - "`TraverseCoverageGraphWithLoops` missed some `BasicCoverageBlock`s: {:?}", - traversal.unvisited(), - ); } /// Make sure the given node has a node counter, and then make sure each of diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs index 092bce1de2c..ad6774fccd6 100644 --- a/compiler/rustc_mir_transform/src/coverage/graph.rs +++ b/compiler/rustc_mir_transform/src/coverage/graph.rs @@ -9,7 +9,6 @@ use rustc_data_structures::graph::dominators::Dominators; use rustc_data_structures::graph::{self, DirectedGraph, StartNode}; use rustc_index::IndexVec; use rustc_index::bit_set::BitSet; -use rustc_middle::bug; use rustc_middle::mir::{self, BasicBlock, Terminator, TerminatorKind}; use tracing::debug; @@ -462,138 +461,6 @@ fn bcb_filtered_successors<'a, 'tcx>(terminator: &'a Terminator<'tcx>) -> Covera CoverageSuccessors { targets, is_yield } } -/// Maintains separate worklists for each loop in the BasicCoverageBlock CFG, plus one for the -/// CoverageGraph outside all loops. This supports traversing the BCB CFG in a way that -/// ensures a loop is completely traversed before processing Blocks after the end of the loop. -#[derive(Debug)] -struct TraversalContext { - /// BCB with one or more incoming loop backedges, indicating which loop - /// this context is for. - /// - /// If `None`, this is the non-loop context for the function as a whole. - loop_header: Option<BasicCoverageBlock>, - - /// Worklist of BCBs to be processed in this context. - worklist: VecDeque<BasicCoverageBlock>, -} - -pub(crate) struct TraverseCoverageGraphWithLoops<'a> { - basic_coverage_blocks: &'a CoverageGraph, - - context_stack: Vec<TraversalContext>, - visited: BitSet<BasicCoverageBlock>, -} - -impl<'a> TraverseCoverageGraphWithLoops<'a> { - pub(crate) fn new(basic_coverage_blocks: &'a CoverageGraph) -> Self { - let worklist = VecDeque::from([basic_coverage_blocks.start_node()]); - let context_stack = vec![TraversalContext { loop_header: None, worklist }]; - - // `context_stack` starts with a `TraversalContext` for the main function context (beginning - // with the `start` BasicCoverageBlock of the function). New worklists are pushed to the top - // of the stack as loops are entered, and popped off of the stack when a loop's worklist is - // exhausted. - let visited = BitSet::new_empty(basic_coverage_blocks.num_nodes()); - Self { basic_coverage_blocks, context_stack, visited } - } - - pub(crate) fn next(&mut self) -> Option<BasicCoverageBlock> { - debug!( - "TraverseCoverageGraphWithLoops::next - context_stack: {:?}", - self.context_stack.iter().rev().collect::<Vec<_>>() - ); - - while let Some(context) = self.context_stack.last_mut() { - let Some(bcb) = context.worklist.pop_front() else { - // This stack level is exhausted; pop it and try the next one. - self.context_stack.pop(); - continue; - }; - - if !self.visited.insert(bcb) { - debug!("Already visited: {bcb:?}"); - continue; - } - debug!("Visiting {bcb:?}"); - - if self.basic_coverage_blocks.is_loop_header.contains(bcb) { - debug!("{bcb:?} is a loop header! Start a new TraversalContext..."); - self.context_stack - .push(TraversalContext { loop_header: Some(bcb), worklist: VecDeque::new() }); - } - self.add_successors_to_worklists(bcb); - return Some(bcb); - } - - None - } - - fn add_successors_to_worklists(&mut self, bcb: BasicCoverageBlock) { - let successors = &self.basic_coverage_blocks.successors[bcb]; - debug!("{:?} has {} successors:", bcb, successors.len()); - - for &successor in successors { - if successor == bcb { - debug!( - "{:?} has itself as its own successor. (Note, the compiled code will \ - generate an infinite loop.)", - bcb - ); - // Don't re-add this successor to the worklist. We are already processing it. - // FIXME: This claims to skip just the self-successor, but it actually skips - // all other successors as well. Does that matter? - break; - } - - // Add successors of the current BCB to the appropriate context. Successors that - // stay within a loop are added to the BCBs context worklist. Successors that - // exit the loop (they are not dominated by the loop header) must be reachable - // from other BCBs outside the loop, and they will be added to a different - // worklist. - // - // Branching blocks (with more than one successor) must be processed before - // blocks with only one successor, to prevent unnecessarily complicating - // `Expression`s by creating a Counter in a `BasicCoverageBlock` that the - // branching block would have given an `Expression` (or vice versa). - - let context = self - .context_stack - .iter_mut() - .rev() - .find(|context| match context.loop_header { - Some(loop_header) => { - self.basic_coverage_blocks.dominates(loop_header, successor) - } - None => true, - }) - .unwrap_or_else(|| bug!("should always fall back to the root non-loop context")); - debug!("adding to worklist for {:?}", context.loop_header); - - // FIXME: The code below had debug messages claiming to add items to a - // particular end of the worklist, but was confused about which end was - // which. The existing behaviour has been preserved for now, but it's - // unclear what the intended behaviour was. - - if self.basic_coverage_blocks.successors[successor].len() > 1 { - context.worklist.push_back(successor); - } else { - context.worklist.push_front(successor); - } - } - } - - pub(crate) fn is_complete(&self) -> bool { - self.visited.count() == self.visited.domain_size() - } - - pub(crate) fn unvisited(&self) -> Vec<BasicCoverageBlock> { - let mut unvisited_set: BitSet<BasicCoverageBlock> = - BitSet::new_filled(self.visited.domain_size()); - unvisited_set.subtract(&self.visited); - unvisited_set.iter().collect::<Vec<_>>() - } -} - /// Wrapper around a [`mir::BasicBlocks`] graph that restricts each node's /// successors to only the ones considered "relevant" when building a coverage /// graph. @@ -622,3 +489,126 @@ impl<'a, 'tcx> graph::Successors for CoverageRelevantSubgraph<'a, 'tcx> { self.coverage_successors(bb).into_iter() } } + +/// State of a node in the coverage graph during ready-first traversal. +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +enum ReadyState { + /// This node has not yet been added to the fallback queue or ready queue. + Unqueued, + /// This node is currently in the fallback queue. + InFallbackQueue, + /// This node's predecessors have all been visited, so it is in the ready queue. + /// (It might also have a stale entry in the fallback queue.) + InReadyQueue, + /// This node has been visited. + /// (It might also have a stale entry in the fallback queue.) + Visited, +} + +/// Iterator that visits nodes in the coverage graph, in an order that always +/// prefers "ready" nodes whose predecessors have already been visited. +pub(crate) struct ReadyFirstTraversal<'a> { + graph: &'a CoverageGraph, + + /// For each node, the number of its predecessor nodes that haven't been visited yet. + n_unvisited_preds: IndexVec<BasicCoverageBlock, u32>, + /// Indicates whether a node has been visited, or which queue it is in. + state: IndexVec<BasicCoverageBlock, ReadyState>, + + /// Holds unvisited nodes whose predecessors have all been visited. + ready_queue: VecDeque<BasicCoverageBlock>, + /// Holds unvisited nodes with some unvisited predecessors. + /// Also contains stale entries for nodes that were upgraded to ready. + fallback_queue: VecDeque<BasicCoverageBlock>, +} + +impl<'a> ReadyFirstTraversal<'a> { + pub(crate) fn new(graph: &'a CoverageGraph) -> Self { + let num_nodes = graph.num_nodes(); + + let n_unvisited_preds = + IndexVec::from_fn_n(|node| graph.predecessors[node].len() as u32, num_nodes); + let mut state = IndexVec::from_elem_n(ReadyState::Unqueued, num_nodes); + + // We know from coverage graph construction that the start node is the + // only node with no predecessors. + debug_assert!( + n_unvisited_preds.iter_enumerated().all(|(node, &n)| (node == START_BCB) == (n == 0)) + ); + let ready_queue = VecDeque::from(vec![START_BCB]); + state[START_BCB] = ReadyState::InReadyQueue; + + Self { graph, state, n_unvisited_preds, ready_queue, fallback_queue: VecDeque::new() } + } + + /// Returns the next node from the ready queue, or else the next unvisited + /// node from the fallback queue. + fn next_inner(&mut self) -> Option<BasicCoverageBlock> { + // Always prefer to yield a ready node if possible. + if let Some(node) = self.ready_queue.pop_front() { + assert_eq!(self.state[node], ReadyState::InReadyQueue); + return Some(node); + } + + while let Some(node) = self.fallback_queue.pop_front() { + match self.state[node] { + // This entry in the fallback queue is not stale, so yield it. + ReadyState::InFallbackQueue => return Some(node), + // This node was added to the fallback queue, but later became + // ready and was visited via the ready queue. Ignore it here. + ReadyState::Visited => {} + // Unqueued nodes can't be in the fallback queue, by definition. + // We know that the ready queue is empty at this point. + ReadyState::Unqueued | ReadyState::InReadyQueue => unreachable!( + "unexpected state for {node:?} in the fallback queue: {:?}", + self.state[node] + ), + } + } + + None + } + + fn mark_visited_and_enqueue_successors(&mut self, node: BasicCoverageBlock) { + assert!(self.state[node] < ReadyState::Visited); + self.state[node] = ReadyState::Visited; + + // For each of this node's successors, decrease the successor's + // "unvisited predecessors" count, and enqueue it if appropriate. + for &succ in &self.graph.successors[node] { + let is_unqueued = match self.state[succ] { + ReadyState::Unqueued => true, + ReadyState::InFallbackQueue => false, + ReadyState::InReadyQueue => { + unreachable!("nodes in the ready queue have no unvisited predecessors") + } + // The successor was already visited via one of its other predecessors. + ReadyState::Visited => continue, + }; + + self.n_unvisited_preds[succ] -= 1; + if self.n_unvisited_preds[succ] == 0 { + // This node's predecessors have all been visited, so add it to + // the ready queue. If it's already in the fallback queue, that + // fallback entry will be ignored later. + self.state[succ] = ReadyState::InReadyQueue; + self.ready_queue.push_back(succ); + } else if is_unqueued { + // This node has unvisited predecessors, so add it to the + // fallback queue in case we run out of ready nodes later. + self.state[succ] = ReadyState::InFallbackQueue; + self.fallback_queue.push_back(succ); + } + } + } +} + +impl<'a> Iterator for ReadyFirstTraversal<'a> { + type Item = BasicCoverageBlock; + + fn next(&mut self) -> Option<Self::Item> { + let node = self.next_inner()?; + self.mark_visited_and_enqueue_successors(node); + Some(node) + } +} diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index 0090f6f3040..edaec3c7965 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -1,8 +1,11 @@ use rustc_data_structures::captures::Captures; use rustc_index::bit_set::BitSet; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use rustc_middle::mir::coverage::{CovTerm, CoverageKind, MappingKind}; -use rustc_middle::mir::{Body, CoverageIdsInfo, Statement, StatementKind}; +use rustc_middle::mir::coverage::{ + CounterId, CovTerm, CoverageIdsInfo, CoverageKind, Expression, ExpressionId, + FunctionCoverageInfo, MappingKind, Op, +}; +use rustc_middle::mir::{Body, Statement, StatementKind}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::util::Providers; @@ -87,10 +90,10 @@ fn coverage_ids_info<'tcx>( ) -> CoverageIdsInfo { let mir_body = tcx.instance_mir(instance_def); - let Some(fn_cov_info) = mir_body.function_coverage_info.as_ref() else { + let Some(fn_cov_info) = mir_body.function_coverage_info.as_deref() else { return CoverageIdsInfo { counters_seen: BitSet::new_empty(0), - expressions_seen: BitSet::new_empty(0), + zero_expressions: BitSet::new_empty(0), }; }; @@ -123,7 +126,10 @@ fn coverage_ids_info<'tcx>( } } - CoverageIdsInfo { counters_seen, expressions_seen } + let zero_expressions = + identify_zero_expressions(fn_cov_info, &counters_seen, &expressions_seen); + + CoverageIdsInfo { counters_seen, zero_expressions } } fn all_coverage_in_mir_body<'a, 'tcx>( @@ -141,3 +147,94 @@ fn is_inlined(body: &Body<'_>, statement: &Statement<'_>) -> bool { let scope_data = &body.source_scopes[statement.source_info.scope]; scope_data.inlined.is_some() || scope_data.inlined_parent_scope.is_some() } + +/// Identify expressions that will always have a value of zero, and note +/// their IDs in a `BitSet`. Mappings that refer to a zero expression +/// can instead become mappings to a constant zero value. +/// +/// This function mainly exists to preserve the simplifications that were +/// already being performed by the Rust-side expression renumbering, so that +/// the resulting coverage mappings don't get worse. +fn identify_zero_expressions( + fn_cov_info: &FunctionCoverageInfo, + counters_seen: &BitSet<CounterId>, + expressions_seen: &BitSet<ExpressionId>, +) -> BitSet<ExpressionId> { + // The set of expressions that either were optimized out entirely, or + // have zero as both of their operands, and will therefore always have + // a value of zero. Other expressions that refer to these as operands + // can have those operands replaced with `CovTerm::Zero`. + let mut zero_expressions = BitSet::new_empty(fn_cov_info.expressions.len()); + + // Simplify a copy of each expression based on lower-numbered expressions, + // and then update the set of always-zero expressions if necessary. + // (By construction, expressions can only refer to other expressions + // that have lower IDs, so one pass is sufficient.) + for (id, expression) in fn_cov_info.expressions.iter_enumerated() { + if !expressions_seen.contains(id) { + // If an expression was not seen, it must have been optimized away, + // so any operand that refers to it can be replaced with zero. + zero_expressions.insert(id); + continue; + } + + // We don't need to simplify the actual expression data in the + // expressions list; we can just simplify a temporary copy and then + // use that to update the set of always-zero expressions. + let Expression { mut lhs, op, mut rhs } = *expression; + + // If an expression has an operand that is also an expression, the + // operand's ID must be strictly lower. This is what lets us find + // all zero expressions in one pass. + let assert_operand_expression_is_lower = |operand_id: ExpressionId| { + assert!( + operand_id < id, + "Operand {operand_id:?} should be less than {id:?} in {expression:?}", + ) + }; + + // If an operand refers to a counter or expression that is always + // zero, then that operand can be replaced with `CovTerm::Zero`. + let maybe_set_operand_to_zero = |operand: &mut CovTerm| { + if let CovTerm::Expression(id) = *operand { + assert_operand_expression_is_lower(id); + } + + if is_zero_term(&counters_seen, &zero_expressions, *operand) { + *operand = CovTerm::Zero; + } + }; + maybe_set_operand_to_zero(&mut lhs); + maybe_set_operand_to_zero(&mut rhs); + + // Coverage counter values cannot be negative, so if an expression + // involves subtraction from zero, assume that its RHS must also be zero. + // (Do this after simplifications that could set the LHS to zero.) + if lhs == CovTerm::Zero && op == Op::Subtract { + rhs = CovTerm::Zero; + } + + // After the above simplifications, if both operands are zero, then + // we know that this expression is always zero too. + if lhs == CovTerm::Zero && rhs == CovTerm::Zero { + zero_expressions.insert(id); + } + } + + zero_expressions +} + +/// Returns `true` if the given term is known to have a value of zero, taking +/// into account knowledge of which counters are unused and which expressions +/// are always zero. +fn is_zero_term( + counters_seen: &BitSet<CounterId>, + zero_expressions: &BitSet<ExpressionId>, + term: CovTerm, +) -> bool { + match term { + CovTerm::Zero => true, + CovTerm::Counter(id) => !counters_seen.contains(id), + CovTerm::Expression(id) => zero_expressions.contains(id), + } +} 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/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index d017202f48b..711cf2edc46 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -106,7 +106,7 @@ impl<'tcx> Analysis<'tcx> for ConstAnalysis<'_, 'tcx> { } } - fn apply_statement_effect( + fn apply_primary_statement_effect( &mut self, state: &mut Self::Domain, statement: &Statement<'tcx>, @@ -117,7 +117,7 @@ impl<'tcx> Analysis<'tcx> for ConstAnalysis<'_, 'tcx> { } } - fn apply_terminator_effect<'mir>( + fn apply_primary_terminator_effect<'mir>( &mut self, state: &mut Self::Domain, terminator: &'mir Terminator<'tcx>, @@ -224,7 +224,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { } /// The effect of a successful function call return should not be - /// applied here, see [`Analysis::apply_terminator_effect`]. + /// applied here, see [`Analysis::apply_primary_terminator_effect`]. fn handle_terminator<'mir>( &self, terminator: &'mir Terminator<'tcx>, @@ -534,8 +534,13 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { // This allows the set of visited edges to grow monotonically with the lattice. FlatSet::Bottom => TerminatorEdges::None, FlatSet::Elem(scalar) => { - let choice = scalar.assert_scalar_int().to_bits_unchecked(); - TerminatorEdges::Single(targets.target_for_value(choice)) + if let Ok(scalar_int) = scalar.try_to_scalar_int() { + TerminatorEdges::Single( + targets.target_for_value(scalar_int.to_bits_unchecked()), + ) + } else { + TerminatorEdges::SwitchInt { discr, targets } + } } FlatSet::Top => TerminatorEdges::SwitchInt { discr, targets }, } @@ -949,7 +954,7 @@ fn try_write_constant<'tcx>( impl<'mir, 'tcx> ResultsVisitor<'mir, 'tcx, ConstAnalysis<'_, 'tcx>> for Collector<'_, 'tcx> { #[instrument(level = "trace", skip(self, results, statement))] - fn visit_statement_before_primary_effect( + fn visit_after_early_statement_effect( &mut self, results: &mut Results<'tcx, ConstAnalysis<'_, 'tcx>>, state: &State<FlatSet<Scalar>>, @@ -971,7 +976,7 @@ impl<'mir, 'tcx> ResultsVisitor<'mir, 'tcx, ConstAnalysis<'_, 'tcx>> for Collect } #[instrument(level = "trace", skip(self, results, statement))] - fn visit_statement_after_primary_effect( + fn visit_after_primary_statement_effect( &mut self, results: &mut Results<'tcx, ConstAnalysis<'_, 'tcx>>, state: &State<FlatSet<Scalar>>, @@ -996,7 +1001,7 @@ impl<'mir, 'tcx> ResultsVisitor<'mir, 'tcx, ConstAnalysis<'_, 'tcx>> for Collect } } - fn visit_terminator_before_primary_effect( + fn visit_after_early_terminator_effect( &mut self, results: &mut Results<'tcx, ConstAnalysis<'_, 'tcx>>, state: &State<FlatSet<Scalar>>, diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs index 0c0f3b61977..3ebc9113725 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drops.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs @@ -127,7 +127,7 @@ impl InitializationData<'_, '_> { self.uninits.seek_before_primary_effect(loc); } - fn maybe_live_dead(&self, path: MovePathIndex) -> (bool, bool) { + fn maybe_init_uninit(&self, path: MovePathIndex) -> (bool, bool) { (self.inits.get().contains(path), self.uninits.get().contains(path)) } } @@ -153,23 +153,23 @@ impl<'a, 'tcx> DropElaborator<'a, 'tcx> for ElaborateDropsCtxt<'a, 'tcx> { #[instrument(level = "debug", skip(self), ret)] fn drop_style(&self, path: Self::Path, mode: DropFlagMode) -> DropStyle { - let ((maybe_live, maybe_dead), multipart) = match mode { - DropFlagMode::Shallow => (self.init_data.maybe_live_dead(path), false), + let ((maybe_init, maybe_uninit), multipart) = match mode { + DropFlagMode::Shallow => (self.init_data.maybe_init_uninit(path), false), DropFlagMode::Deep => { - let mut some_live = false; - let mut some_dead = false; + let mut some_maybe_init = false; + let mut some_maybe_uninit = false; let mut children_count = 0; on_all_children_bits(self.move_data(), path, |child| { - let (live, dead) = self.init_data.maybe_live_dead(child); - debug!("elaborate_drop: state({:?}) = {:?}", child, (live, dead)); - some_live |= live; - some_dead |= dead; + let (maybe_init, maybe_uninit) = self.init_data.maybe_init_uninit(child); + debug!("elaborate_drop: state({:?}) = {:?}", child, (maybe_init, maybe_uninit)); + some_maybe_init |= maybe_init; + some_maybe_uninit |= maybe_uninit; children_count += 1; }); - ((some_live, some_dead), children_count != 1) + ((some_maybe_init, some_maybe_uninit), children_count != 1) } }; - match (maybe_live, maybe_dead, multipart) { + match (maybe_init, maybe_uninit, multipart) { (false, _, _) => DropStyle::Dead, (true, false, _) => DropStyle::Static, (true, true, false) => DropStyle::Conditional, @@ -283,15 +283,15 @@ impl<'a, 'tcx> ElaborateDropsCtxt<'a, 'tcx> { LookupResult::Exact(path) => { self.init_data.seek_before(self.body.terminator_loc(bb)); on_all_children_bits(self.move_data(), path, |child| { - let (maybe_live, maybe_dead) = self.init_data.maybe_live_dead(child); + let (maybe_init, maybe_uninit) = self.init_data.maybe_init_uninit(child); debug!( "collect_drop_flags: collecting {:?} from {:?}@{:?} - {:?}", child, place, path, - (maybe_live, maybe_dead) + (maybe_init, maybe_uninit) ); - if maybe_live && maybe_dead { + if maybe_init && maybe_uninit { self.create_drop_flag(child, terminator.source_info.span) } }); @@ -303,8 +303,8 @@ impl<'a, 'tcx> ElaborateDropsCtxt<'a, 'tcx> { } self.init_data.seek_before(self.body.terminator_loc(bb)); - let (_maybe_live, maybe_dead) = self.init_data.maybe_live_dead(parent); - if maybe_dead { + let (_maybe_init, maybe_uninit) = self.init_data.maybe_init_uninit(parent); + if maybe_uninit { self.tcx.dcx().span_delayed_bug( terminator.source_info.span, format!( diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 79c38b50459..f0acbaf56b6 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; diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index a6ba2f32d32..adc3374df2e 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -47,7 +47,6 @@ impl<'tcx> crate::MirPass<'tcx> for InstSimplify { } ctx.simplify_bool_cmp(rvalue); ctx.simplify_ref_deref(rvalue); - ctx.simplify_len(rvalue); ctx.simplify_ptr_aggregate(rvalue); ctx.simplify_cast(rvalue); } @@ -132,18 +131,6 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> { } } - /// Transform `Len([_; N])` ==> `N`. - fn simplify_len(&self, rvalue: &mut Rvalue<'tcx>) { - if let Rvalue::Len(ref place) = *rvalue { - let place_ty = place.ty(self.local_decls, self.tcx).ty; - if let ty::Array(_, len) = *place_ty.kind() { - let const_ = Const::from_ty_const(len, self.tcx.types.usize, self.tcx); - let constant = ConstOperand { span: DUMMY_SP, const_, user_ty: None }; - *rvalue = Rvalue::Use(Operand::Constant(Box::new(constant))); - } - } - } - /// Transform `Aggregate(RawPtr, [p, ()])` ==> `Cast(PtrToPtr, p)`. fn simplify_ptr_aggregate(&self, rvalue: &mut Rvalue<'tcx>) { if let Rvalue::Aggregate(box AggregateKind::RawPtr(pointee_ty, mutability), fields) = rvalue diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index f0fcb44603b..5c090bf7cad 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -189,6 +189,7 @@ declare_passes! { mod simplify_comparison_integral : SimplifyComparisonIntegral; mod single_use_consts : SingleUseConsts; mod sroa : ScalarReplacementOfAggregates; + mod strip_debuginfo : StripDebugInfo; mod unreachable_enum_branching : UnreachableEnumBranching; mod unreachable_prop : UnreachablePropagation; mod validate : Validator; @@ -699,6 +700,8 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &o1(simplify_branches::SimplifyConstCondition::Final), &o1(remove_noop_landing_pads::RemoveNoopLandingPads), &o1(simplify::SimplifyCfg::Final), + // After the last SimplifyCfg, because this wants one-block functions. + &strip_debuginfo::StripDebugInfo, ©_prop::CopyProp, &dead_store_elimination::DeadStoreElimination::Final, &nrvo::RenameReturnPlace, diff --git a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs index 732a9cd9890..7fb421dea0c 100644 --- a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs +++ b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs @@ -7,7 +7,7 @@ use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::Subdiagnostic; use rustc_hir::CRATE_HIR_ID; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_index::bit_set::ChunkedBitSet; +use rustc_index::bit_set::MixedBitSet; use rustc_index::{IndexSlice, IndexVec}; use rustc_macros::{LintDiagnostic, Subdiagnostic}; use rustc_middle::bug; @@ -49,24 +49,24 @@ struct DropsReachable<'a, 'mir, 'tcx> { move_data: &'a MoveData<'tcx>, maybe_init: &'a mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>, block_drop_value_info: &'a mut IndexSlice<BasicBlock, MovePathIndexAtBlock>, - collected_drops: &'a mut ChunkedBitSet<MovePathIndex>, - visited: FxHashMap<BasicBlock, Rc<RefCell<ChunkedBitSet<MovePathIndex>>>>, + collected_drops: &'a mut MixedBitSet<MovePathIndex>, + visited: FxHashMap<BasicBlock, Rc<RefCell<MixedBitSet<MovePathIndex>>>>, } impl<'a, 'mir, 'tcx> DropsReachable<'a, 'mir, 'tcx> { fn visit(&mut self, block: BasicBlock) { let move_set_size = self.move_data.move_paths.len(); - let make_new_path_set = || Rc::new(RefCell::new(ChunkedBitSet::new_empty(move_set_size))); + let make_new_path_set = || Rc::new(RefCell::new(MixedBitSet::new_empty(move_set_size))); let data = &self.body.basic_blocks[block]; let Some(terminator) = &data.terminator else { return }; - // Given that we observe these dropped locals here at `block` so far, - // we will try to update the successor blocks. - // An occupied entry at `block` in `self.visited` signals that we have visited `block` before. + // Given that we observe these dropped locals here at `block` so far, we will try to update + // the successor blocks. An occupied entry at `block` in `self.visited` signals that we + // have visited `block` before. let dropped_local_here = Rc::clone(self.visited.entry(block).or_insert_with(make_new_path_set)); - // We could have invoked reverse lookup for a `MovePathIndex` every time, but unfortunately it is expensive. - // Let's cache them in `self.block_drop_value_info`. + // We could have invoked reverse lookup for a `MovePathIndex` every time, but unfortunately + // it is expensive. Let's cache them in `self.block_drop_value_info`. match self.block_drop_value_info[block] { MovePathIndexAtBlock::Some(dropped) => { dropped_local_here.borrow_mut().insert(dropped); @@ -76,23 +76,24 @@ impl<'a, 'mir, 'tcx> DropsReachable<'a, 'mir, 'tcx> { && let LookupResult::Exact(idx) | LookupResult::Parent(Some(idx)) = self.move_data.rev_lookup.find(place.as_ref()) { - // Since we are working with MIRs at a very early stage, - // observing a `drop` terminator is not indicative enough that - // the drop will definitely happen. - // That is decided in the drop elaboration pass instead. - // Therefore, we need to consult with the maybe-initialization information. + // Since we are working with MIRs at a very early stage, observing a `drop` + // terminator is not indicative enough that the drop will definitely happen. + // That is decided in the drop elaboration pass instead. Therefore, we need to + // consult with the maybe-initialization information. self.maybe_init.seek_before_primary_effect(Location { block, statement_index: data.statements.len(), }); - // Check if the drop of `place` under inspection is really in effect. - // This is true only when `place` may have been initialized along a control flow path from a BID to the drop program point today. - // In other words, this is where the drop of `place` will happen in the future instead. + // Check if the drop of `place` under inspection is really in effect. This is + // true only when `place` may have been initialized along a control flow path + // from a BID to the drop program point today. In other words, this is where + // the drop of `place` will happen in the future instead. if let MaybeReachable::Reachable(maybe_init) = self.maybe_init.get() && maybe_init.contains(idx) { - // We also cache the drop information, so that we do not need to check on data-flow cursor again + // We also cache the drop information, so that we do not need to check on + // data-flow cursor again. self.block_drop_value_info[block] = MovePathIndexAtBlock::Some(idx); dropped_local_here.borrow_mut().insert(idx); } else { @@ -139,8 +140,9 @@ impl<'a, 'mir, 'tcx> DropsReachable<'a, 'mir, 'tcx> { // Let's check the observed dropped places in. self.collected_drops.union(&*dropped_local_there.borrow()); if self.drop_span.is_none() { - // FIXME(@dingxiangfei2009): it turns out that `self.body.source_scopes` are still a bit wonky. - // There is a high chance that this span still points to a block rather than a statement semicolon. + // FIXME(@dingxiangfei2009): it turns out that `self.body.source_scopes` are + // still a bit wonky. There is a high chance that this span still points to a + // block rather than a statement semicolon. *self.drop_span = Some(terminator.source_info.span); } // Now we have discovered a simple control flow path from a future drop point @@ -394,10 +396,10 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body< for (&block, candidates) in &bid_per_block { // We will collect drops on locals on paths between BID points to their actual drop locations // into `all_locals_dropped`. - let mut all_locals_dropped = ChunkedBitSet::new_empty(move_data.move_paths.len()); + let mut all_locals_dropped = MixedBitSet::new_empty(move_data.move_paths.len()); let mut drop_span = None; for &(_, place) in candidates.iter() { - let mut collected_drops = ChunkedBitSet::new_empty(move_data.move_paths.len()); + let mut collected_drops = MixedBitSet::new_empty(move_data.move_paths.len()); // ## On detecting change in relative drop order ## // Iterate through each BID-containing block `block`. // If the place `P` targeted by the BID is "maybe initialized", @@ -425,8 +427,9 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body< // We shall now exclude some local bindings for the following cases. { - let mut to_exclude = ChunkedBitSet::new_empty(all_locals_dropped.domain_size()); - // We will now do subtraction from the candidate dropped locals, because of the following reasons. + let mut to_exclude = MixedBitSet::new_empty(all_locals_dropped.domain_size()); + // We will now do subtraction from the candidate dropped locals, because of the + // following reasons. for path_idx in all_locals_dropped.iter() { let move_path = &move_data.move_paths[path_idx]; let dropped_local = move_path.place.local; diff --git a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs index f786c676e9e..e955d8277a4 100644 --- a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs +++ b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs @@ -1,5 +1,5 @@ use rustc_abi::FieldIdx; -use rustc_index::bit_set::ChunkedBitSet; +use rustc_index::bit_set::MixedBitSet; use rustc_middle::mir::{Body, TerminatorKind}; use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, VariantDef}; use rustc_mir_dataflow::impls::MaybeInitializedPlaces; @@ -67,7 +67,7 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveUninitDrops { fn is_needs_drop_and_init<'tcx>( tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, - maybe_inits: &ChunkedBitSet<MovePathIndex>, + maybe_inits: &MixedBitSet<MovePathIndex>, move_data: &MoveData<'tcx>, ty: Ty<'tcx>, mpi: MovePathIndex, diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index b8383e734e2..722da3c420d 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -9,6 +9,7 @@ use rustc_index::{Idx, IndexVec}; use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::*; use rustc_middle::query::Providers; +use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::{ self, CoroutineArgs, CoroutineArgsExt, EarlyBinder, GenericArgs, Ty, TyCtxt, }; @@ -710,6 +711,13 @@ fn build_call_shim<'tcx>( }; let def_id = instance.def_id(); + + let rpitit_shim = if let ty::InstanceKind::ReifyShim(..) = instance { + tcx.return_position_impl_trait_in_trait_shim_data(def_id) + } else { + None + }; + let sig = tcx.fn_sig(def_id); let sig = sig.map_bound(|sig| tcx.instantiate_bound_regions_with_erased(sig)); @@ -765,9 +773,34 @@ fn build_call_shim<'tcx>( let mut local_decls = local_decls_for_sig(&sig, span); let source_info = SourceInfo::outermost(span); + let mut destination = Place::return_place(); + if let Some((rpitit_def_id, fn_args)) = rpitit_shim { + let rpitit_args = + fn_args.instantiate_identity().extend_to(tcx, rpitit_def_id, |param, _| { + match param.kind { + ty::GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), + ty::GenericParamDefKind::Type { .. } + | ty::GenericParamDefKind::Const { .. } => { + unreachable!("rpitit should have no addition ty/ct") + } + } + }); + let dyn_star_ty = Ty::new_dynamic( + tcx, + tcx.item_bounds_to_existential_predicates(rpitit_def_id, rpitit_args), + tcx.lifetimes.re_erased, + ty::DynStar, + ); + destination = local_decls.push(local_decls[RETURN_PLACE].clone()).into(); + local_decls[RETURN_PLACE].ty = dyn_star_ty; + let mut inputs_and_output = sig.inputs_and_output.to_vec(); + *inputs_and_output.last_mut().unwrap() = dyn_star_ty; + sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output); + } + let rcvr_place = || { assert!(rcvr_adjustment.is_some()); - Place::from(Local::new(1 + 0)) + Place::from(Local::new(1)) }; let mut statements = vec![]; @@ -854,7 +887,7 @@ fn build_call_shim<'tcx>( TerminatorKind::Call { func: callee, args, - destination: Place::return_place(), + destination, target: Some(BasicBlock::new(1)), unwind: if let Some(Adjustment::RefMut) = rcvr_adjustment { UnwindAction::Cleanup(BasicBlock::new(3)) @@ -882,7 +915,24 @@ fn build_call_shim<'tcx>( ); } // BB #1/#2 - return - block(&mut blocks, vec![], TerminatorKind::Return, false); + // NOTE: If this is an RPITIT in dyn, we also want to coerce + // the return type of the function into a `dyn*`. + let stmts = if rpitit_shim.is_some() { + vec![Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + Place::return_place(), + Rvalue::Cast( + CastKind::PointerCoercion(PointerCoercion::DynStar, CoercionSource::Implicit), + Operand::Move(destination), + sig.output(), + ), + ))), + }] + } else { + vec![] + }; + block(&mut blocks, stmts, TerminatorKind::Return, false); if let Some(Adjustment::RefMut) = rcvr_adjustment { // BB #3 - drop if closure panics block( diff --git a/compiler/rustc_mir_transform/src/strip_debuginfo.rs b/compiler/rustc_mir_transform/src/strip_debuginfo.rs new file mode 100644 index 00000000000..438c75726bb --- /dev/null +++ b/compiler/rustc_mir_transform/src/strip_debuginfo.rs @@ -0,0 +1,34 @@ +use rustc_middle::mir::*; +use rustc_middle::ty::TyCtxt; +use rustc_session::config::MirStripDebugInfo; + +/// Conditionally remove some of the VarDebugInfo in MIR. +/// +/// In particular, stripping non-parameter debug info for tiny, primitive-like +/// methods in core saves work later, and nobody ever wanted to use it anyway. +pub(super) struct StripDebugInfo; + +impl<'tcx> crate::MirPass<'tcx> for StripDebugInfo { + fn is_enabled(&self, sess: &rustc_session::Session) -> bool { + sess.opts.unstable_opts.mir_strip_debuginfo != MirStripDebugInfo::None + } + + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + match tcx.sess.opts.unstable_opts.mir_strip_debuginfo { + MirStripDebugInfo::None => return, + MirStripDebugInfo::AllLocals => {} + MirStripDebugInfo::LocalsInTinyFunctions + if let TerminatorKind::Return { .. } = + body.basic_blocks[START_BLOCK].terminator().kind => {} + MirStripDebugInfo::LocalsInTinyFunctions => return, + } + + body.var_debug_info.retain(|vdi| { + matches!( + vdi.value, + VarDebugInfoContents::Place(place) + if place.local.as_usize() <= body.arg_count && place.local != RETURN_PLACE, + ) + }); + } +} diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 404fbb6b839..bce015046e1 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1115,14 +1115,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ); } UnOp::PtrMetadata => { - if !matches!(self.body.phase, MirPhase::Runtime(_)) { - // It would probably be fine to support this in earlier phases, but at - // the time of writing it's only ever introduced from intrinsic - // lowering or other runtime-phase optimization passes, so earlier - // things can just `bug!` on it. - self.fail(location, "PtrMetadata should be in runtime MIR only"); - } - check_kinds!( a, "Cannot PtrMetadata non-pointer non-reference type {:?}", 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/messages.ftl b/compiler/rustc_monomorphize/messages.ftl index 8528a2e68c0..540ce7cc4ce 100644 --- a/compiler/rustc_monomorphize/messages.ftl +++ b/compiler/rustc_monomorphize/messages.ftl @@ -41,6 +41,4 @@ monomorphize_symbol_already_defined = symbol `{$symbol}` is already defined monomorphize_unknown_cgu_collection_mode = unknown codegen-item collection mode '{$mode}', falling back to 'lazy' mode -monomorphize_unused_generic_params = item has unused generic parameters - monomorphize_written_to_path = the full type name has been written to '{$path}' diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 5efe7ffc7b9..480d82c1a38 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -965,9 +965,7 @@ fn should_codegen_locally<'tcx>(tcx: TyCtxtAt<'tcx>, instance: Instance<'tcx>) - return true; } - if tcx.is_reachable_non_generic(def_id) - || instance.polymorphize(*tcx).upstream_monomorphization(*tcx).is_some() - { + if tcx.is_reachable_non_generic(def_id) || instance.upstream_monomorphization(*tcx).is_some() { // We can link to the item in question, no instance needed in this crate. return false; } @@ -1114,7 +1112,7 @@ fn create_fn_mono_item<'tcx>( crate::util::dump_closure_profile(tcx, instance); } - respan(source, MonoItem::Fn(instance.polymorphize(tcx))) + respan(source, MonoItem::Fn(instance)) } /// Creates a `MonoItem` for each method that is referenced by the vtable for diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs index 02865cad302..fc8d63b5888 100644 --- a/compiler/rustc_monomorphize/src/errors.rs +++ b/compiler/rustc_monomorphize/src/errors.rs @@ -1,11 +1,8 @@ use std::path::PathBuf; -use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level}; use rustc_macros::{Diagnostic, LintDiagnostic}; use rustc_span::{Span, Symbol}; -use crate::fluent_generated as fluent; - #[derive(Diagnostic)] #[diag(monomorphize_recursion_limit)] pub(crate) struct RecursionLimit { @@ -28,28 +25,6 @@ pub(crate) struct NoOptimizedMir { pub crate_name: Symbol, } -pub(crate) struct UnusedGenericParamsHint { - pub span: Span, - pub param_spans: Vec<Span>, - pub param_names: Vec<String>, -} - -impl<G: EmissionGuarantee> Diagnostic<'_, G> for UnusedGenericParamsHint { - #[track_caller] - fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { - let mut diag = Diag::new(dcx, level, fluent::monomorphize_unused_generic_params); - diag.span(self.span); - for (span, name) in self.param_spans.into_iter().zip(self.param_names) { - // FIXME: I can figure out how to do a label with a fluent string with a fixed message, - // or a label with a dynamic value in a hard-coded string, but I haven't figured out - // how to combine the two. 😢 - #[allow(rustc::untranslatable_diagnostic)] - diag.span_label(span, format!("generic parameter `{name}` is unused")); - } - diag - } -} - #[derive(LintDiagnostic)] #[diag(monomorphize_large_assignments)] #[note] diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index 0f08930fb4c..714b64b3a23 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -19,7 +19,6 @@ mod collector; mod errors; mod mono_checks; mod partitioning; -mod polymorphize; mod util; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } @@ -43,13 +42,15 @@ fn custom_coerce_unsize_info<'tcx>( .. })) => Ok(tcx.coerce_unsized_info(impl_def_id)?.custom_kind.unwrap()), impl_source => { - bug!("invalid `CoerceUnsized` impl_source: {:?}", impl_source); + bug!( + "invalid `CoerceUnsized` from {source_ty} to {target_ty}: impl_source: {:?}", + impl_source + ); } } } pub fn provide(providers: &mut Providers) { partitioning::provide(providers); - polymorphize::provide(providers); mono_checks::provide(providers); } diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 7ea4ded2b05..33e1065f051 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -113,7 +113,6 @@ use rustc_middle::mir::mono::{ Visibility, }; use rustc_middle::ty::print::{characteristic_def_id_of_type, with_no_trimmed_paths}; -use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, InstanceKind, TyCtxt}; use rustc_middle::util::Providers; use rustc_session::CodegenUnits; @@ -661,18 +660,14 @@ fn characteristic_def_id_of_mono_item<'tcx>( return None; } - // When polymorphization is enabled, methods which do not depend on their generic - // parameters, but the self-type of their impl block do will fail to normalize. - if !tcx.sess.opts.unstable_opts.polymorphize || !instance.has_param() { - // This is a method within an impl, find out what the self-type is: - let impl_self_ty = tcx.instantiate_and_normalize_erasing_regions( - instance.args, - ty::TypingEnv::fully_monomorphized(), - tcx.type_of(impl_def_id), - ); - if let Some(def_id) = characteristic_def_id_of_type(impl_self_ty) { - return Some(def_id); - } + // This is a method within an impl, find out what the self-type is: + let impl_self_ty = tcx.instantiate_and_normalize_erasing_regions( + instance.args, + ty::TypingEnv::fully_monomorphized(), + tcx.type_of(impl_def_id), + ); + if let Some(def_id) = characteristic_def_id_of_type(impl_self_ty) { + return Some(def_id); } } @@ -838,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. @@ -852,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_monomorphize/src/polymorphize.rs b/compiler/rustc_monomorphize/src/polymorphize.rs deleted file mode 100644 index e049fe99664..00000000000 --- a/compiler/rustc_monomorphize/src/polymorphize.rs +++ /dev/null @@ -1,334 +0,0 @@ -//! Polymorphization Analysis -//! ========================= -//! -//! This module implements an analysis of functions, methods and closures to determine which -//! generic parameters are unused (and eventually, in what ways generic parameters are used - only -//! for their size, offset of a field, etc.). - -use rustc_hir::ConstContext; -use rustc_hir::def::DefKind; -use rustc_hir::def_id::DefId; -use rustc_middle::mir::visit::{TyContext, Visitor}; -use rustc_middle::mir::{self, Local, LocalDecl, Location}; -use rustc_middle::query::Providers; -use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; -use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, UnusedGenericParams}; -use rustc_span::symbol::sym; -use tracing::{debug, instrument}; - -use crate::errors::UnusedGenericParamsHint; - -/// Provide implementations of queries relating to polymorphization analysis. -pub(crate) fn provide(providers: &mut Providers) { - providers.unused_generic_params = unused_generic_params; -} - -/// Determine which generic parameters are used by the instance. -/// -/// Returns a bitset where bits representing unused parameters are set (`is_empty` indicates all -/// parameters are used). -fn unused_generic_params<'tcx>( - tcx: TyCtxt<'tcx>, - instance: ty::InstanceKind<'tcx>, -) -> UnusedGenericParams { - assert!(instance.def_id().is_local()); - - if !tcx.sess.opts.unstable_opts.polymorphize { - // If polymorphization disabled, then all parameters are used. - return UnusedGenericParams::new_all_used(); - } - - let def_id = instance.def_id(); - // Exit early if this instance should not be polymorphized. - if !should_polymorphize(tcx, def_id, instance) { - return UnusedGenericParams::new_all_used(); - } - - let generics = tcx.generics_of(def_id); - debug!(?generics); - - // Exit early when there are no parameters to be unused. - if generics.is_empty() { - return UnusedGenericParams::new_all_used(); - } - - // Create a bitset with N rightmost ones for each parameter. - let generics_count: u32 = - generics.count().try_into().expect("more generic parameters than can fit into a `u32`"); - let mut unused_parameters = UnusedGenericParams::new_all_unused(generics_count); - debug!(?unused_parameters, "(start)"); - - mark_used_by_default_parameters(tcx, def_id, generics, &mut unused_parameters); - debug!(?unused_parameters, "(after default)"); - - // Visit MIR and accumulate used generic parameters. - let body = match tcx.hir().body_const_context(def_id.expect_local()) { - // Const functions are actually called and should thus be considered for polymorphization - // via their runtime MIR. - Some(ConstContext::ConstFn) | None => tcx.optimized_mir(def_id), - Some(_) => tcx.mir_for_ctfe(def_id), - }; - let mut vis = MarkUsedGenericParams { tcx, def_id, unused_parameters: &mut unused_parameters }; - vis.visit_body(body); - debug!(?unused_parameters, "(end)"); - - // Emit errors for debugging and testing if enabled. - if !unused_parameters.all_used() { - emit_unused_generic_params_error(tcx, def_id, generics, &unused_parameters); - } - - unused_parameters -} - -/// Returns `true` if the instance should be polymorphized. -fn should_polymorphize<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: DefId, - instance: ty::InstanceKind<'tcx>, -) -> bool { - // If an instance's MIR body is not polymorphic then the modified generic parameters that are - // derived from polymorphization's result won't make any difference. - if !instance.has_polymorphic_mir_body() { - return false; - } - - // Don't polymorphize intrinsics or virtual calls - calling `instance_mir` will panic. - if matches!(instance, ty::InstanceKind::Intrinsic(..) | ty::InstanceKind::Virtual(..)) { - return false; - } - - // Foreign items have no bodies to analyze. - if tcx.is_foreign_item(def_id) { - return false; - } - - // Make sure there is MIR available. - match tcx.hir().body_const_context(def_id.expect_local()) { - Some(ConstContext::ConstFn) | None if !tcx.is_mir_available(def_id) => { - debug!("no mir available"); - return false; - } - Some(_) if !tcx.is_ctfe_mir_available(def_id) => { - debug!("no ctfe mir available"); - return false; - } - _ => true, - } -} - -/// Some parameters are considered used-by-default, such as non-generic parameters and the dummy -/// generic parameters from closures, this function marks them as used. `leaf_is_closure` should -/// be `true` if the item that `unused_generic_params` was invoked on is a closure. -#[instrument(level = "debug", skip(tcx, def_id, generics, unused_parameters))] -fn mark_used_by_default_parameters<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: DefId, - generics: &'tcx ty::Generics, - unused_parameters: &mut UnusedGenericParams, -) { - match tcx.def_kind(def_id) { - DefKind::Closure | DefKind::SyntheticCoroutineBody => { - for param in &generics.own_params { - debug!(?param, "(closure/gen)"); - unused_parameters.mark_used(param.index); - } - } - DefKind::Mod - | DefKind::Struct - | DefKind::Union - | DefKind::Enum - | DefKind::Variant - | DefKind::Trait - | DefKind::TyAlias - | DefKind::ForeignTy - | DefKind::TraitAlias - | DefKind::AssocTy - | DefKind::TyParam - | DefKind::Fn - | DefKind::Const - | DefKind::ConstParam - | DefKind::Static { .. } - | DefKind::Ctor(_, _) - | DefKind::AssocFn - | DefKind::AssocConst - | DefKind::Macro(_) - | DefKind::ExternCrate - | DefKind::Use - | DefKind::ForeignMod - | DefKind::AnonConst - | DefKind::InlineConst - | DefKind::OpaqueTy - | DefKind::Field - | DefKind::LifetimeParam - | DefKind::GlobalAsm - | DefKind::Impl { .. } => { - for param in &generics.own_params { - debug!(?param, "(other)"); - if let ty::GenericParamDefKind::Lifetime = param.kind { - unused_parameters.mark_used(param.index); - } - } - } - } - - if let Some(parent) = generics.parent { - mark_used_by_default_parameters(tcx, parent, tcx.generics_of(parent), unused_parameters); - } -} - -/// Emit errors for the function annotated by `#[rustc_polymorphize_error]`, labelling each generic -/// parameter which was unused. -#[instrument(level = "debug", skip(tcx, generics))] -fn emit_unused_generic_params_error<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: DefId, - generics: &'tcx ty::Generics, - unused_parameters: &UnusedGenericParams, -) { - let base_def_id = tcx.typeck_root_def_id(def_id); - if !tcx.has_attr(base_def_id, sym::rustc_polymorphize_error) { - return; - } - - let fn_span = match tcx.opt_item_ident(def_id) { - Some(ident) => ident.span, - _ => tcx.def_span(def_id), - }; - - let mut param_spans = Vec::new(); - let mut param_names = Vec::new(); - let mut next_generics = Some(generics); - while let Some(generics) = next_generics { - for param in &generics.own_params { - if unused_parameters.is_unused(param.index) { - debug!(?param); - let def_span = tcx.def_span(param.def_id); - param_spans.push(def_span); - param_names.push(param.name.to_string()); - } - } - - next_generics = generics.parent.map(|did| tcx.generics_of(did)); - } - - tcx.dcx().emit_err(UnusedGenericParamsHint { span: fn_span, param_spans, param_names }); -} - -/// Visitor used to aggregate generic parameter uses. -struct MarkUsedGenericParams<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - def_id: DefId, - unused_parameters: &'a mut UnusedGenericParams, -} - -impl<'a, 'tcx> MarkUsedGenericParams<'a, 'tcx> { - /// Invoke `unused_generic_params` on a body contained within the current item (e.g. - /// a closure, coroutine or constant). - #[instrument(level = "debug", skip(self, def_id, args))] - fn visit_child_body(&mut self, def_id: DefId, args: GenericArgsRef<'tcx>) { - let instance = ty::InstanceKind::Item(def_id); - let unused = self.tcx.unused_generic_params(instance); - debug!(?self.unused_parameters, ?unused); - for (i, arg) in args.iter().enumerate() { - let i = i.try_into().unwrap(); - if unused.is_used(i) { - arg.visit_with(self); - } - } - debug!(?self.unused_parameters); - } -} - -impl<'a, 'tcx> Visitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> { - #[instrument(level = "debug", skip(self, local))] - fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) { - if local == Local::from_usize(1) { - let def_kind = self.tcx.def_kind(self.def_id); - if matches!(def_kind, DefKind::Closure) { - // Skip visiting the closure/coroutine that is currently being processed. This only - // happens because the first argument to the closure is a reference to itself and - // that will call `visit_args`, resulting in each generic parameter captured being - // considered used by default. - debug!("skipping closure args"); - return; - } - } - - self.super_local_decl(local, local_decl); - } - - fn visit_const_operand(&mut self, ct: &mir::ConstOperand<'tcx>, location: Location) { - match ct.const_ { - mir::Const::Ty(_, c) => { - c.visit_with(self); - } - mir::Const::Unevaluated(mir::UnevaluatedConst { def, args: _, promoted }, ty) => { - // Avoid considering `T` unused when constants are of the form: - // `<Self as Foo<T>>::foo::promoted[p]` - if let Some(p) = promoted { - if self.def_id == def && !self.tcx.generics_of(def).has_self { - // If there is a promoted, don't look at the args - since it will always contain - // the generic parameters, instead, traverse the promoted MIR. - let promoted = self.tcx.promoted_mir(def); - self.visit_body(&promoted[p]); - } - } - - Visitor::visit_ty(self, ty, TyContext::Location(location)); - } - mir::Const::Val(_, ty) => Visitor::visit_ty(self, ty, TyContext::Location(location)), - } - } - - fn visit_ty(&mut self, ty: Ty<'tcx>, _: TyContext) { - ty.visit_with(self); - } -} - -impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for MarkUsedGenericParams<'a, 'tcx> { - #[instrument(level = "debug", skip(self))] - fn visit_const(&mut self, c: ty::Const<'tcx>) { - if !c.has_non_region_param() { - return; - } - - match c.kind() { - ty::ConstKind::Param(param) => { - debug!(?param); - self.unused_parameters.mark_used(param.index); - } - ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args }) - if matches!(self.tcx.def_kind(def), DefKind::AnonConst) => - { - self.visit_child_body(def, args); - } - _ => c.super_visit_with(self), - } - } - - #[instrument(level = "debug", skip(self))] - fn visit_ty(&mut self, ty: Ty<'tcx>) { - if !ty.has_non_region_param() { - return; - } - - match *ty.kind() { - ty::Closure(def_id, args) | ty::Coroutine(def_id, args, ..) => { - debug!(?def_id); - // Avoid cycle errors with coroutines. - if def_id == self.def_id { - return; - } - - // Consider any generic parameters used by any closures/coroutines as used in the - // parent. - self.visit_child_body(def_id, args); - } - ty::Param(param) => { - debug!(?param); - self.unused_parameters.mark_used(param.index); - } - _ => ty.super_visit_with(self), - } - } -} 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 f5b1b23b8e9..63dbee2640b 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 @@ -144,7 +144,7 @@ where then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>, ) -> Result<Candidate<I>, NoSolution> { if let Some(projection_pred) = assumption.as_projection_clause() { - if projection_pred.projection_def_id() == goal.predicate.def_id() { + if projection_pred.item_def_id() == goal.predicate.def_id() { let cx = ecx.cx(); if !DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify( goal.predicate.alias.args, 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 6641d2bf924..12df35d30b8 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -169,6 +169,14 @@ where return result; } + // Only consider auto impls of unsafe traits when there are no unsafe + // fields. + if ecx.cx().trait_is_unsafe(goal.predicate.def_id()) + && goal.predicate.self_ty().has_unsafe_fields() + { + return Err(NoSolution); + } + // We only look into opaque types during analysis for opaque types // outside of their defining scope. Doing so for opaques in the // defining scope may require calling `typeck` on the same item we're diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index e5cd4622dae..d50bd18a1d7 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -169,9 +169,6 @@ parse_enum_struct_mutually_exclusive = `enum` and `struct` are mutually exclusiv parse_eq_field_init = expected `:`, found `=` .suggestion = replace equals symbol with a colon -parse_equals_struct_default = default values on `struct` fields aren't supported - .suggestion = remove this unsupported default value - parse_escape_only_char = {$byte -> [true] byte *[false] character diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 14f2dd32e92..4c4e03cdfa3 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -3067,14 +3067,6 @@ pub(crate) struct SingleColonStructType { } #[derive(Diagnostic)] -#[diag(parse_equals_struct_default)] -pub(crate) struct EqualsStructDefault { - #[primary_span] - #[suggestion(code = "", applicability = "machine-applicable", style = "verbose")] - pub span: Span, -} - -#[derive(Diagnostic)] #[diag(parse_macro_rules_missing_bang)] pub(crate) struct MacroRulesMissingBang { #[primary_span] diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index d97f05dc7eb..443ddfc94ec 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -69,24 +69,30 @@ pub(crate) fn lex_token_trees<'psess, 'src>( token: Token::dummy(), diag_info: TokenTreeDiagInfo::default(), }; - let (_open_spacing, stream, res) = lexer.lex_token_trees(/* is_delimited */ false); - let unmatched_delims = lexer.diag_info.unmatched_delims; - - if res.is_ok() && unmatched_delims.is_empty() { - Ok(stream) - } else { - // Return error if there are unmatched delimiters or unclosed delimiters. - // We emit delimiter mismatch errors first, then emit the unclosing delimiter mismatch - // because the delimiter mismatch is more likely to be the root cause of error - let mut buffer: Vec<_> = unmatched_delims - .into_iter() - .filter_map(|unmatched_delim| make_unclosed_delims_error(unmatched_delim, psess)) - .collect(); - if let Err(errs) = res { - // Add unclosing delimiter or diff marker errors - buffer.extend(errs); + let res = lexer.lex_token_trees(/* is_delimited */ false); + + let mut unmatched_delims: Vec<_> = lexer + .diag_info + .unmatched_delims + .into_iter() + .filter_map(|unmatched_delim| make_unclosed_delims_error(unmatched_delim, psess)) + .collect(); + + match res { + Ok((_open_spacing, stream)) => { + if unmatched_delims.is_empty() { + Ok(stream) + } else { + // Return error if there are unmatched delimiters or unclosed delimiters. + Err(unmatched_delims) + } + } + Err(errs) => { + // We emit delimiter mismatch errors first, then emit the unclosing delimiter mismatch + // because the delimiter mismatch is more likely to be the root cause of error + unmatched_delims.extend(errs); + Err(unmatched_delims) } - Err(buffer) } } @@ -822,7 +828,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { /// Detect guarded string literal syntax /// - /// RFC 3598 reserved this syntax for future use. As of Rust 2024, + /// RFC 3593 reserved this syntax for future use. As of Rust 2024, /// using this syntax produces an error. In earlier editions, however, it /// only results in an (allowed by default) lint, and is treated as /// separate tokens. diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs index c6c9eb3b0b2..b3f83a32024 100644 --- a/compiler/rustc_parse/src/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -1,12 +1,10 @@ use rustc_ast::token::{self, Delimiter, Token}; use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree}; use rustc_ast_pretty::pprust::token_to_string; -use rustc_errors::{Applicability, PErr}; -use rustc_span::symbol::kw; +use rustc_errors::Diag; use super::diagnostics::{report_suspicious_mismatch_block, same_indentation_level}; use super::{Lexer, UnmatchedDelim}; -use crate::Parser; impl<'psess, 'src> Lexer<'psess, 'src> { // Lex into a token stream. The `Spacing` in the result is that of the @@ -14,7 +12,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { pub(super) fn lex_token_trees( &mut self, is_delimited: bool, - ) -> (Spacing, TokenStream, Result<(), Vec<PErr<'psess>>>) { + ) -> Result<(Spacing, TokenStream), Vec<Diag<'psess>>> { // Move past the opening delimiter. let open_spacing = self.bump_minimal(); @@ -27,25 +25,25 @@ impl<'psess, 'src> Lexer<'psess, 'src> { debug_assert!(!matches!(delim, Delimiter::Invisible(_))); buf.push(match self.lex_token_tree_open_delim(delim) { Ok(val) => val, - Err(errs) => return (open_spacing, TokenStream::new(buf), Err(errs)), + Err(errs) => return Err(errs), }) } token::CloseDelim(delim) => { // Invisible delimiters cannot occur here because `TokenTreesReader` parses // code directly from strings, with no macro expansion involved. debug_assert!(!matches!(delim, Delimiter::Invisible(_))); - return ( - open_spacing, - TokenStream::new(buf), - if is_delimited { Ok(()) } else { Err(vec![self.close_delim_err(delim)]) }, - ); + return if is_delimited { + Ok((open_spacing, TokenStream::new(buf))) + } else { + Err(vec![self.close_delim_err(delim)]) + }; } token::Eof => { - return ( - open_spacing, - TokenStream::new(buf), - if is_delimited { Err(vec![self.eof_err()]) } else { Ok(()) }, - ); + return if is_delimited { + Err(vec![self.eof_err()]) + } else { + Ok((open_spacing, TokenStream::new(buf))) + }; } _ => { // Get the next normal token. @@ -56,7 +54,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { } } - fn eof_err(&mut self) -> PErr<'psess> { + fn eof_err(&mut self) -> Diag<'psess> { let msg = "this file contains an unclosed delimiter"; let mut err = self.dcx().struct_span_err(self.token.span, msg); @@ -98,7 +96,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { fn lex_token_tree_open_delim( &mut self, open_delim: Delimiter, - ) -> Result<TokenTree, Vec<PErr<'psess>>> { + ) -> Result<TokenTree, Vec<Diag<'psess>>> { // The span for beginning of the delimited section. let pre_span = self.token.span; @@ -107,10 +105,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { // Lex the token trees within the delimiters. // We stop at any delimiter so we can try to recover if the user // uses an incorrect delimiter. - let (open_spacing, tts, res) = self.lex_token_trees(/* is_delimited */ true); - if let Err(errs) = res { - return Err(self.unclosed_delim_err(tts, errs)); - } + let (open_spacing, tts) = self.lex_token_trees(/* is_delimited */ true)?; // Expand to cover the entire delimited token tree. let delim_span = DelimSpan::from_pair(pre_span, self.token.span); @@ -247,68 +242,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { this_spacing } - fn unclosed_delim_err( - &mut self, - tts: TokenStream, - mut errs: Vec<PErr<'psess>>, - ) -> Vec<PErr<'psess>> { - // If there are unclosed delims, see if there are diff markers and if so, point them - // out instead of complaining about the unclosed delims. - let mut parser = Parser::new(self.psess, tts, None); - let mut diff_errs = vec![]; - // Suggest removing a `{` we think appears in an `if`/`while` condition. - // We want to suggest removing a `{` only if we think we're in an `if`/`while` condition, - // but we have no way of tracking this in the lexer itself, so we piggyback on the parser. - let mut in_cond = false; - while parser.token != token::Eof { - if let Err(diff_err) = parser.err_vcs_conflict_marker() { - diff_errs.push(diff_err); - } else if parser.is_keyword_ahead(0, &[kw::If, kw::While]) { - in_cond = true; - } else if matches!( - parser.token.kind, - token::CloseDelim(Delimiter::Brace) | token::FatArrow - ) { - // End of the `if`/`while` body, or the end of a `match` guard. - in_cond = false; - } else if in_cond && parser.token == token::OpenDelim(Delimiter::Brace) { - // Store the `&&` and `let` to use their spans later when creating the diagnostic - let maybe_andand = parser.look_ahead(1, |t| t.clone()); - let maybe_let = parser.look_ahead(2, |t| t.clone()); - if maybe_andand == token::OpenDelim(Delimiter::Brace) { - // This might be the beginning of the `if`/`while` body (i.e., the end of the - // condition). - in_cond = false; - } else if maybe_andand == token::AndAnd && maybe_let.is_keyword(kw::Let) { - let mut err = parser.dcx().struct_span_err( - parser.token.span, - "found a `{` in the middle of a let-chain", - ); - err.span_suggestion( - parser.token.span, - "consider removing this brace to parse the `let` as part of the same chain", - "", - Applicability::MachineApplicable, - ); - err.span_label( - maybe_andand.span.to(maybe_let.span), - "you might have meant to continue the let-chain here", - ); - errs.push(err); - } - } - parser.bump(); - } - if !diff_errs.is_empty() { - for err in errs { - err.cancel(); - } - return diff_errs; - } - errs - } - - fn close_delim_err(&mut self, delim: Delimiter) -> PErr<'psess> { + fn close_delim_err(&mut self, delim: Delimiter) -> Diag<'psess> { // An unexpected closing delimiter (i.e., there is no matching opening delimiter). let token_str = token_to_string(&self.token); let msg = format!("unexpected closing delimiter: `{token_str}`"); diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 2792050a0b3..f963a424a7f 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -24,6 +24,7 @@ use rustc_data_structures::sync::Lrc; use rustc_errors::{Diag, FatalError, PResult}; use rustc_session::parse::ParseSess; use rustc_span::{FileName, SourceFile, Span}; +pub use unicode_normalization::UNICODE_VERSION as UNICODE_NORMALIZATION_VERSION; pub const MACRO_ARGUMENTS: Option<&str> = Some("macro arguments"); diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index b6fa2099588..8cf37671185 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -1,6 +1,5 @@ -use rustc_ast as ast; -use rustc_ast::attr; use rustc_ast::token::{self, Delimiter}; +use rustc_ast::{self as ast, Attribute, attr}; use rustc_errors::codes::*; use rustc_errors::{Diag, PResult}; use rustc_span::symbol::kw; @@ -48,7 +47,7 @@ impl<'a> Parser<'a> { 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 prev_outer_attr_sp = outer_attrs.last().map(|attr: &Attribute| attr.span); let inner_error_reason = if just_parsed_doc_comment { Some(InnerAttrForbiddenReason::AfterOuterDocComment { diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 34131e3af6e..e5edf605d82 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -16,7 +16,7 @@ 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, PErr, PResult, Subdiagnostic, + Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, FatalError, PResult, Subdiagnostic, Suggestions, pluralize, }; use rustc_session::errors::ExprParenthesesNeeded; @@ -2132,7 +2132,7 @@ impl<'a> Parser<'a> { &mut self, delim: Delimiter, lo: Span, - err: PErr<'a>, + err: Diag<'a>, ) -> P<Expr> { let guar = err.emit(); // Recover from parse error, callers expect the closing delim to be consumed. @@ -3014,7 +3014,7 @@ impl<'a> Parser<'a> { } /// Check for exclusive ranges written as `..<` - pub(crate) fn maybe_err_dotdotlt_syntax(&self, maybe_lt: Token, mut err: PErr<'a>) -> PErr<'a> { + 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)) || matches!(self.token.kind, token::Literal(..))) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 8d16d44b0a2..a2136399b0c 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -15,7 +15,7 @@ use rustc_ast::visit::{Visitor, walk_expr}; use rustc_ast::{ self as ast, AnonConst, Arm, AttrStyle, AttrVec, BinOp, BinOpKind, BlockCheckMode, CaptureBy, ClosureBinder, DUMMY_NODE_ID, Expr, ExprField, ExprKind, FnDecl, FnRetTy, Label, MacCall, - MetaItemLit, Movability, Param, RangeLimits, StmtKind, Ty, TyKind, UnOp, + MetaItemLit, Movability, Param, RangeLimits, StmtKind, Ty, TyKind, UnOp, UnsafeBinderCastKind, }; use rustc_ast_pretty::pprust; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -1369,11 +1369,14 @@ impl<'a> Parser<'a> { )) } else { // Field access `expr.f` + let span = lo.to(self.prev_token.span); if let Some(args) = seg.args { - self.dcx().emit_err(errors::FieldExpressionWithGeneric(args.span())); + // See `StashKey::GenericInFieldExpr` for more info on why we stash this. + self.dcx() + .create_err(errors::FieldExpressionWithGeneric(args.span())) + .stash(seg.ident.span, StashKey::GenericInFieldExpr); } - let span = lo.to(self.prev_token.span); Ok(self.mk_expr(span, ExprKind::Field(self_arg, seg.ident))) } } @@ -1928,6 +1931,12 @@ impl<'a> Parser<'a> { Ok(match ident.name { sym::offset_of => Some(this.parse_expr_offset_of(lo)?), sym::type_ascribe => Some(this.parse_expr_type_ascribe(lo)?), + sym::wrap_binder => { + Some(this.parse_expr_unsafe_binder_cast(lo, UnsafeBinderCastKind::Wrap)?) + } + sym::unwrap_binder => { + Some(this.parse_expr_unsafe_binder_cast(lo, UnsafeBinderCastKind::Unwrap)?) + } _ => None, }) }) @@ -2003,6 +2012,17 @@ impl<'a> Parser<'a> { Ok(self.mk_expr(span, ExprKind::Type(expr, ty))) } + pub(crate) fn parse_expr_unsafe_binder_cast( + &mut self, + lo: Span, + 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 span = lo.to(self.token.span); + Ok(self.mk_expr(span, ExprKind::UnsafeBinderCast(kind, expr, ty))) + } + /// Returns a string literal if the next token is a string literal. /// In case of error returns `Some(lit)` if the next token is a literal with a wrong kind, /// and returns `None` if the next token is not literal at all. @@ -2363,10 +2383,7 @@ impl<'a> Parser<'a> { }; match coroutine_kind { - Some(CoroutineKind::Async { span, .. }) => { - // Feature-gate `async ||` closures. - self.psess.gated_spans.gate(sym::async_closure, span); - } + Some(CoroutineKind::Async { .. }) => {} Some(CoroutineKind::Gen { span, .. }) | Some(CoroutineKind::AsyncGen { span, .. }) => { // Feature-gate `gen ||` and `async gen ||` closures. // FIXME(gen_blocks): This perhaps should be a different gate. @@ -2631,7 +2648,7 @@ impl<'a> Parser<'a> { }; self.bump(); // Eat `let` token let lo = self.prev_token.span; - let pat = self.parse_pat_allow_top_alt( + let pat = self.parse_pat_no_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, @@ -2778,7 +2795,7 @@ impl<'a> Parser<'a> { }; // Try to parse the pattern `for ($PAT) in $EXPR`. let pat = match ( - self.parse_pat_allow_top_alt( + self.parse_pat_allow_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, @@ -3241,7 +3258,7 @@ impl<'a> Parser<'a> { // then we should recover. let mut snapshot = this.create_snapshot_for_diagnostic(); let pattern_follows = snapshot - .parse_pat_allow_top_alt( + .parse_pat_no_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, @@ -3315,43 +3332,37 @@ impl<'a> Parser<'a> { fn parse_match_arm_pat_and_guard(&mut self) -> PResult<'a, (P<Pat>, Option<P<Expr>>)> { if self.token == token::OpenDelim(Delimiter::Parenthesis) { - // Detect and recover from `($pat if $cond) => $arm`. let left = self.token.span; - match self.parse_pat_allow_top_alt( + let pat = self.parse_pat_no_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, CommaRecoveryMode::EitherTupleOrPipe, - ) { - Ok(pat) => Ok((pat, self.parse_match_arm_guard()?)), - Err(err) - if let prev_sp = self.prev_token.span - && let true = self.eat_keyword(kw::If) => - { - // We know for certain we've found `($pat if` so far. - let mut cond = match self.parse_match_guard_condition() { - Ok(cond) => cond, - Err(cond_err) => { - cond_err.cancel(); - return Err(err); - } - }; - err.cancel(); - CondChecker::new(self).visit_expr(&mut cond); - self.eat_to_tokens(&[&token::CloseDelim(Delimiter::Parenthesis)]); - self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; - let right = self.prev_token.span; - self.dcx().emit_err(errors::ParenthesesInMatchPat { - span: vec![left, right], - sugg: errors::ParenthesesInMatchPatSugg { left, right }, - }); - Ok((self.mk_pat(left.to(prev_sp), ast::PatKind::Wild), Some(cond))) - } - Err(err) => Err(err), + )?; + if let ast::PatKind::Paren(subpat) = &pat.kind + && let ast::PatKind::Guard(..) = &subpat.kind + { + // Detect and recover from `($pat if $cond) => $arm`. + // FIXME(guard_patterns): convert this to a normal guard instead + let span = pat.span; + let ast::PatKind::Paren(subpat) = pat.into_inner().kind else { unreachable!() }; + let ast::PatKind::Guard(_, mut cond) = subpat.into_inner().kind else { + unreachable!() + }; + self.psess.gated_spans.ungate_last(sym::guard_patterns, cond.span); + CondChecker::new(self).visit_expr(&mut cond); + let right = self.prev_token.span; + self.dcx().emit_err(errors::ParenthesesInMatchPat { + span: vec![left, right], + sugg: errors::ParenthesesInMatchPatSugg { left, right }, + }); + Ok((self.mk_pat(span, ast::PatKind::Wild), Some(cond))) + } else { + Ok((pat, self.parse_match_arm_guard()?)) } } else { // Regular parser flow: - let pat = self.parse_pat_allow_top_alt( + let pat = self.parse_pat_no_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, @@ -3539,7 +3550,7 @@ impl<'a> Parser<'a> { 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)) { - base = ast::StructRest::Rest(self.prev_token.span.shrink_to_hi()); + base = ast::StructRest::Rest(self.prev_token.span); break; } match self.parse_expr() { @@ -4022,7 +4033,9 @@ impl MutVisitor for CondChecker<'_> { mut_visit::walk_expr(self, e); self.forbid_let_reason = forbid_let_reason; } - ExprKind::Cast(ref mut op, _) | ExprKind::Type(ref mut op, _) => { + ExprKind::Cast(ref mut op, _) + | ExprKind::Type(ref mut op, _) + | ExprKind::UnsafeBinderCast(_, ref mut op, _) => { let forbid_let_reason = self.forbid_let_reason; self.forbid_let_reason = Some(OtherForbidden); self.visit_expr(op); diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 475cd09147f..e27fc963eb9 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -45,7 +45,7 @@ impl<'a> Parser<'a> { let (inner_attrs, items, inner_span) = self.parse_mod(&token::CloseDelim(Delimiter::Brace))?; attrs.extend(inner_attrs); - ModKind::Loaded(items, Inline::Yes, inner_span) + ModKind::Loaded(items, Inline::Yes, inner_span, Ok(())) }; Ok((id, ItemKind::Mod(safety, mod_kind))) } @@ -1836,6 +1836,22 @@ impl<'a> Parser<'a> { return Err(err); } }; + let mut default = None; + if p.token == token::Eq { + let mut snapshot = p.create_snapshot_for_diagnostic(); + snapshot.bump(); + match snapshot.parse_expr_anon_const() { + Ok(const_expr) => { + let sp = ty.span.shrink_to_hi().to(const_expr.value.span); + p.psess.gated_spans.gate(sym::default_field_values, sp); + p.restore_snapshot(snapshot); + default = Some(const_expr); + } + Err(err) => { + err.cancel(); + } + } + } Ok(( FieldDef { @@ -1845,6 +1861,7 @@ impl<'a> Parser<'a> { ident: None, id: DUMMY_NODE_ID, ty, + default, attrs, is_placeholder: false, }, @@ -2024,12 +2041,15 @@ impl<'a> Parser<'a> { if self.token == token::Colon && self.look_ahead(1, |t| *t != token::Colon) { self.dcx().emit_err(errors::SingleColonStructType { span: self.token.span }); } - if self.token == token::Eq { + let default = if self.token == token::Eq { self.bump(); let const_expr = self.parse_expr_anon_const()?; let sp = ty.span.shrink_to_hi().to(const_expr.value.span); - self.dcx().emit_err(errors::EqualsStructDefault { span: sp }); - } + self.psess.gated_spans.gate(sym::default_field_values, sp); + Some(const_expr) + } else { + None + }; Ok(FieldDef { span: lo.to(self.prev_token.span), ident: Some(name), @@ -2037,6 +2057,7 @@ impl<'a> Parser<'a> { safety, id: DUMMY_NODE_ID, ty, + default, attrs, is_placeholder: false, }) diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 37556c064d8..976ffe608a2 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -29,9 +29,9 @@ use rustc_ast::tokenstream::{ }; 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; @@ -1376,7 +1376,7 @@ impl<'a> Parser<'a> { AttrArgs::Delimited(args) } else if self.eat(&token::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 }) diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 8fb6f85d0dd..752a52b382b 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -174,7 +174,7 @@ impl<'a> Parser<'a> { NonterminalKind::Pat(pat_kind) => { NtPat(self.collect_tokens_no_attrs(|this| match pat_kind { PatParam { .. } => this.parse_pat_no_top_alt(None, None), - PatWithOr => this.parse_pat_allow_top_alt( + PatWithOr => this.parse_pat_no_top_guard( None, RecoverComma::No, RecoverColon::No, diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index e08b925f008..255be721525 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -99,9 +99,34 @@ pub enum PatternLocation { impl<'a> Parser<'a> { /// Parses a pattern. /// - /// Corresponds to `pat<no_top_alt>` in RFC 2535 and does not admit or-patterns - /// at the top level. Used when parsing the parameters of lambda expressions, - /// functions, function pointers, and `pat` macro fragments. + /// Corresponds to `Pattern` in RFC 3637 and admits guard patterns at the top level. + /// Used when parsing patterns in all cases where neither `PatternNoTopGuard` nor + /// `PatternNoTopAlt` (see below) are used. + pub fn parse_pat_allow_top_guard( + &mut self, + expected: Option<Expected>, + rc: RecoverComma, + ra: RecoverColon, + rt: CommaRecoveryMode, + ) -> PResult<'a, P<Pat>> { + let pat = self.parse_pat_no_top_guard(expected, rc, ra, rt)?; + + if self.eat_keyword(kw::If) { + let cond = self.parse_expr()?; + // Feature-gate guard patterns + self.psess.gated_spans.gate(sym::guard_patterns, cond.span); + let span = pat.span.to(cond.span); + Ok(self.mk_pat(span, PatKind::Guard(pat, cond))) + } else { + Ok(pat) + } + } + + /// Parses a pattern. + /// + /// Corresponds to `PatternNoTopAlt` in RFC 3637 and does not admit or-patterns + /// or guard patterns at the top level. Used when parsing the parameters of lambda + /// expressions, functions, function pointers, and `pat_param` macro fragments. pub fn parse_pat_no_top_alt( &mut self, expected: Option<Expected>, @@ -112,25 +137,26 @@ impl<'a> Parser<'a> { /// Parses a pattern. /// - /// Corresponds to `top_pat` in RFC 2535 and allows or-pattern at the top level. - /// Used for parsing patterns in all cases when `pat<no_top_alt>` is not used. + /// Corresponds to `PatternNoTopGuard` in RFC 3637 and allows or-patterns, but not + /// guard patterns, at the top level. Used for parsing patterns in `pat` fragments (until + /// the next edition) and `let`, `if let`, and `while let` expressions. /// /// Note that after the FCP in <https://github.com/rust-lang/rust/issues/81415>, /// a leading vert is allowed in nested or-patterns, too. This allows us to /// simplify the grammar somewhat. - pub fn parse_pat_allow_top_alt( + pub fn parse_pat_no_top_guard( &mut self, expected: Option<Expected>, rc: RecoverComma, ra: RecoverColon, rt: CommaRecoveryMode, ) -> PResult<'a, P<Pat>> { - self.parse_pat_allow_top_alt_inner(expected, rc, ra, rt, None).map(|(pat, _)| pat) + self.parse_pat_no_top_guard_inner(expected, rc, ra, rt, None).map(|(pat, _)| pat) } /// Returns the pattern and a bool indicating whether we recovered from a trailing vert (true = /// recovered). - fn parse_pat_allow_top_alt_inner( + fn parse_pat_no_top_guard_inner( &mut self, expected: Option<Expected>, rc: RecoverComma, @@ -231,7 +257,7 @@ impl<'a> Parser<'a> { // We use `parse_pat_allow_top_alt` regardless of whether we actually want top-level // or-patterns so that we can detect when a user tries to use it. This allows us to print a // better error message. - let (pat, trailing_vert) = self.parse_pat_allow_top_alt_inner( + let (pat, trailing_vert) = self.parse_pat_no_top_guard_inner( expected, rc, RecoverColon::No, @@ -696,7 +722,7 @@ impl<'a> Parser<'a> { } else if self.check(&token::OpenDelim(Delimiter::Bracket)) { // Parse `[pat, pat,...]` as a slice pattern. let (pats, _) = self.parse_delim_comma_seq(Delimiter::Bracket, |p| { - p.parse_pat_allow_top_alt( + p.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::No, @@ -944,7 +970,7 @@ impl<'a> Parser<'a> { let open_paren = self.token.span; let (fields, trailing_comma) = self.parse_paren_comma_seq(|p| { - p.parse_pat_allow_top_alt( + p.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::No, @@ -1345,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)) @@ -1361,7 +1387,7 @@ impl<'a> Parser<'a> { path: Path, ) -> PResult<'a, PatKind> { let (fields, _) = self.parse_paren_comma_seq(|p| { - p.parse_pat_allow_top_alt( + p.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::No, @@ -1396,7 +1422,7 @@ impl<'a> Parser<'a> { self.parse_builtin(|self_, _lo, ident| { Ok(match ident.name { // builtin#deref(PAT) - sym::deref => Some(ast::PatKind::Deref(self_.parse_pat_allow_top_alt( + sym::deref => Some(ast::PatKind::Deref(self_.parse_pat_allow_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, @@ -1671,7 +1697,7 @@ impl<'a> Parser<'a> { // Parsing a pattern of the form `fieldname: pat`. let fieldname = self.parse_field_name()?; self.bump(); - let pat = self.parse_pat_allow_top_alt( + let pat = self.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::No, diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 2f19a9b6b20..6a7029a8f1c 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -469,7 +469,7 @@ impl<'a> Parser<'a> { PathStyle::Pat if let Ok(_) = self .parse_paren_comma_seq(|p| { - p.parse_pat_allow_top_alt( + p.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::No, diff --git a/compiler/rustc_parse/src/parser/tests.rs b/compiler/rustc_parse/src/parser/tests.rs index decaecd2682..1813960dad0 100644 --- a/compiler/rustc_parse/src/parser/tests.rs +++ b/compiler/rustc_parse/src/parser/tests.rs @@ -1,3 +1,5 @@ +#![cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] + use std::assert_matches::assert_matches; use std::io::prelude::*; use std::iter::Peekable; diff --git a/compiler/rustc_parse/src/parser/tokenstream/tests.rs b/compiler/rustc_parse/src/parser/tokenstream/tests.rs index efe266f5290..b13b68c266a 100644 --- a/compiler/rustc_parse/src/parser/tokenstream/tests.rs +++ b/compiler/rustc_parse/src/parser/tokenstream/tests.rs @@ -1,3 +1,5 @@ +#![cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] + use rustc_ast::token::{self, IdentIsRaw}; use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_span::{BytePos, Span, Symbol, create_default_session_globals_then}; diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 8cff23c2e32..f696074e66a 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -5,7 +5,7 @@ use rustc_ast::{ self as ast, BareFnTy, BoundAsyncness, BoundConstness, BoundPolarity, DUMMY_NODE_ID, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime, MacCall, MutTy, Mutability, Pinnedness, PolyTraitRef, PreciseCapturingArg, TraitBoundModifiers, TraitObjectSyntax, Ty, - TyKind, + TyKind, UnsafeBinderTy, }; use rustc_errors::{Applicability, PResult}; use rustc_span::symbol::{Ident, kw, sym}; @@ -348,6 +348,10 @@ impl<'a> Parser<'a> { TyKind::Err(guar) } } + } else if self.check_keyword(kw::Unsafe) + && self.look_ahead(1, |tok| matches!(tok.kind, token::Lt)) + { + self.parse_unsafe_binder_ty()? } else { let msg = format!("expected type, found {}", super::token_descr(&self.token)); let mut err = self.dcx().struct_span_err(lo, msg); @@ -369,6 +373,19 @@ impl<'a> Parser<'a> { if allow_qpath_recovery { self.maybe_recover_from_bad_qpath(ty) } else { Ok(ty) } } + fn parse_unsafe_binder_ty(&mut self) -> PResult<'a, TyKind> { + let lo = self.token.span; + assert!(self.eat_keyword(kw::Unsafe)); + self.expect_lt()?; + let generic_params = self.parse_generic_params()?; + self.expect_gt()?; + let inner_ty = self.parse_ty()?; + let span = lo.to(self.prev_token.span); + self.psess.gated_spans.gate(sym::unsafe_binders, span); + + Ok(TyKind::UnsafeBinder(P(UnsafeBinderTy { generic_params, inner_ty }))) + } + /// Parses either: /// - `(TYPE)`, a parenthesized type. /// - `(TYPE,)`, a tuple with a single field of type TYPE. 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..fd133ba878e 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 diff --git a/compiler/rustc_passes/src/abi_test.rs b/compiler/rustc_passes/src/abi_test.rs index 4db8584b884..ff3ae65fbea 100644 --- a/compiler/rustc_passes/src/abi_test.rs +++ b/compiler/rustc_passes/src/abi_test.rs @@ -1,4 +1,4 @@ -use rustc_ast::Attribute; +use rustc_hir::Attribute; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_middle::span_bug; diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 074fe77324f..4d66f5a5387 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; @@ -715,7 +713,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 +731,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 +912,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 +940,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 +1181,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 +1282,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 +2144,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 +2602,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/debugger_visualizer.rs b/compiler/rustc_passes/src/debugger_visualizer.rs index fec149c8c43..062d56a79a0 100644 --- a/compiler/rustc_passes/src/debugger_visualizer.rs +++ b/compiler/rustc_passes/src/debugger_visualizer.rs @@ -1,7 +1,6 @@ //! Detecting usage of the `#[debugger_visualizer]` attribute. use rustc_ast::Attribute; -use rustc_data_structures::sync::Lrc; use rustc_expand::base::resolve_path; use rustc_middle::middle::debugger_visualizer::{DebuggerVisualizerFile, DebuggerVisualizerType}; use rustc_middle::query::{LocalCrate, Providers}; @@ -49,10 +48,10 @@ impl DebuggerVisualizerCollector<'_> { } }; - match std::fs::read(&file) { - Ok(contents) => { + match self.sess.source_map().load_binary_file(&file) { + Ok((source, _)) => { self.visualizers.push(DebuggerVisualizerFile::new( - Lrc::from(contents), + source, visualizer_type, file, )); diff --git a/compiler/rustc_passes/src/diagnostic_items.rs b/compiler/rustc_passes/src/diagnostic_items.rs index 1c3d3bf3dea..071e537233b 100644 --- a/compiler/rustc_passes/src/diagnostic_items.rs +++ b/compiler/rustc_passes/src/diagnostic_items.rs @@ -9,9 +9,8 @@ //! //! * 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}; @@ -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/errors.rs b/compiler/rustc_passes/src/errors.rs index fdc7e1bba2f..f71d5284052 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -216,18 +216,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 b8f66a2b2ec..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, @@ -315,9 +319,40 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { fn visit_expr(&mut self, e: &'v hir::Expr<'v>) { record_variants!((self, e, e.kind, Some(e.hir_id), hir, Expr, ExprKind), [ - ConstBlock, Array, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type, DropTemps, - Let, If, Loop, Match, Closure, Block, Assign, AssignOp, Field, Index, Path, AddrOf, - Break, Continue, Ret, Become, InlineAsm, OffsetOf, Struct, Repeat, Yield, Err + ConstBlock, + Array, + Call, + MethodCall, + Tup, + Binary, + Unary, + Lit, + Cast, + Type, + DropTemps, + Let, + If, + Loop, + Match, + Closure, + Block, + Assign, + AssignOp, + Field, + Index, + Path, + AddrOf, + Break, + Continue, + Ret, + Become, + InlineAsm, + OffsetOf, + Struct, + Repeat, + Yield, + UnsafeBinderCast, + Err ]); hir_visit::walk_expr(self, e) } @@ -335,11 +370,12 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { Ptr, Ref, BareFn, + UnsafeBinder, Never, Tup, - AnonAdt, Path, OpaqueDef, + TraitAscription, TraitObject, Typeof, Infer, @@ -468,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); } @@ -555,6 +591,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { Slice, Rest, Never, + Guard, Paren, MacCall, Err @@ -571,7 +608,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { If, While, ForLoop, Loop, Match, Closure, Block, Await, TryBlock, Assign, AssignOp, Field, Index, Range, Underscore, Path, AddrOf, Break, Continue, Ret, InlineAsm, FormatArgs, OffsetOf, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet, - Become, IncludedBytes, Gen, Err, Dummy + Become, IncludedBytes, Gen, UnsafeBinderCast, Err, Dummy ] ); ast_visit::walk_expr(self, e) @@ -585,6 +622,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { Ref, PinnedRef, BareFn, + UnsafeBinder, Never, Tup, Path, diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs index bb90b5a1e31..dbdc383504c 100644 --- a/compiler/rustc_passes/src/layout_test.rs +++ b/compiler/rustc_passes/src/layout_test.rs @@ -1,5 +1,5 @@ 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; diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs index 8a360c017ad..07f7a1c8f2a 100644 --- a/compiler/rustc_passes/src/lib_features.rs +++ b/compiler/rustc_passes/src/lib_features.rs @@ -4,8 +4,8 @@ //! 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}; diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 9cd95a0b02d..034f7308c4a 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -447,6 +447,7 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { | hir::ExprKind::InlineAsm(..) | hir::ExprKind::OffsetOf(..) | hir::ExprKind::Type(..) + | hir::ExprKind::UnsafeBinderCast(..) | hir::ExprKind::Err(_) | hir::ExprKind::Path(hir::QPath::TypeRelative(..)) | hir::ExprKind::Path(hir::QPath::LangItem(..)) => {} @@ -1007,7 +1008,12 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { hir::ExprKind::Array(exprs) => self.propagate_through_exprs(exprs, succ), hir::ExprKind::Struct(_, fields, ref with_expr) => { - let succ = self.propagate_through_opt_expr(with_expr.as_deref(), succ); + let succ = match with_expr { + hir::StructTailExpr::Base(base) => { + self.propagate_through_opt_expr(Some(base), succ) + } + hir::StructTailExpr::None | hir::StructTailExpr::DefaultFields(_) => succ, + }; fields .iter() .rev() @@ -1046,6 +1052,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { hir::ExprKind::AddrOf(_, _, ref e) | hir::ExprKind::Cast(ref e, _) | hir::ExprKind::Type(ref e, _) + | hir::ExprKind::UnsafeBinderCast(_, ref e, _) | hir::ExprKind::DropTemps(ref e) | hir::ExprKind::Unary(_, ref e) | hir::ExprKind::Repeat(ref e, _) => self.propagate_through_expr(e, succ), @@ -1438,6 +1445,7 @@ fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr<'tcx>) { | hir::ExprKind::Path(_) | hir::ExprKind::Yield(..) | hir::ExprKind::Type(..) + | hir::ExprKind::UnsafeBinderCast(..) | hir::ExprKind::Err(_) => {} } } diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs index b2f8d7dadff..766a9e1bdad 100644 --- a/compiler/rustc_passes/src/naked_functions.rs +++ b/compiler/rustc_passes/src/naked_functions.rs @@ -186,6 +186,7 @@ impl CheckInlineAssembly { | ExprKind::Lit(..) | ExprKind::Cast(..) | ExprKind::Type(..) + | ExprKind::UnsafeBinderCast(..) | ExprKind::Loop(..) | ExprKind::Match(..) | ExprKind::If(..) diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 2809ad453ff..96614a7a128 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}; @@ -199,7 +199,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 +224,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 +280,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 +318,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 +784,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 c845d60ac07..6357efa5bae 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -41,7 +41,7 @@ use rustc_span::Span; use rustc_span::hygiene::Transparency; use rustc_span::symbol::{Ident, 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" } @@ -947,6 +947,25 @@ impl<'tcx> NamePrivacyVisitor<'tcx> { }); } } + + fn check_expanded_fields( + &mut self, + adt: ty::AdtDef<'tcx>, + variant: &'tcx ty::VariantDef, + fields: &[hir::ExprField<'tcx>], + hir_id: hir::HirId, + span: Span, + ) { + for (vf_index, variant_field) in variant.fields.iter_enumerated() { + let field = + fields.iter().find(|f| self.typeck_results().field_index(f.hir_id) == vf_index); + let (hir_id, use_ctxt, span) = match field { + Some(field) => (field.hir_id, field.ident.span, field.span), + None => (hir_id, span, span), + }; + self.check_field(hir_id, use_ctxt, span, adt, variant_field, true); + } + } } impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> { @@ -966,25 +985,29 @@ impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> { let res = self.typeck_results().qpath_res(qpath, expr.hir_id); let adt = self.typeck_results().expr_ty(expr).ty_adt_def().unwrap(); let variant = adt.variant_of_res(res); - if let Some(base) = *base { - // If the expression uses FRU we need to make sure all the unmentioned fields - // are checked for privacy (RFC 736). Rather than computing the set of - // unmentioned fields, just check them all. - for (vf_index, variant_field) in variant.fields.iter_enumerated() { - let field = fields - .iter() - .find(|f| self.typeck_results().field_index(f.hir_id) == vf_index); - let (hir_id, use_ctxt, span) = match field { - Some(field) => (field.hir_id, field.ident.span, field.span), - None => (base.hir_id, base.span, base.span), - }; - self.check_field(hir_id, use_ctxt, span, adt, variant_field, true); + match *base { + hir::StructTailExpr::Base(base) => { + // If the expression uses FRU we need to make sure all the unmentioned fields + // are checked for privacy (RFC 736). Rather than computing the set of + // unmentioned fields, just check them all. + self.check_expanded_fields(adt, variant, fields, base.hir_id, base.span); } - } else { - for field in fields { - let (hir_id, use_ctxt, span) = (field.hir_id, field.ident.span, field.span); - let index = self.typeck_results().field_index(field.hir_id); - self.check_field(hir_id, use_ctxt, span, adt, &variant.fields[index], false); + hir::StructTailExpr::DefaultFields(span) => { + self.check_expanded_fields(adt, variant, fields, expr.hir_id, span); + } + hir::StructTailExpr::None => { + for field in fields { + let (hir_id, use_ctxt, span) = (field.hir_id, field.ident.span, field.span); + let index = self.typeck_results().field_index(field.hir_id); + self.check_field( + hir_id, + use_ctxt, + span, + adt, + &variant.fields[index], + false, + ); + } } } } diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index a1917fed4d9..d2bb0b3f277 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -198,12 +198,12 @@ trait QueryConfigRestored<'tcx> { -> Self::RestoredValue; } -pub fn query_system<'tcx>( +pub fn query_system<'a>( local_providers: Providers, extern_providers: ExternProviders, - on_disk_cache: Option<OnDiskCache<'tcx>>, + on_disk_cache: Option<OnDiskCache>, incremental: bool, -) -> QuerySystem<'tcx> { +) -> QuerySystem<'a> { QuerySystem { states: Default::default(), arenas: Default::default(), diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index f72f656b2f8..1b12af62ea5 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -349,9 +349,9 @@ pub(crate) fn create_query_frame< hasher.finish::<Hash64>() }) }; - let ty_def_id = key.ty_def_id(); + let def_id_for_ty_in_cycle = key.def_id_for_ty_in_cycle(); - QueryStackFrame::new(description, span, def_id, def_kind, kind, ty_def_id, hash) + QueryStackFrame::new(description, span, def_id, def_kind, kind, def_id_for_ty_in_cycle, hash) } pub(crate) fn encode_query_results<'a, 'tcx, Q>( 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/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index b81386f06ec..82c51193a19 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -33,7 +33,7 @@ pub struct QueryStackFrame { pub def_id: Option<DefId>, pub def_kind: Option<DefKind>, /// A def-id that is extracted from a `Ty` in a query key - pub ty_def_id: Option<DefId>, + pub def_id_for_ty_in_cycle: Option<DefId>, pub dep_kind: DepKind, /// This hash is used to deterministically pick /// a query to remove cycles in the parallel compiler. @@ -48,10 +48,10 @@ impl QueryStackFrame { def_id: Option<DefId>, def_kind: Option<DefKind>, dep_kind: DepKind, - ty_def_id: Option<DefId>, + def_id_for_ty_in_cycle: Option<DefId>, hash: impl FnOnce() -> Hash64, ) -> Self { - Self { description, span, def_id, def_kind, ty_def_id, dep_kind, hash: hash() } + Self { description, span, def_id, def_kind, def_id_for_ty_in_cycle, dep_kind, hash: hash() } } // FIXME(eddyb) Get more valid `Span`s on queries. 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 293cee500bb..f82fd6a6f5f 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; @@ -634,7 +634,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, }; @@ -770,17 +770,21 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { ); } - ItemKind::Mod(..) => { + ItemKind::Mod(.., ref mod_kind) => { let module = self.r.new_module( Some(parent), ModuleKind::Def(def_kind, def_id, ident.name), 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)); + if let ast::ModKind::Loaded(_, _, _, Err(_)) = mod_kind { + self.r.mods_with_parse_errors.insert(def_id); + } + // Descend into the module. self.parent_scope.module = module; } @@ -831,7 +835,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 { @@ -1172,11 +1176,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()) { @@ -1229,7 +1233,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 { @@ -1503,7 +1507,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/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 66492842581..ec442e22204 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -356,6 +356,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 +366,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 +376,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 4c76617a391..7cd56a8fb95 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -2817,11 +2817,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. @@ -3056,7 +3056,7 @@ impl<'tcx> visit::Visitor<'tcx> for UsePlacementFinder { fn visit_item(&mut self, item: &'tcx ast::Item) { if self.target_module == item.id { - if let ItemKind::Mod(_, ModKind::Loaded(items, _inline, mod_spans)) = &item.kind { + if let ItemKind::Mod(_, ModKind::Loaded(items, _inline, mod_spans, _)) = &item.kind { let inject = mod_spans.inject_use_span; if is_span_suitable_for_use_injection(inject) { self.first_legal_span = Some(inject); diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 466e190028a..5906a682f3e 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -1428,6 +1428,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ignore_import: Option<Import<'ra>>, ) -> PathResult<'ra> { let mut module = None; + let mut module_had_parse_errors = false; let mut allow_super = true; let mut second_binding = None; @@ -1471,9 +1472,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { continue; } } - return PathResult::failed(ident, false, finalize.is_some(), module, || { - ("there are too many leading `super` keywords".to_string(), None) - }); + return PathResult::failed( + ident, + false, + finalize.is_some(), + module_had_parse_errors, + module, + || ("there are too many leading `super` keywords".to_string(), None), + ); } if segment_idx == 0 { if name == kw::SelfLower { @@ -1511,19 +1517,26 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Report special messages for path segment keywords in wrong positions. if ident.is_path_segment_keyword() && segment_idx != 0 { - return PathResult::failed(ident, false, finalize.is_some(), module, || { - let name_str = if name == kw::PathRoot { - "crate root".to_string() - } else { - format!("`{name}`") - }; - let label = if segment_idx == 1 && path[0].ident.name == kw::PathRoot { - format!("global paths cannot start with {name_str}") - } else { - format!("{name_str} in paths can only be used in start position") - }; - (label, None) - }); + return PathResult::failed( + ident, + false, + finalize.is_some(), + module_had_parse_errors, + module, + || { + let name_str = if name == kw::PathRoot { + "crate root".to_string() + } else { + format!("`{name}`") + }; + let label = if segment_idx == 1 && path[0].ident.name == kw::PathRoot { + format!("global paths cannot start with {name_str}") + } else { + format!("{name_str} in paths can only be used in start position") + }; + (label, None) + }, + ); } let binding = if let Some(module) = module { @@ -1589,6 +1602,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let maybe_assoc = opt_ns != Some(MacroNS) && PathSource::Type.is_expected(res); if let Some(next_module) = binding.module() { + if self.mods_with_parse_errors.contains(&next_module.def_id()) { + module_had_parse_errors = true; + } module = Some(ModuleOrUniformRoot::Module(next_module)); record_segment_res(self, res); } else if res == Res::ToolMod && !is_last && opt_ns.is_some() { @@ -1614,6 +1630,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ident, is_last, finalize.is_some(), + module_had_parse_errors, module, || { let label = format!( @@ -1637,19 +1654,26 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } - return PathResult::failed(ident, is_last, finalize.is_some(), module, || { - self.report_path_resolution_error( - path, - opt_ns, - parent_scope, - ribs, - ignore_binding, - ignore_import, - module, - segment_idx, - ident, - ) - }); + return PathResult::failed( + ident, + is_last, + finalize.is_some(), + module_had_parse_errors, + module, + || { + self.report_path_resolution_error( + path, + opt_ns, + parent_scope, + ribs, + ignore_binding, + ignore_import, + module, + segment_idx, + ident, + ) + }, + ); } } } diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 51fbcb8ebb8..2ed3f4d2c4f 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -670,9 +670,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { fn throw_unresolved_import_error( &mut self, - errors: Vec<(Import<'_>, UnresolvedImportError)>, + mut errors: Vec<(Import<'_>, UnresolvedImportError)>, glob_error: bool, ) { + errors.retain(|(_import, err)| match err.module { + // Skip `use` errors for `use foo::Bar;` if `foo.rs` has unrecovered parse errors. + Some(def_id) if self.mods_with_parse_errors.contains(&def_id) => false, + _ => true, + }); if errors.is_empty() { return; } @@ -898,6 +903,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { label, suggestion, module, + error_implied_by_parse_error: _, } => { if no_ambiguity { assert!(import.imported_module.get().is_none()); diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index fc92dc8b4ed..61be85d2dff 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -13,11 +13,15 @@ use std::collections::hash_map::Entry; use std::mem::{replace, swap, take}; use rustc_ast::ptr::P; -use rustc_ast::visit::{AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, visit_opt, walk_list}; +use rustc_ast::visit::{ + AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, try_visit, visit_opt, walk_list, +}; 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}; @@ -262,12 +266,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, + } } } @@ -749,8 +758,8 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r self.resolve_block(block); self.parent_scope.macro_rules = old_macro_rules; } - fn visit_anon_const(&mut self, _constant: &'ast AnonConst) { - bug!("encountered anon const without a manual call to `resolve_anon_const`"); + fn visit_anon_const(&mut self, constant: &'ast AnonConst) { + bug!("encountered anon const without a manual call to `resolve_anon_const`: {constant:#?}"); } fn visit_expr(&mut self, expr: &'ast Expr) { self.resolve_expr(expr, None); @@ -885,6 +894,28 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r }, ) } + TyKind::UnsafeBinder(unsafe_binder) => { + // FIXME(unsafe_binder): Better span + let span = ty.span; + self.with_generic_param_rib( + &unsafe_binder.generic_params, + RibKind::Normal, + LifetimeRibKind::Generics { + binder: ty.id, + kind: LifetimeBinderKind::BareFnType, + span, + }, + |this| { + this.visit_generic_params(&unsafe_binder.generic_params, false); + this.with_lifetime_rib( + // We don't allow anonymous `unsafe &'_ ()` binders, + // although I guess we could. + LifetimeRibKind::AnonymousReportError, + |this| this.visit_ty(&unsafe_binder.inner_ty), + ); + }, + ) + } TyKind::Array(element_ty, length) => { self.visit_ty(element_ty); self.resolve_anon_const(length, AnonConstKind::ConstArg(IsRepeatExpr::No)); @@ -1346,7 +1377,24 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r fn visit_field_def(&mut self, f: &'ast FieldDef) { self.resolve_doc_links(&f.attrs, MaybeExported::Ok(f.id)); - visit::walk_field_def(self, f) + let FieldDef { + attrs, + id: _, + span: _, + vis, + ident, + ty, + is_placeholder: _, + default, + safety: _, + } = f; + walk_list!(self, visit_attribute, attrs); + try_visit!(self.visit_vis(vis)); + visit_opt!(self, visit_ident, ident); + try_visit!(self.visit_ty(ty)); + if let Some(v) = &default { + self.resolve_anon_const(v, AnonConstKind::ConstArg(IsRepeatExpr::No)); + } } } @@ -3734,6 +3782,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, @@ -3742,7 +3791,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, @@ -3768,8 +3816,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 @@ -3802,6 +3851,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, @@ -4376,6 +4449,12 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { PathResult::Module(ModuleOrUniformRoot::Module(module)) if !module.is_normal() => { PartialRes::new(module.res().unwrap()) } + // A part of this path references a `mod` that had a parse error. To avoid resolution + // errors for each reference to that module, we don't emit an error for them until the + // `mod` is fixed. this can have a significant cascade effect. + PathResult::Failed { error_implied_by_parse_error: true, .. } => { + PartialRes::new(Res::Err) + } // In `a(::assoc_item)*` `a` cannot be a module. If `a` does resolve to a module we // don't report an error right away, but try to fallback to a primitive type. // So, we are still able to successfully resolve something like @@ -4424,6 +4503,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { suggestion, module, segment_name, + error_implied_by_parse_error: _, } => { return Err(respan(span, ResolutionError::FailedToResolve { segment: Some(segment_name), diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 09f3e848766..1625f69158f 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -430,6 +430,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 +629,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 +1121,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 ca4adce37ce..5c4c401af31 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -190,6 +190,7 @@ impl InvocationParent { enum ImplTraitContext { Existential, Universal, + InBinding, } /// Used for tracking import use types which will be used for redundant import checking. @@ -450,6 +451,7 @@ enum PathResult<'ra> { module: Option<ModuleOrUniformRoot<'ra>>, /// The segment name of target segment_name: Symbol, + error_implied_by_parse_error: bool, }, } @@ -458,6 +460,7 @@ impl<'ra> PathResult<'ra> { ident: Ident, is_error_from_last_segment: bool, finalize: bool, + error_implied_by_parse_error: bool, module: Option<ModuleOrUniformRoot<'ra>>, label_and_suggestion: impl FnOnce() -> (String, Option<Suggestion>), ) -> PathResult<'ra> { @@ -470,6 +473,7 @@ impl<'ra> PathResult<'ra> { suggestion, is_error_from_last_segment, module, + error_implied_by_parse_error, } } } @@ -1198,6 +1202,8 @@ pub struct Resolver<'ra, 'tcx> { /// This is the `Span` where an `extern crate foo;` suggestion would be inserted, if `foo` /// could be a crate that wasn't imported. For diagnostics use only. current_crate_outer_attr_insert_span: Span, + + mods_with_parse_errors: FxHashSet<DefId>, } /// This provides memory for the rest of the crate. The `'ra` lifetime that is @@ -1543,6 +1549,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { impl_unexpanded_invocations: Default::default(), impl_binding_keys: Default::default(), current_crate_outer_attr_insert_span, + mods_with_parse_errors: Default::default(), }; let root_parent_scope = ParentScope::module(graph_root, &resolver); diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 0b4d0e04c29..43e260aa264 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}; @@ -166,7 +167,7 @@ fn soft_custom_inner_attributes_gate(path: &ast::Path, invoc: &Invocation) -> bo [seg1, seg2] if seg1.ident.name == sym::rustfmt && seg2.ident.name == sym::skip => { if let InvocationKind::Attr { item, .. } = &invoc.kind { if let Annotatable::Item(item) = item { - if let ItemKind::Mod(_, ModKind::Loaded(_, Inline::No, _)) = item.kind { + if let ItemKind::Mod(_, ModKind::Loaded(_, Inline::No, _, _)) = item.kind { return true; } } @@ -1126,7 +1127,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..ed421da0241 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -5,6 +5,7 @@ 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; @@ -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>, Vec<A>) { let mut doc_fragments = Vec::new(); - let mut other_attrs = ast::AttrVec::new(); + let mut other_attrs = Vec::<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_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs index 2f4387e412d..34e1c31683a 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs @@ -716,8 +716,7 @@ fn encode_ty_name(tcx: TyCtxt<'_>, def_id: DefId) -> String { | hir::definitions::DefPathData::Use | hir::definitions::DefPathData::GlobalAsm | hir::definitions::DefPathData::MacroNs(..) - | hir::definitions::DefPathData::LifetimeNs(..) - | hir::definitions::DefPathData::AnonAdt => { + | hir::definitions::DefPathData::LifetimeNs(..) => { bug!("encode_ty_name: unexpected `{:?}`", disambiguated_data.data); } }); diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl index 8fd87893a98..eb14b78a003 100644 --- a/compiler/rustc_session/messages.ftl +++ b/compiler/rustc_session/messages.ftl @@ -135,5 +135,6 @@ session_unsupported_crate_type_for_target = session_unsupported_dwarf_version = requested DWARF version {$dwarf_version} is greater than 5 +session_unsupported_reg_struct_return_arch = `-Zreg-struct-return` is only supported on x86 session_unsupported_regparm = `-Zregparm={$regparm}` is unsupported (valid values 0-3) session_unsupported_regparm_arch = `-Zregparm=N` is only supported on x86 diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index cb6d539cdf9..936c2ca87d6 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -472,6 +472,13 @@ impl ToString for DebugInfoCompression { } } +#[derive(Clone, Copy, Debug, PartialEq, Hash)] +pub enum MirStripDebugInfo { + None, + LocalsInTinyFunctions, + AllLocals, +} + /// Split debug-information is enabled by `-C split-debuginfo`, this enum is only used if split /// debug-information is enabled (in either `Packed` or `Unpacked` modes), and the platform /// uses DWARF for debug-information. @@ -2900,10 +2907,10 @@ pub(crate) mod dep_tracking { BranchProtection, CFGuard, CFProtection, CollapseMacroDebuginfo, CoverageOptions, CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FmtDebug, FunctionReturn, InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, LocationDetail, - LtoCli, NextSolverConfig, OomStrategy, OptLevel, OutFileName, OutputType, OutputTypes, - PatchableFunctionEntry, Polonius, RemapPathScopeComponents, ResolveDocLinks, - SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, - WasiExecModel, + LtoCli, MirStripDebugInfo, NextSolverConfig, OomStrategy, OptLevel, OutFileName, + OutputType, OutputTypes, PatchableFunctionEntry, Polonius, RemapPathScopeComponents, + ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, + SymbolManglingVersion, WasiExecModel, }; use crate::lint; use crate::utils::NativeLib; @@ -2971,6 +2978,7 @@ pub(crate) mod dep_tracking { LtoCli, DebugInfo, DebugInfoCompression, + MirStripDebugInfo, CollapseMacroDebuginfo, UnstableFeatures, NativeLib, diff --git a/compiler/rustc_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs index 99d9d5b7665..43cf1324edd 100644 --- a/compiler/rustc_session/src/config/cfg.rs +++ b/compiler/rustc_session/src/config/cfg.rs @@ -371,7 +371,7 @@ impl CheckCfg { ins!(sym::target_feature, empty_values).extend( rustc_target::target_features::all_rust_features() - .filter(|(_, s)| s.is_supported()) + .filter(|(_, s)| s.in_cfg()) .map(|(f, _s)| f) .chain(rustc_target::target_features::RUSTC_SPECIFIC_FEATURES.iter().cloned()) .map(Symbol::intern), diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index 736a5ce0704..6c26a781487 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -490,6 +490,10 @@ pub(crate) struct UnsupportedRegparm { pub(crate) struct UnsupportedRegparmArch; #[derive(Diagnostic)] +#[diag(session_unsupported_reg_struct_return_arch)] +pub(crate) struct UnsupportedRegStructReturnArch; + +#[derive(Diagnostic)] #[diag(session_failed_to_create_profiler)] pub(crate) struct FailedToCreateProfiler { pub(crate) err: String, 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 2c0302bbb2b..3873a203bd3 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -391,6 +391,8 @@ mod desc { pub(crate) const parse_cfprotection: &str = "`none`|`no`|`n` (default), `branch`, `return`, or `full`|`yes`|`y` (equivalent to `branch` and `return`)"; pub(crate) const parse_debuginfo: &str = "either an integer (0, 1, 2), `none`, `line-directives-only`, `line-tables-only`, `limited`, or `full`"; pub(crate) const parse_debuginfo_compression: &str = "one of `none`, `zlib`, or `zstd`"; + pub(crate) const parse_mir_strip_debuginfo: &str = + "one of `none`, `locals-in-tiny-functions`, or `all-locals`"; pub(crate) const parse_collapse_macro_debuginfo: &str = "one of `no`, `external`, or `yes`"; pub(crate) const parse_strip: &str = "either `none`, `debuginfo`, or `symbols`"; pub(crate) const parse_linker_flavor: &str = ::rustc_target::spec::LinkerFlavorCli::one_of(); @@ -925,6 +927,16 @@ pub mod parse { true } + pub(crate) fn parse_mir_strip_debuginfo(slot: &mut MirStripDebugInfo, v: Option<&str>) -> bool { + match v { + Some("none") => *slot = MirStripDebugInfo::None, + Some("locals-in-tiny-functions") => *slot = MirStripDebugInfo::LocalsInTinyFunctions, + Some("all-locals") => *slot = MirStripDebugInfo::AllLocals, + _ => return false, + }; + true + } + pub(crate) fn parse_linker_flavor(slot: &mut Option<LinkerFlavorCli>, v: Option<&str>) -> bool { match v.and_then(LinkerFlavorCli::from_str) { Some(lf) => *slot = Some(lf), @@ -1893,6 +1905,8 @@ options! { #[rustc_lint_opt_deny_field_access("use `Session::mir_opt_level` instead of this field")] mir_opt_level: Option<usize> = (None, parse_opt_number, [TRACKED], "MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds)"), + mir_strip_debuginfo: MirStripDebugInfo = (MirStripDebugInfo::None, parse_mir_strip_debuginfo, [TRACKED], + "Whether to remove some of the MIR debug info from methods. Default: None"), move_size_limit: Option<usize> = (None, parse_opt_number, [TRACKED], "the size at which the `large_assignments` lint starts to be emitted"), mutable_noalias: bool = (true, parse_bool, [TRACKED], @@ -1950,8 +1964,6 @@ options! { (default: PLT is disabled if full relro is enabled on x86_64)"), polonius: Polonius = (Polonius::default(), parse_polonius, [TRACKED], "enable polonius-based borrow-checker (default: no)"), - polymorphize: bool = (false, parse_bool, [TRACKED], - "perform polymorphization analysis"), pre_link_arg: (/* redirected to pre_link_args */) = ((), parse_string_push, [UNTRACKED], "a single extra argument to prepend the linker invocation (can be used several times)"), pre_link_args: Vec<String> = (Vec::new(), parse_list, [UNTRACKED], @@ -1988,6 +2000,9 @@ options! { "enable queries of the dependency graph for regression testing (default: no)"), randomize_layout: bool = (false, parse_bool, [TRACKED], "randomize the layout of types (default: no)"), + reg_struct_return: bool = (false, parse_bool, [TRACKED], + "On x86-32 targets, it overrides the default ABI to return small structs in registers. + It is UNSOUND to link together crates that use different values for this flag!"), regparm: Option<u32> = (None, parse_opt_number, [TRACKED], "On x86-32 targets, setting this to N causes the compiler to pass N arguments \ in registers EAX, EDX, and ECX instead of on the stack for\ diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 120ae9946ea..9f6106f9cfb 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -8,7 +8,6 @@ use std::{env, fmt, io}; use rustc_data_structures::flock; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; -use rustc_data_structures::jobserver::{self, Client}; use rustc_data_structures::profiling::{SelfProfiler, SelfProfilerRef}; use rustc_data_structures::sync::{ DynSend, DynSync, Lock, Lrc, MappedReadGuard, ReadGuard, RwLock, @@ -19,7 +18,6 @@ use rustc_errors::emitter::{ DynEmitter, HumanEmitter, HumanReadableErrorType, OutputTheme, stderr_destination, }; use rustc_errors::json::JsonEmitter; -use rustc_errors::registry::Registry; use rustc_errors::{ Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, Diagnostic, ErrorGuaranteed, FatalAbort, FluentBundle, LazyFallbackBundle, TerminalUrl, fallback_fluent_bundle, @@ -155,16 +153,9 @@ pub struct Session { /// Data about code being compiled, gathered during compilation. pub code_stats: CodeStats, - /// Loaded up early on in the initialization of this `Session` to avoid - /// false positives about a job server in our environment. - pub jobserver: Client, - /// This only ever stores a `LintStore` but we don't want a dependency on that type here. pub lint_store: Option<Lrc<dyn LintStoreMarker>>, - /// Should be set if any lints are registered in `lint_store`. - pub registered_lints: bool, - /// Cap lint level specified by a driver specifically. pub driver_lint_caps: FxHashMap<lint::LintId, lint::Level>, @@ -276,11 +267,11 @@ impl Session { } /// Invoked all the way at the end to finish off diagnostics printing. - pub fn finish_diagnostics(&self, registry: &Registry) -> Option<ErrorGuaranteed> { + pub fn finish_diagnostics(&self) -> Option<ErrorGuaranteed> { let mut guar = None; guar = guar.or(self.check_miri_unleashed_features()); guar = guar.or(self.dcx().emit_stashed_diagnostics()); - self.dcx().print_error_count(registry); + self.dcx().print_error_count(); if self.opts.json_future_incompat { self.dcx().emit_future_breakage_report(); } @@ -880,7 +871,6 @@ impl Session { #[allow(rustc::bad_opt_access)] fn default_emitter( sopts: &config::Options, - registry: rustc_errors::registry::Registry, source_map: Lrc<SourceMap>, bundle: Option<Lrc<FluentBundle>>, fallback_bundle: LazyFallbackBundle, @@ -943,7 +933,6 @@ fn default_emitter( json_rendered, color_config, ) - .registry(Some(registry)) .fluent_bundle(bundle) .ui_testing(sopts.unstable_opts.ui_testing) .ignored_directories_in_source_blocks( @@ -999,11 +988,11 @@ pub fn build_session( sopts.unstable_opts.translate_directionality_markers, ); let source_map = rustc_span::source_map::get_source_map().unwrap(); - let emitter = - default_emitter(&sopts, registry, Lrc::clone(&source_map), bundle, fallback_bundle); + let emitter = default_emitter(&sopts, Lrc::clone(&source_map), bundle, fallback_bundle); - let mut dcx = - DiagCtxt::new(emitter).with_flags(sopts.unstable_opts.dcx_flags(can_emit_warnings)); + let mut dcx = DiagCtxt::new(emitter) + .with_flags(sopts.unstable_opts.dcx_flags(can_emit_warnings)) + .with_registry(registry); if let Some(ice_file) = ice_file { dcx = dcx.with_ice_file(ice_file); } @@ -1075,9 +1064,7 @@ pub fn build_session( incr_comp_session: RwLock::new(IncrCompSession::NotInitialized), prof, code_stats: Default::default(), - jobserver: jobserver::client(), lint_store: None, - registered_lints: false, driver_lint_caps, ctfe_backtrace, miri_unleashed_features: Lock::new(Default::default()), @@ -1305,6 +1292,11 @@ fn validate_commandline_args_with_session_available(sess: &Session) { sess.dcx().emit_err(errors::UnsupportedRegparmArch); } } + if sess.opts.unstable_opts.reg_struct_return { + if sess.target.arch != "x86" { + sess.dcx().emit_err(errors::UnsupportedRegStructReturnArch); + } + } // The code model check applies to `thunk` and `thunk-extern`, but not `thunk-inline`, so it is // kept as a `match` to force a change if new ones are added, even if we currently only support 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/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs index dec2a77619b..c465367b6b9 100644 --- a/compiler/rustc_smir/src/rustc_internal/internal.rs +++ b/compiler/rustc_smir/src/rustc_internal/internal.rs @@ -141,6 +141,10 @@ impl RustcInternal for RigidTy { RigidTy::Coroutine(def, args, _mov) => { rustc_ty::TyKind::Coroutine(def.0.internal(tables, tcx), args.internal(tables, tcx)) } + RigidTy::CoroutineClosure(def, args) => rustc_ty::TyKind::CoroutineClosure( + def.0.internal(tables, tcx), + args.internal(tables, tcx), + ), RigidTy::CoroutineWitness(def, args) => rustc_ty::TyKind::CoroutineWitness( def.0.internal(tables, tcx), args.internal(tables, tcx), diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index d4b034ea219..dc63ea1999e 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -107,6 +107,10 @@ impl<'tcx> Tables<'tcx> { stable_mir::ty::CoroutineDef(self.create_def_id(did)) } + pub fn coroutine_closure_def(&mut self, did: DefId) -> stable_mir::ty::CoroutineClosureDef { + stable_mir::ty::CoroutineClosureDef(self.create_def_id(did)) + } + pub fn alias_def(&mut self, did: DefId) -> stable_mir::ty::AliasDef { stable_mir::ty::AliasDef(self.create_def_id(did)) } @@ -314,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; @@ -342,8 +346,9 @@ macro_rules! run_driver { /// Runs the compiler against given target and tests it with `test_function` pub fn run(&mut self) -> Result<C, CompilerError<B>> { - let compiler_result = rustc_driver::catch_fatal_errors(|| { - RunCompiler::new(&self.args.clone(), self).run() + let compiler_result = rustc_driver::catch_fatal_errors(|| -> interface::Result::<()> { + RunCompiler::new(&self.args.clone(), self).run(); + Ok(()) }); match (compiler_result, self.result.take()) { (Ok(Ok(())), Some(ControlFlow::Continue(value))) => Ok(value), diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index 9025b47c422..4b172517fd5 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -255,7 +255,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { attr.iter().map(|seg| rustc_span::symbol::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 +266,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/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index fcdf8703b14..a5a17b4b573 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -565,8 +565,11 @@ impl<'tcx> Stable<'tcx> for mir::AggregateKind<'tcx> { tables.tcx.coroutine_movability(*def_id).stable(tables), ) } - mir::AggregateKind::CoroutineClosure(..) => { - todo!("FIXME(async_closures): Lower these to SMIR") + mir::AggregateKind::CoroutineClosure(def_id, generic_args) => { + stable_mir::mir::AggregateKind::CoroutineClosure( + tables.coroutine_closure_def(*def_id), + generic_args.stable(tables), + ) } mir::AggregateKind::RawPtr(ty, mutability) => { stable_mir::mir::AggregateKind::RawPtr(ty.stable(tables), mutability.stable(tables)) diff --git a/compiler/rustc_span/src/edit_distance/tests.rs b/compiler/rustc_span/src/edit_distance/tests.rs index c9c7a1f1bf2..9540f934d7e 100644 --- a/compiler/rustc_span/src/edit_distance/tests.rs +++ b/compiler/rustc_span/src/edit_distance/tests.rs @@ -1,3 +1,5 @@ +#![cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] + use super::*; #[test] diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index a5826137181..3cdae437b7d 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -1163,6 +1163,9 @@ pub enum DesugaringKind { WhileLoop, /// `async Fn()` bound modifier BoundModifier, + /// Marks a `&raw const *_1` needed as part of getting the length of a mutable + /// slice for the bounds check, so that MIRI's retag handling can recognize it. + IndexBoundsCheckReborrow, } impl DesugaringKind { @@ -1179,6 +1182,7 @@ impl DesugaringKind { DesugaringKind::ForLoop => "`for` loop", DesugaringKind::WhileLoop => "`while` loop", DesugaringKind::BoundModifier => "trait bound modifier", + DesugaringKind::IndexBoundsCheckReborrow => "slice indexing", } } } diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 57c645bfaba..6e25de847fc 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)] @@ -51,6 +50,7 @@ pub mod source_map; use source_map::{SourceMap, SourceMapInputs}; pub use self::caching_source_map_view::CachingSourceMapView; +use crate::fatal_error::FatalError; pub mod edition; use edition::Edition; @@ -522,6 +522,12 @@ impl SpanData { } } +impl Default for SpanData { + fn default() -> Self { + Self { lo: BytePos(0), hi: BytePos(0), ctxt: SyntaxContext::root(), parent: None } + } +} + impl PartialOrd for Span { fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> { PartialOrd::partial_cmp(&self.data(), &rhs.data()) @@ -2614,6 +2620,10 @@ impl ErrorGuaranteed { pub fn unchecked_error_guaranteed() -> Self { ErrorGuaranteed(()) } + + pub fn raise_fatal(self) -> ! { + FatalError.raise() + } } impl<E: rustc_serialize::Encoder> Encodable<E> for ErrorGuaranteed { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 47ceab750cf..7d99ca5a31e 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -306,6 +306,7 @@ symbols! { RwLockWriteGuard, Saturating, SeekFrom, + SelfTy, Send, SeqCst, Sized, @@ -461,6 +462,7 @@ symbols! { async_drop_slice, async_drop_surface_drop_in_place, async_fn, + async_fn_in_dyn_trait, async_fn_in_trait, async_fn_kind_helper, async_fn_kind_upvars, @@ -728,6 +730,7 @@ symbols! { declare_lint_pass, decode, default_alloc_error_handler, + default_field_values, default_fn, default_lib_allocator, default_method_body_is_const, @@ -999,6 +1002,7 @@ symbols! { global_registration, globs, gt, + guard_patterns, half_open_range_patterns, half_open_range_patterns_in_slices, hash, @@ -1187,6 +1191,7 @@ symbols! { loongarch_target_feature, loop_break_value, lt, + m68k_target_feature, macro_at_most_once_rep, macro_attributes_in_derive_output, macro_escape, @@ -1746,7 +1751,6 @@ symbols! { rustc_peek_liveness, rustc_peek_maybe_init, rustc_peek_maybe_uninit, - rustc_polymorphize_error, rustc_preserve_ub_checks, rustc_private, rustc_proc_macro_decls, @@ -2103,6 +2107,7 @@ symbols! { unreachable_macro, unrestricted_attribute_tokens, unsafe_attributes, + unsafe_binders, unsafe_block_in_unsafe_fn, unsafe_cell, unsafe_cell_raw_get, @@ -2126,6 +2131,7 @@ symbols! { unwind_attributes, unwind_safe_trait, unwrap, + unwrap_binder, unwrap_or, use_extern_macros, use_nested_groups, @@ -2184,6 +2190,7 @@ symbols! { windows, windows_subsystem, with_negative_coherence, + wrap_binder, wrapping_add, wrapping_div, wrapping_mul, @@ -2199,6 +2206,7 @@ symbols! { writeln_macro, x86_amx_intrinsics, x87_reg, + x87_target_feature, xer, xmm_reg, xop_target_feature, @@ -2470,13 +2478,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) { diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index 59ccd6dff85..0d6d8488a23 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -2,7 +2,7 @@ use std::fmt::{self, Write}; use std::mem::{self, discriminant}; use rustc_data_structures::stable_hasher::{Hash64, HashStable, StableHasher}; -use rustc_hir::def_id::CrateNum; +use rustc_hir::def_id::{CrateNum, DefId}; use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; use rustc_middle::bug; use rustc_middle::ty::print::{PrettyPrinter, Print, PrintError, Printer}; @@ -378,6 +378,33 @@ impl<'tcx> Printer<'tcx> for SymbolPrinter<'tcx> { Ok(()) } } + + fn print_impl_path( + &mut self, + impl_def_id: DefId, + args: &'tcx [GenericArg<'tcx>], + mut self_ty: Ty<'tcx>, + mut impl_trait_ref: Option<ty::TraitRef<'tcx>>, + ) -> Result<(), PrintError> { + let mut typing_env = ty::TypingEnv::post_analysis(self.tcx, impl_def_id); + if !args.is_empty() { + typing_env.param_env = + ty::EarlyBinder::bind(typing_env.param_env).instantiate(self.tcx, args); + } + + match &mut impl_trait_ref { + Some(impl_trait_ref) => { + assert_eq!(impl_trait_ref.self_ty(), self_ty); + *impl_trait_ref = self.tcx.normalize_erasing_regions(typing_env, *impl_trait_ref); + self_ty = impl_trait_ref.self_ty(); + } + None => { + self_ty = self.tcx.normalize_erasing_regions(typing_env, self_ty); + } + } + + self.default_print_impl_path(impl_def_id, args, self_ty, impl_trait_ref) + } } impl<'tcx> PrettyPrinter<'tcx> for SymbolPrinter<'tcx> { diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index dcbc5f0f76d..4f568a20332 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -256,7 +256,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { }); // Encode impl generic params if the generic parameters contain non-region parameters - // (implying polymorphization is enabled) and this isn't an inherent impl. + // and this isn't an inherent impl. if impl_trait_ref.is_some() && args.iter().any(|a| a.has_non_region_param()) { self.path_generic_args( |this| { @@ -330,9 +330,8 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { ty::Float(FloatTy::F128) => "C4f128", ty::Never => "z", - // Should only be encountered with polymorphization, - // or within the identity-substituted impl header of an - // item nested within an impl item. + // Should only be encountered within the identity-substituted + // impl header of an item nested within an impl item. ty::Param(_) => "p", ty::Bound(..) | ty::Placeholder(_) | ty::Infer(_) | ty::Error(_) => bug!(), @@ -440,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 { @@ -558,9 +557,8 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { let (ct_ty, valtree) = match ct.kind() { ty::ConstKind::Value(ty, val) => (ty, val), - // Should only be encountered with polymorphization, - // or within the identity-substituted impl header of an - // item nested within an impl item. + // Should only be encountered within the identity-substituted + // impl header of an item nested within an impl item. ty::ConstKind::Param(_) => { // Never cached (single-character). self.push("p"); @@ -774,8 +772,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { | DefPathData::GlobalAsm | DefPathData::Impl | DefPathData::MacroNs(_) - | DefPathData::LifetimeNs(_) - | DefPathData::AnonAdt => { + | DefPathData::LifetimeNs(_) => { bug!("symbol_names: unexpected DefPathData: {:?}", disambiguated_data.data) } }; diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index fb0fe402934..746e8173807 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -661,7 +661,9 @@ impl<'a, Ty> FnAbi<'a, Ty> { } _ => (x86::Flavor::General, None), }; - x86::compute_abi_info(cx, self, x86::X86Options { flavor, regparm }); + let reg_struct_return = cx.x86_abi_opt().reg_struct_return; + let opts = x86::X86Options { flavor, regparm, reg_struct_return }; + x86::compute_abi_info(cx, self, opts); } "x86_64" => match abi { spec::abi::Abi::SysV64 { .. } => x86_64::compute_abi_info(cx, self), diff --git a/compiler/rustc_target/src/callconv/powerpc64.rs b/compiler/rustc_target/src/callconv/powerpc64.rs index 71e533b8cc5..3a71592cbe0 100644 --- a/compiler/rustc_target/src/callconv/powerpc64.rs +++ b/compiler/rustc_target/src/callconv/powerpc64.rs @@ -99,7 +99,7 @@ where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout + HasTargetSpec, { - let abi = if cx.target_spec().env == "musl" { + let abi = if cx.target_spec().env == "musl" || cx.target_spec().os == "freebsd" { ELFv2 } else if cx.target_spec().os == "aix" { AIX diff --git a/compiler/rustc_target/src/callconv/x86.rs b/compiler/rustc_target/src/callconv/x86.rs index a5af975d4d2..cd8465c09ca 100644 --- a/compiler/rustc_target/src/callconv/x86.rs +++ b/compiler/rustc_target/src/callconv/x86.rs @@ -14,6 +14,7 @@ pub(crate) enum Flavor { pub(crate) struct X86Options { pub flavor: Flavor, pub regparm: Option<u32>, + pub reg_struct_return: bool, } pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, opts: X86Options) @@ -31,7 +32,7 @@ where // https://www.angelcode.com/dev/callconv/callconv.html // Clang's ABI handling is in lib/CodeGen/TargetInfo.cpp let t = cx.target_spec(); - if t.abi_return_struct_as_int { + if t.abi_return_struct_as_int || opts.reg_struct_return { // According to Clang, everyone but MSVC returns single-element // float aggregates directly in a floating-point register. if !t.is_like_msvc && fn_abi.ret.layout.is_single_fp_element(cx) { diff --git a/compiler/rustc_target/src/spec/base/linux_musl.rs b/compiler/rustc_target/src/spec/base/linux_musl.rs index e020bb85238..1a854fe362d 100644 --- a/compiler/rustc_target/src/spec/base/linux_musl.rs +++ b/compiler/rustc_target/src/spec/base/linux_musl.rs @@ -8,8 +8,5 @@ pub(crate) fn opts() -> TargetOptions { base.post_link_objects_self_contained = crt_objects::post_musl_self_contained(); base.link_self_contained = LinkSelfContainedDefault::InferredForMusl; - // These targets statically link libc by default - base.crt_static_default = true; - base } diff --git a/compiler/rustc_target/src/spec/base/windows_gnullvm.rs b/compiler/rustc_target/src/spec/base/windows_gnullvm.rs index d5acd37092a..4f370ec8bd0 100644 --- a/compiler/rustc_target/src/spec/base/windows_gnullvm.rs +++ b/compiler/rustc_target/src/spec/base/windows_gnullvm.rs @@ -42,6 +42,8 @@ pub(crate) fn opts() -> TargetOptions { eh_frame_header: false, no_default_libraries: false, has_thread_local: true, + crt_static_allows_dylibs: true, + crt_static_respected: true, // FIXME(davidtwco): Support Split DWARF on Windows GNU - may require LLVM changes to // output DWO, despite using DWARF, doesn't use ELF.. debuginfo_kind: DebuginfoKind::Pdb, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index a2e9430830a..f7706f094af 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -2117,6 +2117,8 @@ pub struct X86Abi { /// On x86-32 targets, the regparm N causes the compiler to pass arguments /// in registers EAX, EDX, and ECX instead of on the stack. pub regparm: Option<u32>, + /// Override the default ABI to return small structs in registers + pub reg_struct_return: bool, } pub trait HasX86AbiOpt { @@ -2211,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>, @@ -2601,6 +2607,14 @@ impl TargetOptions { .collect(); } } + + pub(crate) fn has_feature(&self, search_feature: &str) -> bool { + 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)) + } } impl Default for TargetOptions { @@ -3152,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/aarch64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_musl.rs index bb65048a56d..4fefdfa5c5e 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_musl.rs @@ -12,6 +12,9 @@ pub(crate) fn target() -> Target { | SanitizerSet::MEMORY | SanitizerSet::THREAD; + // FIXME(compiler-team#422): musl targets should be dynamically linked by default. + base.crt_static_default = true; + Target { llvm_target: "aarch64-unknown-linux-musl".into(), metadata: crate::spec::TargetMetadata { diff --git a/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabi.rs b/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabi.rs index b96d8455a5b..416bb5432fd 100644 --- a/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabi.rs +++ b/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabi.rs @@ -22,6 +22,8 @@ pub(crate) fn target() -> Target { features: "+strict-align,+v6".into(), max_atomic_width: Some(64), mcount: "\u{1}mcount".into(), + // FIXME(compiler-team#422): musl targets should be dynamically linked by default. + crt_static_default: true, ..base::linux_musl::opts() }, } diff --git a/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabihf.rs b/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabihf.rs index 3418a7090d3..909eb78f698 100644 --- a/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabihf.rs +++ b/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabihf.rs @@ -22,6 +22,8 @@ pub(crate) fn target() -> Target { features: "+strict-align,+v6,+vfp2,-d32".into(), max_atomic_width: Some(64), mcount: "\u{1}mcount".into(), + // FIXME(compiler-team#422): musl targets should be dynamically linked by default. + crt_static_default: true, ..base::linux_musl::opts() }, } diff --git a/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_musleabi.rs b/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_musleabi.rs index 1bcd090b9f2..5e3ad42e5a4 100644 --- a/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_musleabi.rs +++ b/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_musleabi.rs @@ -23,6 +23,8 @@ pub(crate) fn target() -> Target { max_atomic_width: Some(32), mcount: "\u{1}mcount".into(), has_thumb_interworking: true, + // FIXME(compiler-team#422): musl targets should be dynamically linked by default. + crt_static_default: true, ..base::linux_musl::opts() }, } diff --git a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabi.rs b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabi.rs index 16923497325..843adcfc711 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabi.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabi.rs @@ -26,6 +26,8 @@ pub(crate) fn target() -> Target { features: "+v7,+thumb2,+soft-float,-neon".into(), max_atomic_width: Some(64), mcount: "\u{1}mcount".into(), + // FIXME(compiler-team#422): musl targets should be dynamically linked by default. + crt_static_default: true, ..base::linux_musl::opts() }, } diff --git a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabihf.rs b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabihf.rs index 5adfa0bc2f8..e0630817bc3 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabihf.rs @@ -25,6 +25,8 @@ pub(crate) fn target() -> Target { features: "+v7,+vfp3,-d32,+thumb2,-neon".into(), max_atomic_width: Some(64), mcount: "\u{1}mcount".into(), + // FIXME(compiler-team#422): musl targets should be dynamically linked by default. + crt_static_default: true, ..base::linux_musl::opts() }, } diff --git a/compiler/rustc_target/src/spec/targets/i586_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/i586_unknown_linux_musl.rs index 623422a89ea..8ad93496f3a 100644 --- a/compiler/rustc_target/src/spec/targets/i586_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/i586_unknown_linux_musl.rs @@ -4,5 +4,7 @@ pub(crate) fn target() -> Target { let mut base = super::i686_unknown_linux_musl::target(); base.cpu = "pentium".into(); base.llvm_target = "i586-unknown-linux-musl".into(); + // FIXME(compiler-team#422): musl targets should be dynamically linked by default. + base.crt_static_default = true; base } diff --git a/compiler/rustc_target/src/spec/targets/i686_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/i686_unknown_linux_musl.rs index b805b80b85b..6ba87c732b7 100644 --- a/compiler/rustc_target/src/spec/targets/i686_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/i686_unknown_linux_musl.rs @@ -6,6 +6,8 @@ pub(crate) fn target() -> Target { base.max_atomic_width = Some(64); base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m32", "-Wl,-melf_i386"]); base.stack_probes = StackProbeType::Inline; + // FIXME(compiler-team#422): musl targets should be dynamically linked by default. + base.crt_static_default = true; // The unwinder used by i686-unknown-linux-musl, the LLVM libunwind // implementation, apparently relies on frame pointers existing... somehow. diff --git a/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs b/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs index cc288e042d9..bddcc457498 100644 --- a/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs @@ -8,7 +8,6 @@ pub(crate) fn target() -> Target { base.cpu = "mips64r2".into(); base.features = "+mips64r2,+soft-float".into(); base.max_atomic_width = Some(64); - base.crt_static_default = false; Target { // LLVM doesn't recognize "muslabi64" yet. diff --git a/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs b/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs index 69af2da1100..32f5c79d653 100644 --- a/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs +++ b/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs @@ -22,6 +22,8 @@ pub(crate) fn target() -> Target { abi: "abi64".into(), endian: Endian::Big, mcount: "_mcount".into(), + // FIXME(compiler-team#422): musl targets should be dynamically linked by default. + crt_static_default: true, ..base }, } diff --git a/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs b/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs index 4f50e8b7033..5e7c37fd46c 100644 --- a/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs +++ b/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs @@ -5,6 +5,8 @@ pub(crate) fn target() -> Target { base.cpu = "mips64r2".into(); base.features = "+mips64r2".into(); base.max_atomic_width = Some(64); + // FIXME(compiler-team#422): musl targets should be dynamically linked by default. + base.crt_static_default = true; Target { // LLVM doesn't recognize "muslabi64" yet. llvm_target: "mips64el-unknown-linux-musl".into(), diff --git a/compiler/rustc_target/src/spec/targets/mips_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/mips_unknown_linux_musl.rs index b283b3b1ef7..5076ae345a9 100644 --- a/compiler/rustc_target/src/spec/targets/mips_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/mips_unknown_linux_musl.rs @@ -6,7 +6,6 @@ pub(crate) fn target() -> Target { base.cpu = "mips32r2".into(); base.features = "+mips32r2,+soft-float".into(); base.max_atomic_width = Some(32); - base.crt_static_default = false; Target { llvm_target: "mips-unknown-linux-musl".into(), metadata: crate::spec::TargetMetadata { diff --git a/compiler/rustc_target/src/spec/targets/mipsel_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/mipsel_unknown_linux_musl.rs index 744396aa1ba..339b32b6339 100644 --- a/compiler/rustc_target/src/spec/targets/mipsel_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/mipsel_unknown_linux_musl.rs @@ -5,7 +5,6 @@ pub(crate) fn target() -> Target { base.cpu = "mips32r2".into(); base.features = "+mips32r2,+soft-float".into(); base.max_atomic_width = Some(32); - base.crt_static_default = false; Target { llvm_target: "mipsel-unknown-linux-musl".into(), metadata: crate::spec::TargetMetadata { diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_ibm_aix.rs b/compiler/rustc_target/src/spec/targets/powerpc64_ibm_aix.rs index 0f361054f7a..edabbbf5f00 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_ibm_aix.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_ibm_aix.rs @@ -19,7 +19,7 @@ pub(crate) fn target() -> Target { std: None, // ? }, pointer_width: 64, - data_layout: "E-m:a-Fi64-i64:64-n32:64-S128-v256:256:256-v512:512:512".into(), + data_layout: "E-m:a-Fi64-i64:64-i128:128-n32:64-S128-v256:256:256-v512:512:512".into(), arch: "powerpc64".into(), options: base, } diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_freebsd.rs index 680b024cb6e..4ccb3ee4664 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_freebsd.rs @@ -11,13 +11,13 @@ pub(crate) fn target() -> Target { Target { llvm_target: "powerpc64-unknown-freebsd".into(), metadata: crate::spec::TargetMetadata { - description: Some("PPC64 FreeBSD (ELFv1 and ELFv2)".into()), + description: Some("PPC64 FreeBSD (ELFv2)".into()), tier: Some(3), host_tools: Some(true), std: Some(true), }, pointer_width: 64, - data_layout: "E-m:e-Fn32-i64:64-n32:64".into(), + data_layout: "E-m:e-Fn32-i64:64-i128:128-n32:64".into(), arch: "powerpc64".into(), options: TargetOptions { endian: Endian::Big, mcount: "_mcount".into(), ..base }, } diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_gnu.rs index 5acd9205a8c..351ffa65eba 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_gnu.rs @@ -17,7 +17,7 @@ pub(crate) fn target() -> Target { std: Some(true), }, pointer_width: 64, - data_layout: "E-m:e-Fi64-i64:64-n32:64-S128-v256:256:256-v512:512:512".into(), + data_layout: "E-m:e-Fi64-i64:64-i128:128-n32:64-S128-v256:256:256-v512:512:512".into(), arch: "powerpc64".into(), options: TargetOptions { endian: Endian::Big, mcount: "_mcount".into(), ..base }, } diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs index 62c30aebc51..a54b17c87a7 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs @@ -7,6 +7,8 @@ pub(crate) fn target() -> Target { base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline; + // FIXME(compiler-team#422): musl targets should be dynamically linked by default. + base.crt_static_default = true; Target { llvm_target: "powerpc64-unknown-linux-musl".into(), @@ -17,7 +19,7 @@ pub(crate) fn target() -> Target { std: Some(true), }, pointer_width: 64, - data_layout: "E-m:e-Fn32-i64:64-n32:64-S128-v256:256:256-v512:512:512".into(), + data_layout: "E-m:e-Fn32-i64:64-i128:128-n32:64-S128-v256:256:256-v512:512:512".into(), arch: "powerpc64".into(), options: TargetOptions { endian: Endian::Big, mcount: "_mcount".into(), ..base }, } diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_openbsd.rs index c723847cada..31fbb141524 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_openbsd.rs @@ -17,7 +17,7 @@ pub(crate) fn target() -> Target { std: Some(true), }, pointer_width: 64, - data_layout: "E-m:e-Fn32-i64:64-n32:64".into(), + data_layout: "E-m:e-Fn32-i64:64-i128:128-n32:64".into(), arch: "powerpc64".into(), options: TargetOptions { endian: Endian::Big, mcount: "_mcount".into(), ..base }, } diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs index 1d3d9f1b77d..c37aa8d502a 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs @@ -17,7 +17,7 @@ pub(crate) fn target() -> Target { std: Some(true), }, pointer_width: 64, - data_layout: "E-m:e-Fi64-i64:64-n32:64-S128-v256:256:256-v512:512:512".into(), + data_layout: "E-m:e-Fi64-i64:64-i128:128-n32:64-S128-v256:256:256-v512:512:512".into(), arch: "powerpc64".into(), options: TargetOptions { endian: Endian::Big, ..base }, } diff --git a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_freebsd.rs b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_freebsd.rs index 0c1218bd059..13885c7326a 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_freebsd.rs @@ -16,7 +16,7 @@ pub(crate) fn target() -> Target { std: Some(true), }, pointer_width: 64, - data_layout: "e-m:e-Fn32-i64:64-n32:64".into(), + data_layout: "e-m:e-Fn32-i64:64-i128:128-n32:64".into(), arch: "powerpc64".into(), options: TargetOptions { mcount: "_mcount".into(), ..base }, } diff --git a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_gnu.rs index 23913687a1f..06ae54063ce 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_gnu.rs @@ -16,7 +16,7 @@ pub(crate) fn target() -> Target { std: Some(true), }, pointer_width: 64, - data_layout: "e-m:e-Fn32-i64:64-n32:64-S128-v256:256:256-v512:512:512".into(), + data_layout: "e-m:e-Fn32-i64:64-i128:128-n32:64-S128-v256:256:256-v512:512:512".into(), arch: "powerpc64".into(), options: TargetOptions { mcount: "_mcount".into(), ..base }, } 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 9a76c9c6745..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 @@ -6,6 +6,8 @@ pub(crate) fn target() -> Target { base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline; + // FIXME(compiler-team#422): musl targets should be dynamically linked by default. + base.crt_static_default = true; Target { llvm_target: "powerpc64le-unknown-linux-musl".into(), @@ -16,7 +18,7 @@ pub(crate) fn target() -> Target { std: Some(true), }, pointer_width: 64, - data_layout: "e-m:e-Fn32-i64:64-n32:64-S128-v256:256:256-v512:512:512".into(), + data_layout: "e-m:e-Fn32-i64:64-i128:128-n32:64-S128-v256:256:256-v512:512:512".into(), arch: "powerpc64".into(), options: TargetOptions { mcount: "_mcount".into(), ..base }, } diff --git a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_musl.rs index 5372a83e29a..0cd0ea96ad3 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_musl.rs @@ -6,6 +6,8 @@ pub(crate) fn target() -> Target { base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m32"]); base.max_atomic_width = Some(32); base.stack_probes = StackProbeType::Inline; + // FIXME(compiler-team#422): musl targets should be dynamically linked by default. + base.crt_static_default = true; Target { llvm_target: "powerpc-unknown-linux-musl".into(), diff --git a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_muslspe.rs b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_muslspe.rs index 2305db81c5e..b86c3c2e8e0 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_muslspe.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_muslspe.rs @@ -6,6 +6,8 @@ pub(crate) fn target() -> Target { base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-mspe"]); base.max_atomic_width = Some(32); base.stack_probes = StackProbeType::Inline; + // FIXME(compiler-team#422): musl targets should be dynamically linked by default. + base.crt_static_default = true; Target { llvm_target: "powerpc-unknown-linux-muslspe".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs index 08871d9d72b..a07429bb0c5 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs @@ -23,6 +23,8 @@ pub(crate) fn target() -> Target { llvm_abiname: "ilp32d".into(), max_atomic_width: Some(32), supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]), + // FIXME(compiler-team#422): musl targets should be dynamically linked by default. + crt_static_default: true, ..base::linux_musl::opts() }, } diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs index 397c202ec1a..0a4cc3b8be6 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs @@ -21,7 +21,6 @@ pub(crate) fn target() -> Target { llvm_abiname: "lp64d".into(), max_atomic_width: Some(64), supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]), - crt_static_default: false, ..base::linux_musl::opts() }, } diff --git a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs index 7a78004927b..fbe8c48eca7 100644 --- a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs @@ -12,6 +12,8 @@ pub(crate) fn target() -> Target { base.stack_probes = StackProbeType::Inline; base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD; + // FIXME(compiler-team#422): musl targets should be dynamically linked by default. + base.crt_static_default = true; Target { llvm_target: "s390x-unknown-linux-musl".into(), 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/thumbv7neon_unknown_linux_musleabihf.rs b/compiler/rustc_target/src/spec/targets/thumbv7neon_unknown_linux_musleabihf.rs index 2f539f4f600..a0852b4611e 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv7neon_unknown_linux_musleabihf.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv7neon_unknown_linux_musleabihf.rs @@ -29,6 +29,8 @@ pub(crate) fn target() -> Target { features: "+v7,+thumb-mode,+thumb2,+vfp3,+neon".into(), max_atomic_width: Some(64), mcount: "\u{1}mcount".into(), + // FIXME(compiler-team#422): musl targets should be dynamically linked by default. + crt_static_default: true, ..base::linux_musl::opts() }, } diff --git a/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs b/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs index a70cebbd9c8..bdb1fc55711 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs @@ -32,7 +32,8 @@ pub(crate) fn target() -> Target { std: Some(true), }, pointer_width: 32, - data_layout: "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-f128:64-n32:64-S128-ni:1:10:20".into(), + data_layout: "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-f128:64-n32:64-S128-ni:1:10:20" + .into(), arch: "wasm32".into(), options: opts, } diff --git a/compiler/rustc_target/src/spec/targets/wasm32_unknown_unknown.rs b/compiler/rustc_target/src/spec/targets/wasm32_unknown_unknown.rs index e7165533b9c..96237f20891 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_unknown_unknown.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_unknown_unknown.rs @@ -37,7 +37,7 @@ pub(crate) fn target() -> Target { std: Some(true), }, pointer_width: 32, - data_layout: "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20".into(), + data_layout: "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20".into(), arch: "wasm32".into(), options, } diff --git a/compiler/rustc_target/src/spec/targets/wasm32_wasip1.rs b/compiler/rustc_target/src/spec/targets/wasm32_wasip1.rs index 1cd30f21bec..0862958d05d 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_wasip1.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_wasip1.rs @@ -55,7 +55,7 @@ pub(crate) fn target() -> Target { std: Some(true), }, pointer_width: 32, - data_layout: "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20".into(), + data_layout: "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20".into(), arch: "wasm32".into(), options, } diff --git a/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs b/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs index 19bc5db4d9b..0c2e2bfeda6 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs @@ -66,7 +66,7 @@ pub(crate) fn target() -> Target { std: Some(true), }, pointer_width: 32, - data_layout: "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20".into(), + data_layout: "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20".into(), arch: "wasm32".into(), options, } diff --git a/compiler/rustc_target/src/spec/targets/wasm32_wasip2.rs b/compiler/rustc_target/src/spec/targets/wasm32_wasip2.rs index f06112160d1..3f4618fad5a 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_wasip2.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_wasip2.rs @@ -66,7 +66,7 @@ pub(crate) fn target() -> Target { std: Some(true), }, pointer_width: 32, - data_layout: "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20".into(), + data_layout: "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20".into(), arch: "wasm32".into(), options, } diff --git a/compiler/rustc_target/src/spec/targets/wasm32v1_none.rs b/compiler/rustc_target/src/spec/targets/wasm32v1_none.rs index bf35ae009c6..5c35e9c21d3 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32v1_none.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32v1_none.rs @@ -44,7 +44,7 @@ pub(crate) fn target() -> Target { std: Some(false), }, pointer_width: 32, - data_layout: "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20".into(), + data_layout: "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20".into(), arch: "wasm32".into(), options, } diff --git a/compiler/rustc_target/src/spec/targets/wasm64_unknown_unknown.rs b/compiler/rustc_target/src/spec/targets/wasm64_unknown_unknown.rs index 5ba0fca9f64..22fa26d3cdb 100644 --- a/compiler/rustc_target/src/spec/targets/wasm64_unknown_unknown.rs +++ b/compiler/rustc_target/src/spec/targets/wasm64_unknown_unknown.rs @@ -40,7 +40,7 @@ pub(crate) fn target() -> Target { std: None, // ? }, pointer_width: 64, - data_layout: "e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20".into(), + data_layout: "e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20".into(), arch: "wasm64".into(), options, } 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/spec/targets/x86_64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_musl.rs index 8be0f335db9..8dcdc5be8a9 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_musl.rs @@ -14,6 +14,8 @@ pub(crate) fn target() -> Target { | SanitizerSet::MEMORY | SanitizerSet::THREAD; base.supports_xray = true; + // FIXME(compiler-team#422): musl targets should be dynamically linked by default. + base.crt_static_default = true; Target { llvm_target: "x86_64-unknown-linux-musl".into(), diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 67c047dddfc..acd53ed8008 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -5,6 +5,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_span::symbol::{Symbol, sym}; +use crate::spec::Target; + /// Features that control behaviour of rustc, rather than the codegen. /// These exist globally and are not in the target-specific lists below. pub const RUSTC_SPECIFIC_FEATURES: &[&str] = &["crt-static"]; @@ -15,45 +17,148 @@ pub const RUSTC_SPECIFIC_FEATURES: &[&str] = &["crt-static"]; pub const RUSTC_SPECIAL_FEATURES: &[&str] = &["backchain"]; /// Stability information for target features. -#[derive(Debug, Clone, Copy)] -pub enum Stability { +/// `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)] +pub enum Stability<Toggleability> { /// This target feature is stable, it can be used in `#[target_feature]` and /// `#[cfg(target_feature)]`. - Stable, - /// This target feature is unstable; using it in `#[target_feature]` or `#[cfg(target_feature)]` - /// requires enabling the given nightly feature. - Unstable(Symbol), - /// This feature can not be set via `-Ctarget-feature` or `#[target_feature]`, it can only be set in the basic - /// target definition. Used in particular for features that change the floating-point ABI. + Stable { + /// When enabling/disabling the feature via `-Ctarget-feature` or `#[target_feature]`, + /// determine if that is allowed. + allow_toggle: Toggleability, + }, + /// This target feature is unstable. It is only present in `#[cfg(target_feature)]` on + /// nightly and using it in `#[target_feature]` requires enabling the given nightly feature. + Unstable { + /// This must be a *language* feature, or else rustc will ICE when reporting a missing + /// feature gate! + nightly_feature: Symbol, + /// See `Stable::allow_toggle` comment above. + allow_toggle: Toggleability, + }, + /// This feature can not be set via `-Ctarget-feature` or `#[target_feature]`, it can only be + /// 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 }, } -use Stability::*; -impl<CTX> HashStable<CTX> for Stability { +/// Returns `Ok` if the toggle is allowed, `Err` with an explanation of not. +/// 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<AllowToggleComputed>; + +impl<CTX, Toggleability: HashStable<CTX>> HashStable<CTX> for Stability<Toggleability> { #[inline] fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { std::mem::discriminant(self).hash_stable(hcx, hasher); match self { - Stable => {} - Unstable(sym) => { - sym.hash_stable(hcx, hasher); + Stability::Stable { allow_toggle } => { + allow_toggle.hash_stable(hcx, hasher); + } + Stability::Unstable { nightly_feature, allow_toggle } => { + nightly_feature.hash_stable(hcx, hasher); + allow_toggle.hash_stable(hcx, hasher); + } + Stability::Forbidden { reason } => { + reason.hash_stable(hcx, hasher); } - Forbidden { .. } => {} } } } -impl Stability { - pub fn is_stable(self) -> bool { - matches!(self, Stable) +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 { + !matches!(self, Stability::Forbidden { .. }) + } + + /// Returns the nightly feature that is required to toggle this target feature via + /// `#[target_feature]`/`-Ctarget-feature` or to test it via `cfg(target_feature)`. + /// (For `cfg` we only care whether the feature is nightly or not, we don't require + /// the feature gate to actually be enabled when using a nightly compiler.) + /// + /// 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 { + Stability::Unstable { nightly_feature, .. } => Some(nightly_feature), + Stability::Stable { .. } => None, + Stability::Forbidden { .. } => panic!("forbidden features should not reach this far"), + } + } +} + +impl StabilityUncomputed { + pub fn compute_toggleability(&self, target: &Target) -> StabilityComputed { + use Stability::*; + 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: 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), + } } +} - /// Forbidden features are not supported. - pub fn is_supported(self) -> bool { - !matches!(self, Forbidden { .. }) +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 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 } => 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, _enable| Ok(()) }; +const fn unstable(nightly_feature: Symbol) -> StabilityUncomputed { + Stability::Unstable { nightly_feature, allow_toggle: |_target, _enable| Ok(()) } +} + // Here we list target features that rustc "understands": they can be used in `#[target_feature]` // and `#[cfg(target_feature)]`. They also do not trigger any warnings when used with // `-Ctarget-feature`. @@ -99,181 +204,218 @@ impl Stability { // Both of these are also applied transitively. type ImpliedFeatures = &'static [&'static str]; -const ARM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ +const ARM_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("aclass", Unstable(sym::arm_target_feature), &[]), - ("aes", Unstable(sym::arm_target_feature), &["neon"]), - ("crc", Unstable(sym::arm_target_feature), &[]), - ("d32", Unstable(sym::arm_target_feature), &[]), - ("dotprod", Unstable(sym::arm_target_feature), &["neon"]), - ("dsp", Unstable(sym::arm_target_feature), &[]), - ("fp-armv8", Unstable(sym::arm_target_feature), &["vfp4"]), - ("i8mm", Unstable(sym::arm_target_feature), &["neon"]), - ("mclass", Unstable(sym::arm_target_feature), &[]), - ("neon", Unstable(sym::arm_target_feature), &["vfp3"]), - ("rclass", Unstable(sym::arm_target_feature), &[]), - ("sha2", Unstable(sym::arm_target_feature), &["neon"]), - ("soft-float", Forbidden { reason: "unsound because it changes float ABI" }, &[]), + ("aclass", unstable(sym::arm_target_feature), &[]), + ("aes", unstable(sym::arm_target_feature), &["neon"]), + ("crc", unstable(sym::arm_target_feature), &[]), + ("d32", unstable(sym::arm_target_feature), &[]), + ("dotprod", unstable(sym::arm_target_feature), &["neon"]), + ("dsp", unstable(sym::arm_target_feature), &[]), + ("fp-armv8", unstable(sym::arm_target_feature), &["vfp4"]), + ( + "fpregs", + Stability::Unstable { + nightly_feature: sym::arm_target_feature, + 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") { + Ok(()) + } else { + Err("unsound on hard-float targets because it changes float ABI") + } + }, + }, + &[], + ), + ("i8mm", unstable(sym::arm_target_feature), &["neon"]), + ("mclass", unstable(sym::arm_target_feature), &[]), + ("neon", unstable(sym::arm_target_feature), &["vfp3"]), + ("rclass", unstable(sym::arm_target_feature), &[]), + ("sha2", unstable(sym::arm_target_feature), &["neon"]), + ("soft-float", Stability::Forbidden { reason: "unsound because it changes float ABI" }, &[]), // This is needed for inline assembly, but shouldn't be stabilized as-is // since it should be enabled per-function using #[instruction_set], not // #[target_feature]. - ("thumb-mode", Unstable(sym::arm_target_feature), &[]), - ("thumb2", Unstable(sym::arm_target_feature), &[]), - ("trustzone", Unstable(sym::arm_target_feature), &[]), - ("v5te", Unstable(sym::arm_target_feature), &[]), - ("v6", Unstable(sym::arm_target_feature), &["v5te"]), - ("v6k", Unstable(sym::arm_target_feature), &["v6"]), - ("v6t2", Unstable(sym::arm_target_feature), &["v6k", "thumb2"]), - ("v7", Unstable(sym::arm_target_feature), &["v6t2"]), - ("v8", Unstable(sym::arm_target_feature), &["v7"]), - ("vfp2", Unstable(sym::arm_target_feature), &[]), - ("vfp3", Unstable(sym::arm_target_feature), &["vfp2", "d32"]), - ("vfp4", Unstable(sym::arm_target_feature), &["vfp3"]), - ("virtualization", Unstable(sym::arm_target_feature), &[]), + ("thumb-mode", unstable(sym::arm_target_feature), &[]), + ("thumb2", unstable(sym::arm_target_feature), &[]), + ("trustzone", unstable(sym::arm_target_feature), &[]), + ("v5te", unstable(sym::arm_target_feature), &[]), + ("v6", unstable(sym::arm_target_feature), &["v5te"]), + ("v6k", unstable(sym::arm_target_feature), &["v6"]), + ("v6t2", unstable(sym::arm_target_feature), &["v6k", "thumb2"]), + ("v7", unstable(sym::arm_target_feature), &["v6t2"]), + ("v8", unstable(sym::arm_target_feature), &["v7"]), + ("vfp2", unstable(sym::arm_target_feature), &[]), + ("vfp3", unstable(sym::arm_target_feature), &["vfp2", "d32"]), + ("vfp4", unstable(sym::arm_target_feature), &["vfp3"]), + ("virtualization", unstable(sym::arm_target_feature), &[]), // tidy-alphabetical-end - // FIXME: need to also forbid turning off `fpregs` on hardfloat targets ]; -const AARCH64_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ +const AARCH64_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ // tidy-alphabetical-start // FEAT_AES & FEAT_PMULL - ("aes", Stable, &["neon"]), + ("aes", STABLE, &["neon"]), // FEAT_BF16 - ("bf16", Stable, &[]), + ("bf16", STABLE, &[]), // FEAT_BTI - ("bti", Stable, &[]), + ("bti", STABLE, &[]), // FEAT_CRC - ("crc", Stable, &[]), + ("crc", STABLE, &[]), // FEAT_CSSC - ("cssc", Unstable(sym::aarch64_unstable_target_feature), &[]), + ("cssc", unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_DIT - ("dit", Stable, &[]), + ("dit", STABLE, &[]), // FEAT_DotProd - ("dotprod", Stable, &["neon"]), + ("dotprod", STABLE, &["neon"]), // FEAT_DPB - ("dpb", Stable, &[]), + ("dpb", STABLE, &[]), // FEAT_DPB2 - ("dpb2", Stable, &["dpb"]), + ("dpb2", STABLE, &["dpb"]), // FEAT_ECV - ("ecv", Unstable(sym::aarch64_unstable_target_feature), &[]), + ("ecv", unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_F32MM - ("f32mm", Stable, &["sve"]), + ("f32mm", STABLE, &["sve"]), // FEAT_F64MM - ("f64mm", Stable, &["sve"]), + ("f64mm", STABLE, &["sve"]), // FEAT_FAMINMAX - ("faminmax", Unstable(sym::aarch64_unstable_target_feature), &[]), + ("faminmax", unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_FCMA - ("fcma", Stable, &["neon"]), + ("fcma", STABLE, &["neon"]), // FEAT_FHM - ("fhm", Stable, &["fp16"]), + ("fhm", STABLE, &["fp16"]), // FEAT_FLAGM - ("flagm", Stable, &[]), + ("flagm", STABLE, &[]), // FEAT_FLAGM2 - ("flagm2", Unstable(sym::aarch64_unstable_target_feature), &[]), + ("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"]), + ("fp16", STABLE, &["neon"]), // FEAT_FP8 - ("fp8", Unstable(sym::aarch64_unstable_target_feature), &["faminmax", "lut", "bf16"]), + ("fp8", unstable(sym::aarch64_unstable_target_feature), &["faminmax", "lut", "bf16"]), // FEAT_FP8DOT2 - ("fp8dot2", Unstable(sym::aarch64_unstable_target_feature), &["fp8dot4"]), + ("fp8dot2", unstable(sym::aarch64_unstable_target_feature), &["fp8dot4"]), // FEAT_FP8DOT4 - ("fp8dot4", Unstable(sym::aarch64_unstable_target_feature), &["fp8fma"]), + ("fp8dot4", unstable(sym::aarch64_unstable_target_feature), &["fp8fma"]), // FEAT_FP8FMA - ("fp8fma", Unstable(sym::aarch64_unstable_target_feature), &["fp8"]), + ("fp8fma", unstable(sym::aarch64_unstable_target_feature), &["fp8"]), // FEAT_FRINTTS - ("frintts", Stable, &[]), + ("frintts", STABLE, &[]), // FEAT_HBC - ("hbc", Unstable(sym::aarch64_unstable_target_feature), &[]), + ("hbc", unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_I8MM - ("i8mm", Stable, &[]), + ("i8mm", STABLE, &[]), // FEAT_JSCVT // Rust ties FP and Neon: https://github.com/rust-lang/rust/pull/91608 - ("jsconv", Stable, &["neon"]), + ("jsconv", STABLE, &["neon"]), // FEAT_LOR - ("lor", Stable, &[]), + ("lor", STABLE, &[]), // FEAT_LSE - ("lse", Stable, &[]), + ("lse", STABLE, &[]), // FEAT_LSE128 - ("lse128", Unstable(sym::aarch64_unstable_target_feature), &["lse"]), + ("lse128", unstable(sym::aarch64_unstable_target_feature), &["lse"]), // FEAT_LSE2 - ("lse2", Unstable(sym::aarch64_unstable_target_feature), &[]), + ("lse2", unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_LUT - ("lut", Unstable(sym::aarch64_unstable_target_feature), &[]), + ("lut", unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_MOPS - ("mops", Unstable(sym::aarch64_unstable_target_feature), &[]), + ("mops", unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_MTE & FEAT_MTE2 - ("mte", Stable, &[]), + ("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, &[]), + ("paca", STABLE, &[]), // FEAT_PAUTH (generic authentication) - ("pacg", Stable, &[]), + ("pacg", STABLE, &[]), // FEAT_PAN - ("pan", Stable, &[]), + ("pan", STABLE, &[]), // FEAT_PAuth_LR - ("pauth-lr", Unstable(sym::aarch64_unstable_target_feature), &[]), + ("pauth-lr", unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_PMUv3 - ("pmuv3", Stable, &[]), + ("pmuv3", STABLE, &[]), // FEAT_RNG - ("rand", Stable, &[]), + ("rand", STABLE, &[]), // FEAT_RAS & FEAT_RASv1p1 - ("ras", Stable, &[]), + ("ras", STABLE, &[]), // FEAT_LRCPC - ("rcpc", Stable, &[]), + ("rcpc", STABLE, &[]), // FEAT_LRCPC2 - ("rcpc2", Stable, &["rcpc"]), + ("rcpc2", STABLE, &["rcpc"]), // FEAT_LRCPC3 - ("rcpc3", Unstable(sym::aarch64_unstable_target_feature), &["rcpc2"]), + ("rcpc3", unstable(sym::aarch64_unstable_target_feature), &["rcpc2"]), // FEAT_RDM - ("rdm", Stable, &["neon"]), + ("rdm", STABLE, &["neon"]), // This is needed for inline assembly, but shouldn't be stabilized as-is // since it should be enabled globally using -Zfixed-x18, not // #[target_feature]. // Note that cfg(target_feature = "reserve-x18") is currently not set for // targets that reserve x18 by default. - ("reserve-x18", Unstable(sym::aarch64_unstable_target_feature), &[]), + ("reserve-x18", unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_SB - ("sb", Stable, &[]), + ("sb", STABLE, &[]), // FEAT_SHA1 & FEAT_SHA256 - ("sha2", Stable, &["neon"]), + ("sha2", STABLE, &["neon"]), // FEAT_SHA512 & FEAT_SHA3 - ("sha3", Stable, &["sha2"]), + ("sha3", STABLE, &["sha2"]), // FEAT_SM3 & FEAT_SM4 - ("sm4", Stable, &["neon"]), + ("sm4", STABLE, &["neon"]), // FEAT_SME - ("sme", Unstable(sym::aarch64_unstable_target_feature), &["bf16"]), + ("sme", unstable(sym::aarch64_unstable_target_feature), &["bf16"]), // FEAT_SME_B16B16 - ("sme-b16b16", Unstable(sym::aarch64_unstable_target_feature), &["bf16", "sme2", "sve-b16b16"]), + ("sme-b16b16", unstable(sym::aarch64_unstable_target_feature), &["bf16", "sme2", "sve-b16b16"]), // FEAT_SME_F16F16 - ("sme-f16f16", Unstable(sym::aarch64_unstable_target_feature), &["sme2"]), + ("sme-f16f16", unstable(sym::aarch64_unstable_target_feature), &["sme2"]), // FEAT_SME_F64F64 - ("sme-f64f64", Unstable(sym::aarch64_unstable_target_feature), &["sme"]), + ("sme-f64f64", unstable(sym::aarch64_unstable_target_feature), &["sme"]), // FEAT_SME_F8F16 - ("sme-f8f16", Unstable(sym::aarch64_unstable_target_feature), &["sme-f8f32"]), + ("sme-f8f16", unstable(sym::aarch64_unstable_target_feature), &["sme-f8f32"]), // FEAT_SME_F8F32 - ("sme-f8f32", Unstable(sym::aarch64_unstable_target_feature), &["sme2", "fp8"]), + ("sme-f8f32", unstable(sym::aarch64_unstable_target_feature), &["sme2", "fp8"]), // FEAT_SME_FA64 - ("sme-fa64", Unstable(sym::aarch64_unstable_target_feature), &["sme", "sve2"]), + ("sme-fa64", unstable(sym::aarch64_unstable_target_feature), &["sme", "sve2"]), // FEAT_SME_I16I64 - ("sme-i16i64", Unstable(sym::aarch64_unstable_target_feature), &["sme"]), + ("sme-i16i64", unstable(sym::aarch64_unstable_target_feature), &["sme"]), // FEAT_SME_LUTv2 - ("sme-lutv2", Unstable(sym::aarch64_unstable_target_feature), &[]), + ("sme-lutv2", unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_SME2 - ("sme2", Unstable(sym::aarch64_unstable_target_feature), &["sme"]), + ("sme2", unstable(sym::aarch64_unstable_target_feature), &["sme"]), // FEAT_SME2p1 - ("sme2p1", Unstable(sym::aarch64_unstable_target_feature), &["sme2"]), + ("sme2p1", unstable(sym::aarch64_unstable_target_feature), &["sme2"]), // FEAT_SPE - ("spe", Stable, &[]), + ("spe", STABLE, &[]), // FEAT_SSBS & FEAT_SSBS2 - ("ssbs", Stable, &[]), + ("ssbs", STABLE, &[]), // FEAT_SSVE_FP8FDOT2 - ("ssve-fp8dot2", Unstable(sym::aarch64_unstable_target_feature), &["ssve-fp8dot4"]), + ("ssve-fp8dot2", unstable(sym::aarch64_unstable_target_feature), &["ssve-fp8dot4"]), // FEAT_SSVE_FP8FDOT4 - ("ssve-fp8dot4", Unstable(sym::aarch64_unstable_target_feature), &["ssve-fp8fma"]), + ("ssve-fp8dot4", unstable(sym::aarch64_unstable_target_feature), &["ssve-fp8fma"]), // FEAT_SSVE_FP8FMA - ("ssve-fp8fma", Unstable(sym::aarch64_unstable_target_feature), &["sme2", "fp8"]), + ("ssve-fp8fma", unstable(sym::aarch64_unstable_target_feature), &["sme2", "fp8"]), // FEAT_SVE // It was decided that SVE requires Neon: https://github.com/rust-lang/rust/pull/91608 // @@ -281,46 +423,46 @@ const AARCH64_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // exist together: https://developer.arm.com/documentation/102340/0100/New-features-in-SVE2 // // "For backwards compatibility, Neon and VFP are required in the latest architectures." - ("sve", Stable, &["neon"]), + ("sve", STABLE, &["neon"]), // FEAT_SVE_B16B16 (SVE or SME Z-targeting instructions) - ("sve-b16b16", Unstable(sym::aarch64_unstable_target_feature), &["bf16"]), + ("sve-b16b16", unstable(sym::aarch64_unstable_target_feature), &["bf16"]), // FEAT_SVE2 - ("sve2", Stable, &["sve"]), + ("sve2", STABLE, &["sve"]), // FEAT_SVE_AES & FEAT_SVE_PMULL128 - ("sve2-aes", Stable, &["sve2", "aes"]), + ("sve2-aes", STABLE, &["sve2", "aes"]), // FEAT_SVE2_BitPerm - ("sve2-bitperm", Stable, &["sve2"]), + ("sve2-bitperm", STABLE, &["sve2"]), // FEAT_SVE2_SHA3 - ("sve2-sha3", Stable, &["sve2", "sha3"]), + ("sve2-sha3", STABLE, &["sve2", "sha3"]), // FEAT_SVE2_SM4 - ("sve2-sm4", Stable, &["sve2", "sm4"]), + ("sve2-sm4", STABLE, &["sve2", "sm4"]), // FEAT_SVE2p1 - ("sve2p1", Unstable(sym::aarch64_unstable_target_feature), &["sve2"]), + ("sve2p1", unstable(sym::aarch64_unstable_target_feature), &["sve2"]), // FEAT_TME - ("tme", Stable, &[]), - ("v8.1a", Unstable(sym::aarch64_ver_target_feature), &[ + ("tme", STABLE, &[]), + ("v8.1a", unstable(sym::aarch64_ver_target_feature), &[ "crc", "lse", "rdm", "pan", "lor", "vh", ]), - ("v8.2a", Unstable(sym::aarch64_ver_target_feature), &["v8.1a", "ras", "dpb"]), - ("v8.3a", Unstable(sym::aarch64_ver_target_feature), &[ + ("v8.2a", unstable(sym::aarch64_ver_target_feature), &["v8.1a", "ras", "dpb"]), + ("v8.3a", unstable(sym::aarch64_ver_target_feature), &[ "v8.2a", "rcpc", "paca", "pacg", "jsconv", ]), - ("v8.4a", Unstable(sym::aarch64_ver_target_feature), &["v8.3a", "dotprod", "dit", "flagm"]), - ("v8.5a", Unstable(sym::aarch64_ver_target_feature), &["v8.4a", "ssbs", "sb", "dpb2", "bti"]), - ("v8.6a", Unstable(sym::aarch64_ver_target_feature), &["v8.5a", "bf16", "i8mm"]), - ("v8.7a", Unstable(sym::aarch64_ver_target_feature), &["v8.6a", "wfxt"]), - ("v8.8a", Unstable(sym::aarch64_ver_target_feature), &["v8.7a", "hbc", "mops"]), - ("v8.9a", Unstable(sym::aarch64_ver_target_feature), &["v8.8a", "cssc"]), - ("v9.1a", Unstable(sym::aarch64_ver_target_feature), &["v9a", "v8.6a"]), - ("v9.2a", Unstable(sym::aarch64_ver_target_feature), &["v9.1a", "v8.7a"]), - ("v9.3a", Unstable(sym::aarch64_ver_target_feature), &["v9.2a", "v8.8a"]), - ("v9.4a", Unstable(sym::aarch64_ver_target_feature), &["v9.3a", "v8.9a"]), - ("v9.5a", Unstable(sym::aarch64_ver_target_feature), &["v9.4a"]), - ("v9a", Unstable(sym::aarch64_ver_target_feature), &["v8.5a", "sve2"]), + ("v8.4a", unstable(sym::aarch64_ver_target_feature), &["v8.3a", "dotprod", "dit", "flagm"]), + ("v8.5a", unstable(sym::aarch64_ver_target_feature), &["v8.4a", "ssbs", "sb", "dpb2", "bti"]), + ("v8.6a", unstable(sym::aarch64_ver_target_feature), &["v8.5a", "bf16", "i8mm"]), + ("v8.7a", unstable(sym::aarch64_ver_target_feature), &["v8.6a", "wfxt"]), + ("v8.8a", unstable(sym::aarch64_ver_target_feature), &["v8.7a", "hbc", "mops"]), + ("v8.9a", unstable(sym::aarch64_ver_target_feature), &["v8.8a", "cssc"]), + ("v9.1a", unstable(sym::aarch64_ver_target_feature), &["v9a", "v8.6a"]), + ("v9.2a", unstable(sym::aarch64_ver_target_feature), &["v9.1a", "v8.7a"]), + ("v9.3a", unstable(sym::aarch64_ver_target_feature), &["v9.2a", "v8.8a"]), + ("v9.4a", unstable(sym::aarch64_ver_target_feature), &["v9.3a", "v8.9a"]), + ("v9.5a", unstable(sym::aarch64_ver_target_feature), &["v9.4a"]), + ("v9a", unstable(sym::aarch64_ver_target_feature), &["v8.5a", "sve2"]), // FEAT_VHE - ("vh", Stable, &[]), + ("vh", STABLE, &[]), // FEAT_WFxT - ("wfxt", Unstable(sym::aarch64_unstable_target_feature), &[]), + ("wfxt", unstable(sym::aarch64_unstable_target_feature), &[]), // tidy-alphabetical-end ]; @@ -328,240 +470,337 @@ const AARCH64_TIED_FEATURES: &[&[&str]] = &[ &["paca", "pacg"], // Together these represent `pauth` in LLVM ]; -const X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ +const X86_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("adx", Stable, &[]), - ("aes", Stable, &["sse2"]), - ("amx-bf16", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), - ("amx-complex", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), - ("amx-fp16", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), - ("amx-int8", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), - ("amx-tile", Unstable(sym::x86_amx_intrinsics), &[]), - ("avx", Stable, &["sse4.2"]), - ("avx2", Stable, &["avx"]), - ("avx512bf16", Unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512bitalg", Unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512bw", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512cd", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512dq", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512f", Unstable(sym::avx512_target_feature), &["avx2", "fma", "f16c"]), - ("avx512fp16", Unstable(sym::avx512_target_feature), &["avx512bw", "avx512vl", "avx512dq"]), - ("avx512ifma", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vbmi", Unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512vbmi2", Unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512vl", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vnni", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vp2intersect", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vpopcntdq", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avxifma", Unstable(sym::avx512_target_feature), &["avx2"]), - ("avxneconvert", Unstable(sym::avx512_target_feature), &["avx2"]), - ("avxvnni", Unstable(sym::avx512_target_feature), &["avx2"]), - ("avxvnniint16", Unstable(sym::avx512_target_feature), &["avx2"]), - ("avxvnniint8", Unstable(sym::avx512_target_feature), &["avx2"]), - ("bmi1", Stable, &[]), - ("bmi2", Stable, &[]), - ("cmpxchg16b", Stable, &[]), - ("ermsb", Unstable(sym::ermsb_target_feature), &[]), - ("f16c", Stable, &["avx"]), - ("fma", Stable, &["avx"]), - ("fxsr", Stable, &[]), - ("gfni", Unstable(sym::avx512_target_feature), &["sse2"]), - ("lahfsahf", Unstable(sym::lahfsahf_target_feature), &[]), - ("lzcnt", Stable, &[]), - ("movbe", Stable, &[]), - ("pclmulqdq", Stable, &["sse2"]), - ("popcnt", Stable, &[]), - ("prfchw", Unstable(sym::prfchw_target_feature), &[]), - ("rdrand", Stable, &[]), - ("rdseed", Stable, &[]), - ("rtm", Unstable(sym::rtm_target_feature), &[]), - ("sha", Stable, &["sse2"]), - ("sha512", Unstable(sym::sha512_sm_x86), &["avx2"]), - ("sm3", Unstable(sym::sha512_sm_x86), &["avx"]), - ("sm4", Unstable(sym::sha512_sm_x86), &["avx2"]), - ("soft-float", Forbidden { reason: "unsound because it changes float ABI" }, &[]), - ("sse", Stable, &[]), - ("sse2", Stable, &["sse"]), - ("sse3", Stable, &["sse2"]), - ("sse4.1", Stable, &["ssse3"]), - ("sse4.2", Stable, &["sse4.1"]), - ("sse4a", Unstable(sym::sse4a_target_feature), &["sse3"]), - ("ssse3", Stable, &["sse3"]), - ("tbm", Unstable(sym::tbm_target_feature), &[]), - ("vaes", Unstable(sym::avx512_target_feature), &["avx2", "aes"]), - ("vpclmulqdq", Unstable(sym::avx512_target_feature), &["avx", "pclmulqdq"]), - ("xop", Unstable(sym::xop_target_feature), &[/*"fma4", */ "avx", "sse4a"]), - ("xsave", Stable, &[]), - ("xsavec", Stable, &["xsave"]), - ("xsaveopt", Stable, &["xsave"]), - ("xsaves", Stable, &["xsave"]), + ("adx", STABLE, &[]), + ("aes", STABLE, &["sse2"]), + ("amx-bf16", unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-complex", unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-fp16", unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-int8", unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-tile", unstable(sym::x86_amx_intrinsics), &[]), + ("avx", STABLE, &["sse4.2"]), + ("avx2", STABLE, &["avx"]), + ("avx512bf16", unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512bitalg", unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512bw", unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512cd", unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512dq", unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512f", unstable(sym::avx512_target_feature), &["avx2", "fma", "f16c"]), + ("avx512fp16", unstable(sym::avx512_target_feature), &["avx512bw", "avx512vl", "avx512dq"]), + ("avx512ifma", unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vbmi", unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512vbmi2", unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512vl", unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vnni", unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vp2intersect", unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vpopcntdq", unstable(sym::avx512_target_feature), &["avx512f"]), + ("avxifma", unstable(sym::avx512_target_feature), &["avx2"]), + ("avxneconvert", unstable(sym::avx512_target_feature), &["avx2"]), + ("avxvnni", unstable(sym::avx512_target_feature), &["avx2"]), + ("avxvnniint16", unstable(sym::avx512_target_feature), &["avx2"]), + ("avxvnniint8", unstable(sym::avx512_target_feature), &["avx2"]), + ("bmi1", STABLE, &[]), + ("bmi2", STABLE, &[]), + ("cmpxchg16b", STABLE, &[]), + ("ermsb", unstable(sym::ermsb_target_feature), &[]), + ("f16c", STABLE, &["avx"]), + ("fma", STABLE, &["avx"]), + ("fxsr", STABLE, &[]), + ("gfni", unstable(sym::avx512_target_feature), &["sse2"]), + ("lahfsahf", unstable(sym::lahfsahf_target_feature), &[]), + ("lzcnt", STABLE, &[]), + ("movbe", STABLE, &[]), + ("pclmulqdq", STABLE, &["sse2"]), + ("popcnt", STABLE, &[]), + ("prfchw", unstable(sym::prfchw_target_feature), &[]), + ("rdrand", STABLE, &[]), + ("rdseed", STABLE, &[]), + ("rtm", unstable(sym::rtm_target_feature), &[]), + ("sha", STABLE, &["sse2"]), + ("sha512", unstable(sym::sha512_sm_x86), &["avx2"]), + ("sm3", unstable(sym::sha512_sm_x86), &["avx"]), + ("sm4", unstable(sym::sha512_sm_x86), &["avx2"]), + ("soft-float", Stability::Forbidden { reason: "unsound because it changes float ABI" }, &[]), + ("sse", STABLE, &[]), + ("sse2", STABLE, &["sse"]), + ("sse3", STABLE, &["sse2"]), + ("sse4.1", STABLE, &["ssse3"]), + ("sse4.2", STABLE, &["sse4.1"]), + ("sse4a", unstable(sym::sse4a_target_feature), &["sse3"]), + ("ssse3", STABLE, &["sse3"]), + ("tbm", unstable(sym::tbm_target_feature), &[]), + ("vaes", unstable(sym::avx512_target_feature), &["avx2", "aes"]), + ("vpclmulqdq", unstable(sym::avx512_target_feature), &["avx", "pclmulqdq"]), + ( + "x87", + Stability::Unstable { + nightly_feature: sym::x87_target_feature, + 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") { + Ok(()) + } else { + Err("unsound on hard-float targets because it changes float ABI") + } + }, + }, + &[], + ), + ("xop", unstable(sym::xop_target_feature), &[/*"fma4", */ "avx", "sse4a"]), + ("xsave", STABLE, &[]), + ("xsavec", STABLE, &["xsave"]), + ("xsaveopt", STABLE, &["xsave"]), + ("xsaves", STABLE, &["xsave"]), // tidy-alphabetical-end - // FIXME: need to also forbid turning off `x87` on hardfloat targets ]; -const HEXAGON_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ +const HEXAGON_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("hvx", Unstable(sym::hexagon_target_feature), &[]), - ("hvx-length128b", Unstable(sym::hexagon_target_feature), &["hvx"]), + ("hvx", unstable(sym::hexagon_target_feature), &[]), + ("hvx-length128b", unstable(sym::hexagon_target_feature), &["hvx"]), // tidy-alphabetical-end ]; -const POWERPC_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ +const POWERPC_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("altivec", Unstable(sym::powerpc_target_feature), &[]), - ("partword-atomics", Unstable(sym::powerpc_target_feature), &[]), - ("power10-vector", Unstable(sym::powerpc_target_feature), &["power9-vector"]), - ("power8-altivec", Unstable(sym::powerpc_target_feature), &["altivec"]), - ("power8-vector", Unstable(sym::powerpc_target_feature), &["vsx", "power8-altivec"]), - ("power9-altivec", Unstable(sym::powerpc_target_feature), &["power8-altivec"]), - ("power9-vector", Unstable(sym::powerpc_target_feature), &["power8-vector", "power9-altivec"]), - ("quadword-atomics", Unstable(sym::powerpc_target_feature), &[]), - ("vsx", Unstable(sym::powerpc_target_feature), &["altivec"]), + ("altivec", unstable(sym::powerpc_target_feature), &[]), + ("partword-atomics", unstable(sym::powerpc_target_feature), &[]), + ("power10-vector", unstable(sym::powerpc_target_feature), &["power9-vector"]), + ("power8-altivec", unstable(sym::powerpc_target_feature), &["altivec"]), + ("power8-crypto", unstable(sym::powerpc_target_feature), &["power8-altivec"]), + ("power8-vector", unstable(sym::powerpc_target_feature), &["vsx", "power8-altivec"]), + ("power9-altivec", unstable(sym::powerpc_target_feature), &["power8-altivec"]), + ("power9-vector", unstable(sym::powerpc_target_feature), &["power8-vector", "power9-altivec"]), + ("quadword-atomics", unstable(sym::powerpc_target_feature), &[]), + ("vsx", unstable(sym::powerpc_target_feature), &["altivec"]), // tidy-alphabetical-end ]; -const MIPS_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ +const MIPS_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("fp64", Unstable(sym::mips_target_feature), &[]), - ("msa", Unstable(sym::mips_target_feature), &[]), - ("virt", Unstable(sym::mips_target_feature), &[]), + ("fp64", unstable(sym::mips_target_feature), &[]), + ("msa", unstable(sym::mips_target_feature), &[]), + ("virt", unstable(sym::mips_target_feature), &[]), // tidy-alphabetical-end ]; -const RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ +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), &[]), - ("m", Stable, &[]), - ("relax", Unstable(sym::riscv_target_feature), &[]), - ("unaligned-scalar-mem", Unstable(sym::riscv_target_feature), &[]), - ("v", Unstable(sym::riscv_target_feature), &[]), - ("zaamo", Unstable(sym::riscv_target_feature), &[]), - ("zabha", Unstable(sym::riscv_target_feature), &["zaamo"]), - ("zalrsc", Unstable(sym::riscv_target_feature), &[]), - ("zba", Stable, &[]), - ("zbb", Stable, &[]), - ("zbc", Stable, &[]), - ("zbkb", Stable, &[]), - ("zbkc", Stable, &[]), - ("zbkx", Stable, &[]), - ("zbs", Stable, &[]), - ("zdinx", Unstable(sym::riscv_target_feature), &["zfinx"]), - ("zfh", Unstable(sym::riscv_target_feature), &["zfhmin"]), - ("zfhmin", Unstable(sym::riscv_target_feature), &["f"]), - ("zfinx", Unstable(sym::riscv_target_feature), &[]), - ("zhinx", Unstable(sym::riscv_target_feature), &["zhinxmin"]), - ("zhinxmin", Unstable(sym::riscv_target_feature), &["zfinx"]), - ("zk", Stable, &["zkn", "zkr", "zkt"]), - ("zkn", Stable, &["zbkb", "zbkc", "zbkx", "zkne", "zknd", "zknh"]), - ("zknd", Stable, &[]), - ("zkne", Stable, &[]), - ("zknh", Stable, &[]), - ("zkr", Stable, &[]), - ("zks", Stable, &["zbkb", "zbkc", "zbkx", "zksed", "zksh"]), - ("zksed", Stable, &[]), - ("zksh", Stable, &[]), - ("zkt", Stable, &[]), + ("a", STABLE, &["zaamo", "zalrsc"]), + ("c", STABLE, &[]), + ( + "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), &[]), + ("v", unstable(sym::riscv_target_feature), &[]), + ("zaamo", unstable(sym::riscv_target_feature), &[]), + ("zabha", unstable(sym::riscv_target_feature), &["zaamo"]), + ("zalrsc", unstable(sym::riscv_target_feature), &[]), + ("zba", STABLE, &[]), + ("zbb", STABLE, &[]), + ("zbc", STABLE, &[]), + ("zbkb", STABLE, &[]), + ("zbkc", STABLE, &[]), + ("zbkx", STABLE, &[]), + ("zbs", STABLE, &[]), + ("zdinx", unstable(sym::riscv_target_feature), &["zfinx"]), + ("zfh", unstable(sym::riscv_target_feature), &["zfhmin"]), + ("zfhmin", unstable(sym::riscv_target_feature), &["f"]), + ("zfinx", unstable(sym::riscv_target_feature), &[]), + ("zhinx", unstable(sym::riscv_target_feature), &["zhinxmin"]), + ("zhinxmin", unstable(sym::riscv_target_feature), &["zfinx"]), + ("zk", STABLE, &["zkn", "zkr", "zkt"]), + ("zkn", STABLE, &["zbkb", "zbkc", "zbkx", "zkne", "zknd", "zknh"]), + ("zknd", STABLE, &[]), + ("zkne", STABLE, &[]), + ("zknh", STABLE, &[]), + ("zkr", STABLE, &[]), + ("zks", STABLE, &["zbkb", "zbkc", "zbkx", "zksed", "zksh"]), + ("zksed", STABLE, &[]), + ("zksh", STABLE, &[]), + ("zkt", STABLE, &[]), // tidy-alphabetical-end ]; -const WASM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ +const WASM_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("atomics", Unstable(sym::wasm_target_feature), &[]), - ("bulk-memory", Stable, &[]), - ("exception-handling", Unstable(sym::wasm_target_feature), &[]), - ("extended-const", Stable, &[]), - ("multivalue", Stable, &[]), - ("mutable-globals", Stable, &[]), - ("nontrapping-fptoint", Stable, &[]), - ("reference-types", Stable, &[]), - ("relaxed-simd", Stable, &["simd128"]), - ("sign-ext", Stable, &[]), - ("simd128", Stable, &[]), - ("tail-call", Stable, &[]), - ("wide-arithmetic", Unstable(sym::wasm_target_feature), &[]), + ("atomics", unstable(sym::wasm_target_feature), &[]), + ("bulk-memory", STABLE, &[]), + ("exception-handling", unstable(sym::wasm_target_feature), &[]), + ("extended-const", STABLE, &[]), + ("multivalue", STABLE, &[]), + ("mutable-globals", STABLE, &[]), + ("nontrapping-fptoint", STABLE, &[]), + ("reference-types", STABLE, &[]), + ("relaxed-simd", STABLE, &["simd128"]), + ("sign-ext", STABLE, &[]), + ("simd128", STABLE, &[]), + ("tail-call", STABLE, &[]), + ("wide-arithmetic", unstable(sym::wasm_target_feature), &[]), // tidy-alphabetical-end ]; -const BPF_FEATURES: &[(&str, Stability, ImpliedFeatures)] = - &[("alu32", Unstable(sym::bpf_target_feature), &[])]; +const BPF_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = + &[("alu32", unstable(sym::bpf_target_feature), &[])]; -const CSKY_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ +const CSKY_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("10e60", Unstable(sym::csky_target_feature), &["7e10"]), - ("2e3", Unstable(sym::csky_target_feature), &["e2"]), - ("3e3r1", Unstable(sym::csky_target_feature), &[]), - ("3e3r2", Unstable(sym::csky_target_feature), &["3e3r1", "doloop"]), - ("3e3r3", Unstable(sym::csky_target_feature), &["doloop"]), - ("3e7", Unstable(sym::csky_target_feature), &["2e3"]), - ("7e10", Unstable(sym::csky_target_feature), &["3e7"]), - ("cache", Unstable(sym::csky_target_feature), &[]), - ("doloop", Unstable(sym::csky_target_feature), &[]), - ("dsp1e2", Unstable(sym::csky_target_feature), &[]), - ("dspe60", Unstable(sym::csky_target_feature), &[]), - ("e1", Unstable(sym::csky_target_feature), &["elrw"]), - ("e2", Unstable(sym::csky_target_feature), &["e2"]), - ("edsp", Unstable(sym::csky_target_feature), &[]), - ("elrw", Unstable(sym::csky_target_feature), &[]), - ("float1e2", Unstable(sym::csky_target_feature), &[]), - ("float1e3", Unstable(sym::csky_target_feature), &[]), - ("float3e4", Unstable(sym::csky_target_feature), &[]), - ("float7e60", Unstable(sym::csky_target_feature), &[]), - ("floate1", Unstable(sym::csky_target_feature), &[]), - ("hard-tp", Unstable(sym::csky_target_feature), &[]), - ("high-registers", Unstable(sym::csky_target_feature), &[]), - ("hwdiv", Unstable(sym::csky_target_feature), &[]), - ("mp", Unstable(sym::csky_target_feature), &["2e3"]), - ("mp1e2", Unstable(sym::csky_target_feature), &["3e7"]), - ("nvic", Unstable(sym::csky_target_feature), &[]), - ("trust", Unstable(sym::csky_target_feature), &[]), - ("vdsp2e60f", Unstable(sym::csky_target_feature), &[]), - ("vdspv1", Unstable(sym::csky_target_feature), &[]), - ("vdspv2", Unstable(sym::csky_target_feature), &[]), + ("10e60", unstable(sym::csky_target_feature), &["7e10"]), + ("2e3", unstable(sym::csky_target_feature), &["e2"]), + ("3e3r1", unstable(sym::csky_target_feature), &[]), + ("3e3r2", unstable(sym::csky_target_feature), &["3e3r1", "doloop"]), + ("3e3r3", unstable(sym::csky_target_feature), &["doloop"]), + ("3e7", unstable(sym::csky_target_feature), &["2e3"]), + ("7e10", unstable(sym::csky_target_feature), &["3e7"]), + ("cache", unstable(sym::csky_target_feature), &[]), + ("doloop", unstable(sym::csky_target_feature), &[]), + ("dsp1e2", unstable(sym::csky_target_feature), &[]), + ("dspe60", unstable(sym::csky_target_feature), &[]), + ("e1", unstable(sym::csky_target_feature), &["elrw"]), + ("e2", unstable(sym::csky_target_feature), &["e2"]), + ("edsp", unstable(sym::csky_target_feature), &[]), + ("elrw", unstable(sym::csky_target_feature), &[]), + ("float1e2", unstable(sym::csky_target_feature), &[]), + ("float1e3", unstable(sym::csky_target_feature), &[]), + ("float3e4", unstable(sym::csky_target_feature), &[]), + ("float7e60", unstable(sym::csky_target_feature), &[]), + ("floate1", unstable(sym::csky_target_feature), &[]), + ("hard-tp", unstable(sym::csky_target_feature), &[]), + ("high-registers", unstable(sym::csky_target_feature), &[]), + ("hwdiv", unstable(sym::csky_target_feature), &[]), + ("mp", unstable(sym::csky_target_feature), &["2e3"]), + ("mp1e2", unstable(sym::csky_target_feature), &["3e7"]), + ("nvic", unstable(sym::csky_target_feature), &[]), + ("trust", unstable(sym::csky_target_feature), &[]), + ("vdsp2e60f", unstable(sym::csky_target_feature), &[]), + ("vdspv1", unstable(sym::csky_target_feature), &[]), + ("vdspv2", unstable(sym::csky_target_feature), &[]), // tidy-alphabetical-end //fpu // tidy-alphabetical-start - ("fdivdu", Unstable(sym::csky_target_feature), &[]), - ("fpuv2_df", Unstable(sym::csky_target_feature), &[]), - ("fpuv2_sf", Unstable(sym::csky_target_feature), &[]), - ("fpuv3_df", Unstable(sym::csky_target_feature), &[]), - ("fpuv3_hf", Unstable(sym::csky_target_feature), &[]), - ("fpuv3_hi", Unstable(sym::csky_target_feature), &[]), - ("fpuv3_sf", Unstable(sym::csky_target_feature), &[]), - ("hard-float", Unstable(sym::csky_target_feature), &[]), - ("hard-float-abi", Unstable(sym::csky_target_feature), &[]), + ("fdivdu", unstable(sym::csky_target_feature), &[]), + ("fpuv2_df", unstable(sym::csky_target_feature), &[]), + ("fpuv2_sf", unstable(sym::csky_target_feature), &[]), + ("fpuv3_df", unstable(sym::csky_target_feature), &[]), + ("fpuv3_hf", unstable(sym::csky_target_feature), &[]), + ("fpuv3_hi", unstable(sym::csky_target_feature), &[]), + ("fpuv3_sf", unstable(sym::csky_target_feature), &[]), + ("hard-float", unstable(sym::csky_target_feature), &[]), + ("hard-float-abi", unstable(sym::csky_target_feature), &[]), // tidy-alphabetical-end ]; -const LOONGARCH_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ +const LOONGARCH_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("d", Unstable(sym::loongarch_target_feature), &["f"]), - ("f", Unstable(sym::loongarch_target_feature), &[]), - ("frecipe", Unstable(sym::loongarch_target_feature), &[]), - ("lasx", Unstable(sym::loongarch_target_feature), &["lsx"]), - ("lbt", Unstable(sym::loongarch_target_feature), &[]), - ("lsx", Unstable(sym::loongarch_target_feature), &["d"]), - ("lvz", Unstable(sym::loongarch_target_feature), &[]), - ("relax", Unstable(sym::loongarch_target_feature), &[]), - ("ual", Unstable(sym::loongarch_target_feature), &[]), + ("d", unstable(sym::loongarch_target_feature), &["f"]), + ("f", unstable(sym::loongarch_target_feature), &[]), + ("frecipe", unstable(sym::loongarch_target_feature), &[]), + ("lasx", unstable(sym::loongarch_target_feature), &["lsx"]), + ("lbt", unstable(sym::loongarch_target_feature), &[]), + ("lsx", unstable(sym::loongarch_target_feature), &["d"]), + ("lvz", unstable(sym::loongarch_target_feature), &[]), + ("relax", unstable(sym::loongarch_target_feature), &[]), + ("ual", unstable(sym::loongarch_target_feature), &[]), // tidy-alphabetical-end ]; -const IBMZ_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ +const IBMZ_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("backchain", Unstable(sym::s390x_target_feature), &[]), - ("vector", Unstable(sym::s390x_target_feature), &[]), + ("backchain", unstable(sym::s390x_target_feature), &[]), + ("vector", unstable(sym::s390x_target_feature), &[]), // tidy-alphabetical-end ]; -const SPARC_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ +const SPARC_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("leoncasa", Unstable(sym::sparc_target_feature), &[]), - ("v8plus", Unstable(sym::sparc_target_feature), &[]), - ("v9", Unstable(sym::sparc_target_feature), &[]), + ("leoncasa", unstable(sym::sparc_target_feature), &[]), + ("v8plus", unstable(sym::sparc_target_feature), &[]), + ("v9", unstable(sym::sparc_target_feature), &[]), + // 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 ]; @@ -569,7 +808,7 @@ const SPARC_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ /// primitives may be documented. /// /// IMPORTANT: If you're adding another feature list above, make sure to add it to this iterator! -pub fn all_rust_features() -> impl Iterator<Item = (&'static str, Stability)> { +pub fn all_rust_features() -> impl Iterator<Item = (&'static str, StabilityUncomputed)> { std::iter::empty() .chain(ARM_FEATURES.iter()) .chain(AARCH64_FEATURES.iter()) @@ -584,6 +823,7 @@ pub fn all_rust_features() -> impl Iterator<Item = (&'static str, Stability)> { .chain(LOONGARCH_FEATURES) .chain(IBMZ_FEATURES) .chain(SPARC_FEATURES) + .chain(M68K_FEATURES) .cloned() .map(|(f, s, _)| (f, s)) } @@ -610,9 +850,13 @@ const HEXAGON_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[/*(512, "hvx-length64b"),*/ (1024, "hvx-length128b")]; const MIPS_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "msa")]; const CSKY_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "vdspv1")]; +const LOONGARCH_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = + &[(128, "lsx"), (256, "lasx")]; -impl super::spec::Target { - pub fn rust_target_features(&self) -> &'static [(&'static str, Stability, ImpliedFeatures)] { +impl Target { + pub fn rust_target_features( + &self, + ) -> &'static [(&'static str, StabilityUncomputed, ImpliedFeatures)] { match &*self.arch { "arm" => ARM_FEATURES, "aarch64" | "arm64ec" => AARCH64_FEATURES, @@ -627,6 +871,7 @@ impl super::spec::Target { "loongarch64" => LOONGARCH_FEATURES, "s390x" => IBMZ_FEATURES, "sparc" | "sparc64" => SPARC_FEATURES, + "m68k" => M68K_FEATURES, _ => &[], } } @@ -637,14 +882,14 @@ impl super::spec::Target { "aarch64" | "arm64ec" => AARCH64_FEATURES_FOR_CORRECT_VECTOR_ABI, "arm" => ARM_FEATURES_FOR_CORRECT_VECTOR_ABI, "powerpc" | "powerpc64" => POWERPC_FEATURES_FOR_CORRECT_VECTOR_ABI, - "loongarch64" => &[], // on-stack ABI, so we complain about all by-val vectors + "loongarch64" => LOONGARCH_FEATURES_FOR_CORRECT_VECTOR_ABI, "riscv32" | "riscv64" => RISCV_FEATURES_FOR_CORRECT_VECTOR_ABI, "wasm32" | "wasm64" => WASM_FEATURES_FOR_CORRECT_VECTOR_ABI, "s390x" => S390X_FEATURES_FOR_CORRECT_VECTOR_ABI, "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 8ba7969207e..ee5ce19cb4d 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -52,7 +52,9 @@ use std::{cmp, fmt, iter}; use rustc_abi::ExternAbi; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; -use rustc_errors::{Applicability, Diag, DiagStyledString, IntoDiagArg, StringPart, pluralize}; +use rustc_errors::{ + Applicability, Diag, DiagStyledString, IntoDiagArg, MultiSpan, StringPart, pluralize, +}; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; @@ -61,12 +63,14 @@ 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; use rustc_span::{BytePos, DesugaringKind, Pos, Span, sym}; use tracing::{debug, instrument}; @@ -74,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, @@ -211,7 +215,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } /// Adds a note if the types come from similarly named crates - fn check_and_note_conflicting_crates(&self, err: &mut Diag<'_>, terr: TypeError<'tcx>) { + fn check_and_note_conflicting_crates(&self, err: &mut Diag<'_>, terr: TypeError<'tcx>) -> bool { + // FIXME(estebank): unify with `report_similar_impl_candidates`. The message is similar, + // even if the logic needed to detect the case is very different. use hir::def_id::CrateNum; use rustc_hir::definitions::DisambiguatedDefPathData; use ty::GenericArg; @@ -285,7 +291,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - let report_path_match = |err: &mut Diag<'_>, did1: DefId, did2: DefId| { + let report_path_match = |err: &mut Diag<'_>, did1: DefId, did2: DefId, ty: &str| -> bool { // Only report definitions from different crates. If both definitions // are from a local module we could have false positives, e.g. // let _ = [{struct Foo; Foo}, {struct Foo; Foo}]; @@ -297,24 +303,112 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // We compare strings because DefPath can be different // for imported and non-imported crates + let expected_str = self.tcx.def_path_str(did1); + let found_str = self.tcx.def_path_str(did2); + let Ok(expected_abs) = abs_path(did1) else { return false }; + let Ok(found_abs) = abs_path(did2) else { return false }; let same_path = || -> Result<_, PrintError> { - Ok(self.tcx.def_path_str(did1) == self.tcx.def_path_str(did2) - || abs_path(did1)? == abs_path(did2)?) + Ok(expected_str == found_str || expected_abs == found_abs) + }; + // We want to use as unique a type path as possible. If both types are "locally + // known" by the same name, we use the "absolute path" which uses the original + // crate name instead. + let (expected, found) = if expected_str == found_str { + (expected_abs.join("::"), found_abs.join("::")) + } else { + (expected_str.clone(), found_str.clone()) }; if same_path().unwrap_or(false) { - let crate_name = self.tcx.crate_name(did1.krate); - let msg = if did1.is_local() || did2.is_local() { + // We've displayed "expected `a::b`, found `a::b`". We add context to + // differentiate the different cases where that might happen. + let expected_crate_name = self.tcx.crate_name(did1.krate); + let found_crate_name = self.tcx.crate_name(did2.krate); + let same_crate = expected_crate_name == found_crate_name; + let expected_sp = self.tcx.def_span(did1); + let found_sp = self.tcx.def_span(did2); + + let both_direct_dependencies = if !did1.is_local() + && !did2.is_local() + && let Some(data1) = self.tcx.extern_crate(did1.krate) + && let Some(data2) = self.tcx.extern_crate(did2.krate) + && data1.dependency_of == LOCAL_CRATE + && data2.dependency_of == LOCAL_CRATE + { + // If both crates are directly depended on, we don't want to mention that + // in the final message, as it is redundant wording. + // We skip the case of semver trick, where one version of the local crate + // depends on another version of itself by checking that both crates at play + // are not the current one. + true + } else { + false + }; + + let mut span: MultiSpan = vec![expected_sp, found_sp].into(); + span.push_span_label( + self.tcx.def_span(did1), + format!("this is the expected {ty} `{expected}`"), + ); + span.push_span_label( + self.tcx.def_span(did2), + format!("this is the found {ty} `{found}`"), + ); + for def_id in [did1, did2] { + let crate_name = self.tcx.crate_name(def_id.krate); + if !def_id.is_local() + && let Some(data) = self.tcx.extern_crate(def_id.krate) + { + let descr = if same_crate { + "one version of".to_string() + } else { + format!("one {ty} comes from") + }; + let dependency = if both_direct_dependencies { + if let rustc_session::cstore::ExternCrateSource::Extern(def_id) = + data.src + && let Some(name) = self.tcx.opt_item_name(def_id) + { + format!(", which is renamed locally to `{name}`") + } else { + String::new() + } + } else if data.dependency_of == LOCAL_CRATE { + ", as a direct dependency of the current crate".to_string() + } else { + let dep = self.tcx.crate_name(data.dependency_of); + format!(", as a dependency of crate `{dep}`") + }; + span.push_span_label( + data.span, + format!("{descr} crate `{crate_name}` used here{dependency}"), + ); + } + } + let msg = if (did1.is_local() || did2.is_local()) && same_crate { format!( - "the crate `{crate_name}` is compiled multiple times, possibly with different configurations" + "the crate `{expected_crate_name}` is compiled multiple times, \ + possibly with different configurations", + ) + } else if same_crate { + format!( + "two different versions of crate `{expected_crate_name}` are being \ + used; two types coming from two different versions of the same crate \ + are different types even if they look the same", ) } else { format!( - "perhaps two different versions of crate `{crate_name}` are being used?" + "two types coming from two different crates are different types even \ + if they look the same", ) }; - err.note(msg); + err.span_note(span, msg); + if same_crate { + err.help("you can use `cargo tree` to explore your dependency tree"); + } + return true; } } + false }; match terr { TypeError::Sorts(ref exp_found) => { @@ -323,14 +417,15 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let (&ty::Adt(exp_adt, _), &ty::Adt(found_adt, _)) = (exp_found.expected.kind(), exp_found.found.kind()) { - report_path_match(err, exp_adt.did(), found_adt.did()); + return report_path_match(err, exp_adt.did(), found_adt.did(), "type"); } } TypeError::Traits(ref exp_found) => { - report_path_match(err, exp_found.expected, exp_found.found); + return report_path_match(err, exp_found.expected, exp_found.found, "trait"); } _ => (), // FIXME(#22750) handle traits and stuff } + false } fn note_error_origin( @@ -339,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( @@ -355,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( @@ -524,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`. @@ -657,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); @@ -763,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 } @@ -1151,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) } _ => { @@ -1312,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() { @@ -1365,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)) } }; @@ -1409,6 +1582,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { label_or_note(span, terr.to_string(self.tcx)); } + if self.check_and_note_conflicting_crates(diag, terr) { + return; + } + if let Some((expected, found, path)) = expected_found { let (expected_label, found_label, exp_found) = match exp_found { Mismatch::Variable(ef) => ( @@ -1470,15 +1647,17 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { |prim: Ty<'tcx>, shadow: Ty<'tcx>, defid: DefId, diag: &mut Diag<'_>| { let name = shadow.sort_string(self.tcx); diag.note(format!( - "{prim} and {name} have similar names, but are actually distinct types" + "`{prim}` and {name} have similar names, but are actually distinct types" + )); + diag.note(format!( + "one `{prim}` is a primitive defined by the language", )); - diag.note(format!("{prim} is a primitive defined by the language")); let def_span = self.tcx.def_span(defid); let msg = if defid.is_local() { - format!("{name} is defined in the current crate") + format!("the other {name} is defined in the current crate") } else { let crate_name = self.tcx.crate_name(defid.krate); - format!("{name} is defined in crate `{crate_name}`") + format!("the other {name} is defined in crate `{crate_name}`") }; diag.span_note(def_span, msg); }; @@ -1666,8 +1845,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - self.check_and_note_conflicting_crates(diag, terr); - self.note_and_explain_type_err(diag, terr, cause, span, cause.body_id.to_def_id()); if let Some(exp_found) = exp_found && let exp_found = TypeError::Sorts(exp_found) @@ -1693,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); } @@ -1931,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..677b72477a2 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 @@ -21,6 +21,7 @@ use rustc_span::symbol::{Ident, sym}; use rustc_span::{BytePos, DUMMY_SP, FileName, Span}; 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 +280,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 +302,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 +323,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 +337,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 +412,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..e0f0f100ed7 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 @@ -54,9 +54,9 @@ 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 br = anon_param_info.br; 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 { @@ -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..9844eeacff6 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 @@ -50,7 +50,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 +196,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 +250,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..a2150dc7c8c 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::BoundRegionKind` corresponding to the anonymous region. + pub br: ty::BoundRegionKind, /// The `Span` of the parameter type. pub param_ty_span: Span, /// Signals that the argument is the first parameter in the declaration. @@ -43,7 +43,7 @@ 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 { + let (id, br) = match *anon_region { ty::ReLateParam(late_param) => (late_param.scope, late_param.bound_region), ty::ReEarlyParam(ebr) => { let region_def = tcx.generics_of(generic_param_scope).region_param(ebr, tcx).def_id; @@ -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, br, 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/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index 9fd7dccc57c..0b2665b6e52 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -790,7 +790,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 +864,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); @@ -1097,8 +1097,7 @@ fn msg_span_from_named_region<'tcx>( } ty::ReLateParam(ref fr) => { if !fr.bound_region.is_named() - && let Some((ty, _)) = - find_anon_type(tcx, generic_param_scope, region, &fr.bound_region) + && let Some((ty, _)) = find_anon_type(tcx, generic_param_scope, region) { ("the anonymous lifetime defined here".to_string(), Some(ty.span)) } else { 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 90b18253629..4fb02f60943 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 @@ -1745,9 +1745,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }; ( data.span, - format!( - "one version of crate `{crate_name}` is used here, as a {dependency}" - ), + format!("one version of crate `{crate_name}` used here, as a {dependency}"), ) }) { 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..d54b8598254 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,11 +1,12 @@ 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 _; @@ -15,7 +16,7 @@ use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES; use rustc_span::Span; use rustc_span::symbol::{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 +640,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 +655,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/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 94682f501a8..cc8941b9224 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; @@ -1391,13 +1392,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 afac6fc6004..a5bfb0d0baa 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -10,7 +10,7 @@ use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{Visitor, walk_ty}; -use rustc_hir::{FnRetTy, GenericParamKind}; +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}; @@ -517,7 +517,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 +527,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, ..), @@ -1888,10 +1888,35 @@ pub fn impl_trait_overcapture_suggestion<'tcx>( .collect::<Vec<_>>() .join(", "); - suggs.push(( - tcx.def_span(opaque_def_id).shrink_to_hi(), - format!(" + use<{concatenated_bounds}>"), - )); + let opaque_hir_id = tcx.local_def_id_to_hir_id(opaque_def_id); + // FIXME: This is a bit too conservative, since it ignores parens already written in AST. + let (lparen, rparen) = match tcx + .hir() + .parent_iter(opaque_hir_id) + .nth(1) + .expect("expected ty to have a parent always") + .1 + { + Node::PathSegment(segment) + if segment.args().paren_sugar_output().is_some_and(|ty| ty.hir_id == opaque_hir_id) => + { + ("(", ")") + } + Node::Ty(ty) => match ty.kind { + rustc_hir::TyKind::Ptr(_) | rustc_hir::TyKind::Ref(..) => ("(", ")"), + // FIXME: RPITs are not allowed to be nested in `impl Fn() -> ...`, + // but we eventually could support that, and that would necessitate + // making this more sophisticated. + _ => ("", ""), + }, + _ => ("", ""), + }; + + let rpit_span = tcx.def_span(opaque_def_id); + if !lparen.is_empty() { + suggs.push((rpit_span.shrink_to_lo(), lparen.to_string())); + } + suggs.push((rpit_span.shrink_to_hi(), format!(" + use<{concatenated_bounds}>{rparen}"))); Some(AddPreciseCapturingForOvercapture { suggs, apit_spans }) } 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..20e7a33a701 100644 --- a/compiler/rustc_trait_selection/src/errors/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/errors/note_and_explain.rs @@ -41,8 +41,7 @@ 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) + && let Some((ty, _)) = find_anon_type(tcx, generic_param_scope, region) { (Some(ty.span), "defined_here", String::new()) } else { 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/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index c9297027519..d2abd881c45 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -11,6 +11,7 @@ use rustc_abi::BackendRepr; use rustc_errors::FatalError; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::ty::{ self, EarlyBinder, ExistentialPredicateStableCmpExt as _, GenericArgs, Ty, TyCtxt, @@ -901,23 +902,59 @@ fn contains_illegal_impl_trait_in_trait<'tcx>( fn_def_id: DefId, ty: ty::Binder<'tcx, Ty<'tcx>>, ) -> Option<MethodViolationCode> { - // This would be caught below, but rendering the error as a separate - // `async-specific` message is better. + let ty = tcx.liberate_late_bound_regions(fn_def_id, ty); + if tcx.asyncness(fn_def_id).is_async() { - return Some(MethodViolationCode::AsyncFn); + // FIXME(async_fn_in_dyn_trait): Think of a better way to unify these code paths + // to issue an appropriate feature suggestion when users try to use AFIDT. + // Obviously we must only do this once AFIDT is finished enough to actually be usable. + if tcx.features().async_fn_in_dyn_trait() { + let ty::Alias(ty::Projection, proj) = *ty.kind() else { + bug!("expected async fn in trait to return an RPITIT"); + }; + assert!(tcx.is_impl_trait_in_trait(proj.def_id)); + + // FIXME(async_fn_in_dyn_trait): We should check that this bound is legal too, + // and stop relying on `async fn` in the definition. + for bound in tcx.item_bounds(proj.def_id).instantiate(tcx, proj.args) { + if let Some(violation) = bound + .visit_with(&mut IllegalRpititVisitor { tcx, allowed: Some(proj) }) + .break_value() + { + return Some(violation); + } + } + + None + } else { + // Rendering the error as a separate `async-specific` message is better. + Some(MethodViolationCode::AsyncFn) + } + } else { + ty.visit_with(&mut IllegalRpititVisitor { tcx, allowed: None }).break_value() } +} + +struct IllegalRpititVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + allowed: Option<ty::AliasTy<'tcx>>, +} + +impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for IllegalRpititVisitor<'tcx> { + type Result = ControlFlow<MethodViolationCode>; - // FIXME(RPITIT): Perhaps we should use a visitor here? - ty.skip_binder().walk().find_map(|arg| { - if let ty::GenericArgKind::Type(ty) = arg.unpack() - && let ty::Alias(ty::Projection, proj) = ty.kind() - && tcx.is_impl_trait_in_trait(proj.def_id) + fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { + if let ty::Alias(ty::Projection, proj) = *ty.kind() + && Some(proj) != self.allowed + && self.tcx.is_impl_trait_in_trait(proj.def_id) { - Some(MethodViolationCode::ReferencesImplTraitInTrait(tcx.def_span(proj.def_id))) + ControlFlow::Break(MethodViolationCode::ReferencesImplTraitInTrait( + self.tcx.def_span(proj.def_id), + )) } else { - None + ty.super_visit_with(self) } - }) + } } pub(crate) fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_trait_selection/src/traits/effects.rs b/compiler/rustc_trait_selection/src/traits/effects.rs index 07fb2efb7fe..b17a489a857 100644 --- a/compiler/rustc_trait_selection/src/traits/effects.rs +++ b/compiler/rustc_trait_selection/src/traits/effects.rs @@ -128,7 +128,9 @@ fn evaluate_host_effect_from_selection_candiate<'tcx>( Err(_) => Err(EvaluationFailure::NoSolution), Ok(Some(source)) => match source { ImplSource::UserDefined(impl_) => { - if tcx.constness(impl_.impl_def_id) != hir::Constness::Const { + if tcx.impl_trait_header(impl_.impl_def_id).unwrap().constness + != hir::Constness::Const + { return Err(EvaluationFailure::NoSolution); } diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index 3b17fa6b032..7a67b943e94 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -18,6 +18,7 @@ pub enum CopyImplementationError<'tcx> { InfringingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>), NotAnAdt, HasDestructor, + HasUnsafeFields, } pub enum ConstParamTyImplementationError<'tcx> { @@ -39,11 +40,16 @@ pub enum InfringingFieldsReason<'tcx> { /// /// If it's not an ADT, int ty, `bool`, float ty, `char`, raw pointer, `!`, /// a reference or an array returns `Err(NotAnAdt)`. +/// +/// If the impl is `Safe`, `self_type` must not have unsafe fields. When used to +/// generate suggestions in lints, `Safe` should be supplied so as to not +/// suggest implementing `Copy` for types with unsafe fields. pub fn type_allowed_to_implement_copy<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, self_type: Ty<'tcx>, parent_cause: ObligationCause<'tcx>, + impl_safety: hir::Safety, ) -> Result<(), CopyImplementationError<'tcx>> { let (adt, args) = match self_type.kind() { // These types used to have a builtin impl. @@ -78,6 +84,10 @@ pub fn type_allowed_to_implement_copy<'tcx>( return Err(CopyImplementationError::HasDestructor); } + if impl_safety.is_safe() && self_type.has_unsafe_fields() { + return Err(CopyImplementationError::HasUnsafeFields); + } + Ok(()) } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 069fab6a6e6..069d42d4018 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(); diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 49c34550f8e..5f1dbfeedfb 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -7,8 +7,8 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; use rustc_hir::lang_items::LangItem; -use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::infer::resolve::OpportunisticRegionResolver; +use rustc_infer::infer::{DefineOpaqueTypes, RegionVariableOrigin}; use rustc_infer::traits::{ObligationCauseCode, PredicateObligations}; use rustc_middle::traits::select::OverflowError; use rustc_middle::traits::{BuiltinImplSource, ImplSource, ImplSourceUserDefinedData}; @@ -18,6 +18,7 @@ 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 thin_vec::thin_vec; use tracing::{debug, instrument}; use super::{ @@ -61,6 +62,9 @@ enum ProjectionCandidate<'tcx> { /// Bounds specified on an object type Object(ty::PolyProjectionPredicate<'tcx>), + /// Built-in bound for a dyn async fn in trait + ObjectRpitit, + /// From an "impl" (or a "pseudo-impl" returned by select) Select(Selection<'tcx>), } @@ -744,7 +748,7 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>( let Some(clause) = clause.as_projection_clause() else { return ControlFlow::Continue(()); }; - if clause.projection_def_id() != obligation.predicate.def_id { + if clause.item_def_id() != obligation.predicate.def_id { return ControlFlow::Continue(()); } @@ -827,6 +831,17 @@ fn assemble_candidates_from_object_ty<'cx, 'tcx>( env_predicates, false, ); + + // `dyn Trait` automagically project their AFITs to `dyn* Future`. + if tcx.is_impl_trait_in_trait(obligation.predicate.def_id) + && let Some(out_trait_def_id) = data.principal_def_id() + && let rpitit_trait_def_id = tcx.parent(obligation.predicate.def_id) + && tcx + .supertrait_def_ids(out_trait_def_id) + .any(|trait_def_id| trait_def_id == rpitit_trait_def_id) + { + candidate_set.push_candidate(ProjectionCandidate::ObjectRpitit); + } } #[instrument( @@ -847,7 +862,7 @@ fn assemble_candidates_from_predicates<'cx, 'tcx>( let bound_predicate = predicate.kind(); if let ty::ClauseKind::Projection(data) = predicate.kind().skip_binder() { let data = bound_predicate.rebind(data); - if data.projection_def_id() != obligation.predicate.def_id { + if data.item_def_id() != obligation.predicate.def_id { continue; } @@ -1247,6 +1262,8 @@ fn confirm_candidate<'cx, 'tcx>( ProjectionCandidate::Select(impl_source) => { confirm_select_candidate(selcx, obligation, impl_source) } + + ProjectionCandidate::ObjectRpitit => confirm_object_rpitit_candidate(selcx, obligation), }; // When checking for cycle during evaluation, we compare predicates with @@ -2034,6 +2051,45 @@ fn confirm_impl_candidate<'cx, 'tcx>( } } +fn confirm_object_rpitit_candidate<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTermObligation<'tcx>, +) -> Progress<'tcx> { + let tcx = selcx.tcx(); + let mut obligations = thin_vec![]; + + // Compute an intersection lifetime for all the input components of this GAT. + let intersection = + selcx.infcx.next_region_var(RegionVariableOrigin::MiscVariable(obligation.cause.span)); + for component in obligation.predicate.args { + match component.unpack() { + ty::GenericArgKind::Lifetime(lt) => { + obligations.push(obligation.with(tcx, ty::OutlivesPredicate(lt, intersection))); + } + ty::GenericArgKind::Type(ty) => { + obligations.push(obligation.with(tcx, ty::OutlivesPredicate(ty, intersection))); + } + ty::GenericArgKind::Const(_ct) => { + // Consts have no outlives... + } + } + } + + Progress { + term: Ty::new_dynamic( + tcx, + tcx.item_bounds_to_existential_predicates( + obligation.predicate.def_id, + obligation.predicate.args, + ), + intersection, + ty::DynStar, + ) + .into(), + obligations, + } +} + // Get obligations corresponding to the predicates from the where-clause of the // associated type itself. fn assoc_ty_own_obligations<'cx, 'tcx>( diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs index dc3f5544613..254dee794f1 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs @@ -3,7 +3,7 @@ use rustc_infer::traits::Obligation; use rustc_middle::traits::query::NoSolution; pub use rustc_middle::traits::query::type_op::AscribeUserType; use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; -use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, UserArgs, UserSelfTy, UserType}; +use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, UserArgs, UserSelfTy, UserTypeKind}; use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument}; @@ -46,12 +46,18 @@ pub fn type_op_ascribe_user_type_with_span<'tcx>( let (param_env, AscribeUserType { mir_ty, user_ty }) = key.into_parts(); debug!("type_op_ascribe_user_type: mir_ty={:?} user_ty={:?}", mir_ty, user_ty); let span = span.unwrap_or(DUMMY_SP); - match user_ty { - UserType::Ty(user_ty) => relate_mir_and_user_ty(ocx, param_env, span, mir_ty, user_ty)?, - UserType::TypeOf(def_id, user_args) => { + match user_ty.kind { + UserTypeKind::Ty(user_ty) => relate_mir_and_user_ty(ocx, param_env, span, mir_ty, user_ty)?, + UserTypeKind::TypeOf(def_id, user_args) => { relate_mir_and_user_args(ocx, param_env, span, mir_ty, def_id, user_args)? } }; + + // Enforce any bounds that come from impl trait in bindings. + ocx.register_obligations(user_ty.bounds.iter().map(|clause| { + Obligation::new(ocx.infcx.tcx, ObligationCause::dummy_with_span(span), param_env, clause) + })); + Ok(()) } diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 32b4567aba4..5e27fd43789 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -18,6 +18,7 @@ use rustc_infer::traits::{ use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_middle::ty::{self, ToPolyTraitRef, Ty, TypeVisitableExt, TypingMode}; use rustc_middle::{bug, span_bug}; +use rustc_type_ir::Interner; use tracing::{debug, instrument, trace}; use super::SelectionCandidate::*; @@ -794,6 +795,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Never | ty::Tuple(_) | ty::CoroutineWitness(..) => { + // Only consider auto impls of unsafe traits when there are + // no unsafe fields. + if self.tcx().trait_is_unsafe(def_id) && self_ty.has_unsafe_fields() { + return; + } + // Only consider auto impls if there are no manual impls for the root of `self_ty`. // // For example, we only consider auto candidates for `&i32: Auto` if no explicit impl diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 2fbe2e1e323..962b6b94fa6 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -19,6 +19,7 @@ use rustc_middle::traits::{BuiltinImplSource, SignatureMismatchData}; use rustc_middle::ty::{self, GenericArgsRef, ToPolyTraitRef, Ty, TyCtxt, Upcast}; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::DefId; +use rustc_type_ir::elaborate; use tracing::{debug, instrument}; use super::SelectionCandidate::{self, *}; @@ -624,6 +625,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { for assoc_type in assoc_types { let defs: &ty::Generics = tcx.generics_of(assoc_type); + // When `async_fn_in_dyn_trait` is enabled, we don't need to check the + // RPITIT for compatibility, since it's not provided by the user. + if tcx.features().async_fn_in_dyn_trait() && tcx.is_impl_trait_in_trait(assoc_type) { + continue; + } + if !defs.own_params.is_empty() { tcx.dcx().span_delayed_bug( obligation.cause.span, @@ -1175,6 +1182,38 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::ClauseKind::TypeOutlives(outlives).upcast(tcx), )); + // Require that all AFIT will return something that can be coerced into `dyn*` + // -- a shim will be responsible for doing the actual coercion to `dyn*`. + if let Some(principal) = data.principal() { + for supertrait in + elaborate::supertraits(tcx, principal.with_self_ty(tcx, source)) + { + if tcx.is_trait_alias(supertrait.def_id()) { + continue; + } + + for &assoc_item in tcx.associated_item_def_ids(supertrait.def_id()) { + if !tcx.is_impl_trait_in_trait(assoc_item) { + continue; + } + + // RPITITs with `Self: Sized` don't need to be checked. + if tcx.generics_require_sized_self(assoc_item) { + continue; + } + + let pointer_like_goal = pointer_like_goal_for_rpitit( + tcx, + supertrait, + assoc_item, + &obligation.cause, + ); + + nested.push(predicate_to_obligation(pointer_like_goal.upcast(tcx))); + } + } + } + ImplSource::Builtin(BuiltinImplSource::Misc, nested) } @@ -1280,3 +1319,43 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }) } } + +/// Compute a goal that some RPITIT (right now, only RPITITs corresponding to Futures) +/// implements the `PointerLike` trait, which is a requirement for the RPITIT to be +/// coercible to `dyn* Future`, which is itself a requirement for the RPITIT's parent +/// trait to be coercible to `dyn Trait`. +/// +/// We do this given a supertrait's substitutions, and then augment the substitutions +/// with bound variables to compute the goal universally. Given that `PointerLike` has +/// no region requirements (at least for the built-in pointer types), this shouldn't +/// *really* matter, but it is the best choice for soundness. +fn pointer_like_goal_for_rpitit<'tcx>( + tcx: TyCtxt<'tcx>, + supertrait: ty::PolyTraitRef<'tcx>, + rpitit_item: DefId, + cause: &ObligationCause<'tcx>, +) -> ty::PolyTraitRef<'tcx> { + let mut bound_vars = supertrait.bound_vars().to_vec(); + + let args = supertrait.skip_binder().args.extend_to(tcx, rpitit_item, |arg, _| match arg.kind { + ty::GenericParamDefKind::Lifetime => { + let kind = ty::BoundRegionKind::Named(arg.def_id, tcx.item_name(arg.def_id)); + bound_vars.push(ty::BoundVariableKind::Region(kind)); + ty::Region::new_bound(tcx, ty::INNERMOST, ty::BoundRegion { + var: ty::BoundVar::from_usize(bound_vars.len() - 1), + kind, + }) + .into() + } + ty::GenericParamDefKind::Type { .. } | ty::GenericParamDefKind::Const { .. } => { + unreachable!() + } + }); + + ty::Binder::bind_with_vars( + ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::PointerLike, Some(cause.span)), [ + Ty::new_projection_from_args(tcx, rpitit_item, args), + ]), + tcx.mk_bound_variable_kinds(&bound_vars), + ) +} diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index d362866cbc3..32a2d5a6d93 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -445,7 +445,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 +458,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 +475,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) } /////////////////////////////////////////////////////////////////////////// @@ -1737,7 +1709,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { env_predicate: PolyProjectionPredicate<'tcx>, potentially_unnormalized_candidates: bool, ) -> ProjectionMatchesProjection { - debug_assert_eq!(obligation.predicate.def_id, env_predicate.projection_def_id()); + debug_assert_eq!(obligation.predicate.def_id, env_predicate.item_def_id()); let mut nested_obligations = PredicateObligations::new(); let infer_predicate = self.infcx.instantiate_binder_with_fresh_vars( @@ -1803,18 +1775,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 +1782,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 +1933,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_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index b746d6299ef..b63534880d1 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -45,30 +45,41 @@ fn fn_sig_for_fn_abi<'tcx>( let ty = instance.ty(tcx, typing_env); match *ty.kind() { ty::FnDef(def_id, args) => { - // HACK(davidtwco,eddyb): This is a workaround for polymorphization considering - // parameters unused if they show up in the signature, but not in the `mir::Body` - // (i.e. due to being inside a projection that got normalized, see - // `tests/ui/polymorphization/normalized_sig_types.rs`), and codegen not keeping - // track of a polymorphization `ParamEnv` to allow normalizing later. - // - // We normalize the `fn_sig` again after instantiating at a later point. - let mut sig = tcx.instantiate_bound_regions_with_erased( - tcx.fn_sig(def_id) - .map_bound(|fn_sig| { - tcx.normalize_erasing_regions( - ty::TypingEnv::non_body_analysis(tcx, def_id), - fn_sig, - ) - }) - .instantiate(tcx, args), - ); + let mut sig = tcx + .instantiate_bound_regions_with_erased(tcx.fn_sig(def_id).instantiate(tcx, args)); + // Modify `fn(self, ...)` to `fn(self: *mut Self, ...)`. if let ty::InstanceKind::VTableShim(..) = instance.def { let mut inputs_and_output = sig.inputs_and_output.to_vec(); inputs_and_output[0] = Ty::new_mut_ptr(tcx, inputs_and_output[0]); sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output); } + // Modify `fn() -> impl Future` to `fn() -> dyn* Future`. + if let ty::InstanceKind::ReifyShim(def_id, _) = instance.def + && let Some((rpitit_def_id, fn_args)) = + tcx.return_position_impl_trait_in_trait_shim_data(def_id) + { + let fn_args = fn_args.instantiate(tcx, args); + let rpitit_args = + fn_args.extend_to(tcx, rpitit_def_id, |param, _| match param.kind { + ty::GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), + ty::GenericParamDefKind::Type { .. } + | ty::GenericParamDefKind::Const { .. } => { + unreachable!("rpitit should have no addition ty/ct") + } + }); + let dyn_star_ty = Ty::new_dynamic( + tcx, + tcx.item_bounds_to_existential_predicates(rpitit_def_id, rpitit_args), + tcx.lifetimes.re_erased, + ty::DynStar, + ); + let mut inputs_and_output = sig.inputs_and_output.to_vec(); + *inputs_and_output.last_mut().unwrap() = dyn_star_ty; + sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output); + } + sig } ty::Closure(def_id, args) => { @@ -473,20 +484,30 @@ fn fn_abi_sanity_check<'tcx>( // This really shouldn't happen even for sized aggregates, since // `immediate_llvm_type` will use `layout.fields` to turn this Rust type into an // LLVM type. This means all sorts of Rust type details leak into the ABI. - // However wasm sadly *does* currently use this mode so we have to allow it -- - // but we absolutely shouldn't let any more targets do that. - // (Also see <https://github.com/rust-lang/rust/issues/115666>.) + // However wasm sadly *does* currently use this mode for it's "C" ABI so we + // have to allow it -- but we absolutely shouldn't let any more targets do + // that. (Also see <https://github.com/rust-lang/rust/issues/115666>.) // // The unstable abi `PtxKernel` also uses Direct for now. // It needs to switch to something else before stabilization can happen. // (See issue: https://github.com/rust-lang/rust/issues/117271) - assert!( - matches!(&*tcx.sess.target.arch, "wasm32" | "wasm64") - || matches!(spec_abi, ExternAbi::PtxKernel | ExternAbi::Unadjusted), - "`PassMode::Direct` for aggregates only allowed for \"unadjusted\" and \"ptx-kernel\" functions and on wasm\n\ - Problematic type: {:#?}", - arg.layout, - ); + // + // And finally the unadjusted ABI is ill specified and uses Direct for all + // args, but unfortunately we need it for calling certain LLVM intrinsics. + + match spec_abi { + ExternAbi::Unadjusted => {} + ExternAbi::PtxKernel => {} + ExternAbi::C { unwind: _ } + if matches!(&*tcx.sess.target.arch, "wasm32" | "wasm64") => {} + _ => { + panic!( + "`PassMode::Direct` for aggregates only allowed for \"unadjusted\" and \"ptx-kernel\" functions and on wasm\n\ + Problematic type: {:#?}", + arg.layout, + ); + } + } } } } diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index a201f2b1c11..f45c94127bd 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -136,6 +136,9 @@ pub trait Ty<I: Interner<Ty = Self>>: matches!(self.kind(), ty::FnPtr(..)) } + /// Checks whether this type is an ADT that has unsafe fields. + fn has_unsafe_fields(self) -> bool; + fn fn_sig(self, interner: I) -> ty::Binder<I, ty::FnSig<I>> { match self.kind() { ty::FnPtr(sig_tys, hdr) => sig_tys.with(hdr), diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 0ae688848eb..025ec7ae896 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -270,6 +270,9 @@ pub trait Interner: fn trait_may_be_implemented_via_object(self, trait_def_id: Self::DefId) -> bool; + /// Returns `true` if this is an `unsafe trait`. + fn trait_is_unsafe(self, trait_def_id: Self::DefId) -> bool; + fn is_impl_trait_in_trait(self, def_id: Self::DefId) -> bool; fn delay_bug(self, msg: impl ToString) -> Self::ErrorGuaranteed; diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index 4213ef4803b..9a80f97e274 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -692,7 +692,7 @@ impl<I: Interner> ty::Binder<I, ProjectionPredicate<I>> { /// /// Note that this is not the `DefId` of the `TraitRef` containing this /// associated type, which is in `tcx.associated_item(projection_def_id()).container`. - pub fn projection_def_id(&self) -> I::DefId { + pub fn item_def_id(&self) -> I::DefId { // Ok to skip binder since trait `DefId` does not care about regions. self.skip_binder().projection_term.def_id } diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index f96487cc53c..dfd090b3956 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -5,8 +5,8 @@ use serde::Serialize; use crate::compiler_interface::with; use crate::mir::pretty::function_body; use crate::ty::{ - AdtDef, ClosureDef, CoroutineDef, GenericArgs, MirConst, Movability, Region, RigidTy, Ty, - TyConst, TyKind, VariantIdx, + AdtDef, ClosureDef, CoroutineClosureDef, CoroutineDef, GenericArgs, MirConst, Movability, + Region, RigidTy, Ty, TyConst, TyKind, VariantIdx, }; use crate::{Error, Opaque, Span, Symbol}; @@ -617,6 +617,9 @@ impl Rvalue { AggregateKind::Coroutine(def, ref args, mov) => { Ok(Ty::new_coroutine(def, args.clone(), mov)) } + AggregateKind::CoroutineClosure(def, ref args) => { + Ok(Ty::new_coroutine_closure(def, args.clone())) + } AggregateKind::RawPtr(ty, mutability) => Ok(Ty::new_ptr(ty, mutability)), }, Rvalue::ShallowInitBox(_, ty) => Ok(Ty::new_box(*ty)), @@ -633,6 +636,7 @@ pub enum AggregateKind { Closure(ClosureDef, GenericArgs), // FIXME(stable_mir): Movability here is redundant Coroutine(CoroutineDef, GenericArgs, Movability), + CoroutineClosure(CoroutineClosureDef, GenericArgs), RawPtr(Ty, Mutability), } diff --git a/compiler/stable_mir/src/mir/pretty.rs b/compiler/stable_mir/src/mir/pretty.rs index 01a50d46b2d..93ed32e258a 100644 --- a/compiler/stable_mir/src/mir/pretty.rs +++ b/compiler/stable_mir/src/mir/pretty.rs @@ -410,6 +410,10 @@ fn pretty_aggregate<W: Write>( write!(writer, "{{coroutine@{}}}(", def.span().diagnostic())?; ")" } + AggregateKind::CoroutineClosure(def, _) => { + write!(writer, "{{coroutine-closure@{}}}(", def.span().diagnostic())?; + ")" + } AggregateKind::RawPtr(ty, mutability) => { write!( writer, diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs index 9ce72f155f9..d7eb435e13f 100644 --- a/compiler/stable_mir/src/ty.rs +++ b/compiler/stable_mir/src/ty.rs @@ -63,6 +63,11 @@ impl Ty { Ty::from_rigid_kind(RigidTy::Coroutine(def, args, mov)) } + /// Create a new closure type. + pub fn new_coroutine_closure(def: CoroutineClosureDef, args: GenericArgs) -> Ty { + Ty::from_rigid_kind(RigidTy::CoroutineClosure(def, args)) + } + /// Create a new box type that represents `Box<T>`, for the given inner type `T`. pub fn new_box(inner_ty: Ty) -> Ty { with(|cx| cx.new_box_ty(inner_ty)) @@ -550,6 +555,7 @@ pub enum RigidTy { Closure(ClosureDef, GenericArgs), // FIXME(stable_mir): Movability here is redundant Coroutine(CoroutineDef, GenericArgs, Movability), + CoroutineClosure(CoroutineClosureDef, GenericArgs), Dynamic(Vec<Binder<ExistentialPredicate>>, Region, DynKind), Never, Tuple(Vec<Ty>), @@ -742,6 +748,11 @@ crate_def! { crate_def! { #[derive(Serialize)] + pub CoroutineClosureDef; +} + +crate_def! { + #[derive(Serialize)] pub ParamDef; } diff --git a/compiler/stable_mir/src/visitor.rs b/compiler/stable_mir/src/visitor.rs index 48260285408..3533ed2e851 100644 --- a/compiler/stable_mir/src/visitor.rs +++ b/compiler/stable_mir/src/visitor.rs @@ -168,6 +168,7 @@ impl Visitable for RigidTy { | RigidTy::Closure(_, args) | RigidTy::Coroutine(_, args, _) | RigidTy::CoroutineWitness(_, args) + | RigidTy::CoroutineClosure(_, args) | RigidTy::FnDef(_, args) => args.visit(visitor), RigidTy::FnPtr(sig) => sig.visit(visitor), RigidTy::Dynamic(pred, r, _) => { |
