diff options
Diffstat (limited to 'compiler')
527 files changed, 10575 insertions, 8833 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 09a8954a9a7..03dcc3c21b7 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -23,8 +23,8 @@ pub use GenericArgs::*; pub use UnsafeSource::*; use crate::ptr::P; -use crate::token::{self, CommentKind, Delimiter, Token, TokenKind}; -use crate::tokenstream::{DelimSpan, LazyTokenStream, TokenStream, TokenTree}; +use crate::token::{self, CommentKind, Delimiter}; +use crate::tokenstream::{DelimSpan, LazyTokenStream, TokenStream}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -444,8 +444,7 @@ impl Default for Generics { pub struct WhereClause { /// `true` if we ate a `where` token: this can happen /// if we parsed no predicates (e.g. `struct Foo where {}`). - /// This allows us to accurately pretty-print - /// in `nt_to_tokenstream` + /// This allows us to pretty-print accurately. pub has_where_token: bool, pub predicates: Vec<WherePredicate>, pub span: Span, @@ -573,7 +572,7 @@ pub struct Block { pub span: Span, pub tokens: Option<LazyTokenStream>, /// The following *isn't* a parse error, but will cause multiple errors in following stages. - /// ``` + /// ```compile_fail /// let x = { /// foo: var /// }; @@ -929,16 +928,6 @@ pub struct Stmt { } impl Stmt { - pub fn tokens(&self) -> Option<&LazyTokenStream> { - match self.kind { - StmtKind::Local(ref local) => local.tokens.as_ref(), - StmtKind::Item(ref item) => item.tokens.as_ref(), - StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => expr.tokens.as_ref(), - StmtKind::Empty => None, - StmtKind::MacCall(ref mac) => mac.tokens.as_ref(), - } - } - pub fn has_trailing_semicolon(&self) -> bool { match &self.kind { StmtKind::Semi(_) => true, @@ -1581,20 +1570,7 @@ impl MacArgs { match self { MacArgs::Empty => TokenStream::default(), MacArgs::Delimited(.., tokens) => tokens.clone(), - MacArgs::Eq(_, MacArgsEq::Ast(expr)) => { - // Currently only literals are allowed here. If more complex expression kinds are - // allowed in the future, then `nt_to_tokenstream` should be used to extract the - // token stream. This will require some cleverness, perhaps with a function - // pointer, because `nt_to_tokenstream` is not directly usable from this crate. - // It will also require changing the `parse_expr` call in `parse_mac_args_common` - // to `parse_expr_force_collect`. - if let ExprKind::Lit(lit) = &expr.kind { - let token = Token::new(TokenKind::Literal(lit.token), lit.span); - TokenTree::Token(token).into() - } else { - unreachable!("couldn't extract literal when getting inner tokens: {:?}", expr) - } - } + MacArgs::Eq(_, MacArgsEq::Ast(expr)) => TokenStream::from_ast(expr), MacArgs::Eq(_, MacArgsEq::Hir(lit)) => { unreachable!("in literal form when getting inner tokens: {:?}", lit) } @@ -1986,6 +1962,8 @@ pub struct BareFnTy { pub ext: Extern, pub generic_params: Vec<GenericParam>, pub decl: P<FnDecl>, + /// Span of the `fn(...) -> ...` part. + pub decl_span: Span, } /// The various kinds of type recognized by the compiler. @@ -2684,13 +2662,6 @@ impl Item { } } -impl<K: Into<ItemKind>> Item<K> { - pub fn into_item(self) -> Item { - let Item { attrs, id, span, vis, ident, kind, tokens } = self; - Item { attrs, id, span, vis, ident, kind: kind.into(), tokens } - } -} - /// `extern` qualifier on a function item or function type. #[derive(Clone, Copy, Encodable, Decodable, Debug)] pub enum Extern { diff --git a/compiler/rustc_ast/src/ast_like.rs b/compiler/rustc_ast/src/ast_like.rs deleted file mode 100644 index 1a271b0adef..00000000000 --- a/compiler/rustc_ast/src/ast_like.rs +++ /dev/null @@ -1,320 +0,0 @@ -use super::ptr::P; -use super::token::Nonterminal; -use super::tokenstream::LazyTokenStream; -use super::{Arm, Crate, ExprField, FieldDef, GenericParam, Param, PatField, Variant}; -use super::{AssocItem, Expr, ForeignItem, Item, Local, MacCallStmt}; -use super::{AttrItem, AttrKind, Block, Pat, Path, Ty, Visibility}; -use super::{AttrVec, Attribute, Stmt, StmtKind}; - -use std::fmt; -use std::marker::PhantomData; - -/// An `AstLike` represents an AST node (or some wrapper around -/// and AST node) which stores some combination of attributes -/// and tokens. -pub trait AstLike: Sized + fmt::Debug { - /// This is `true` if this `AstLike` might support 'custom' (proc-macro) inner - /// attributes. Attributes like `#![cfg]` and `#![cfg_attr]` are not - /// considered 'custom' attributes - /// - /// If this is `false`, then this `AstLike` definitely does - /// not support 'custom' inner attributes, which enables some optimizations - /// during token collection. - const SUPPORTS_CUSTOM_INNER_ATTRS: bool; - fn attrs(&self) -> &[Attribute]; - fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)); - fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>>; -} - -impl<T: AstLike + 'static> AstLike for P<T> { - const SUPPORTS_CUSTOM_INNER_ATTRS: bool = T::SUPPORTS_CUSTOM_INNER_ATTRS; - fn attrs(&self) -> &[Attribute] { - (**self).attrs() - } - fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) { - (**self).visit_attrs(f); - } - fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> { - (**self).tokens_mut() - } -} - -impl AstLike for crate::token::Nonterminal { - const SUPPORTS_CUSTOM_INNER_ATTRS: bool = true; - fn attrs(&self) -> &[Attribute] { - match self { - Nonterminal::NtItem(item) => item.attrs(), - Nonterminal::NtStmt(stmt) => stmt.attrs(), - Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.attrs(), - Nonterminal::NtPat(_) - | Nonterminal::NtTy(_) - | Nonterminal::NtMeta(_) - | Nonterminal::NtPath(_) - | Nonterminal::NtVis(_) - | Nonterminal::NtBlock(_) - | Nonterminal::NtIdent(..) - | Nonterminal::NtLifetime(_) => &[], - } - } - fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) { - match self { - Nonterminal::NtItem(item) => item.visit_attrs(f), - Nonterminal::NtStmt(stmt) => stmt.visit_attrs(f), - Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.visit_attrs(f), - Nonterminal::NtPat(_) - | Nonterminal::NtTy(_) - | Nonterminal::NtMeta(_) - | Nonterminal::NtPath(_) - | Nonterminal::NtVis(_) - | Nonterminal::NtBlock(_) - | Nonterminal::NtIdent(..) - | Nonterminal::NtLifetime(_) => {} - } - } - fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> { - match self { - Nonterminal::NtItem(item) => item.tokens_mut(), - Nonterminal::NtStmt(stmt) => stmt.tokens_mut(), - Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.tokens_mut(), - Nonterminal::NtPat(pat) => pat.tokens_mut(), - Nonterminal::NtTy(ty) => ty.tokens_mut(), - Nonterminal::NtMeta(attr_item) => attr_item.tokens_mut(), - Nonterminal::NtPath(path) => path.tokens_mut(), - Nonterminal::NtVis(vis) => vis.tokens_mut(), - Nonterminal::NtBlock(block) => block.tokens_mut(), - Nonterminal::NtIdent(..) | Nonterminal::NtLifetime(..) => None, - } - } -} - -fn visit_attrvec(attrs: &mut AttrVec, f: impl FnOnce(&mut Vec<Attribute>)) { - crate::mut_visit::visit_clobber(attrs, |attrs| { - let mut vec = attrs.into(); - f(&mut vec); - vec.into() - }); -} - -impl AstLike for StmtKind { - // This might be an `StmtKind::Item`, which contains - // an item that supports inner attrs - const SUPPORTS_CUSTOM_INNER_ATTRS: bool = true; - - fn attrs(&self) -> &[Attribute] { - match self { - StmtKind::Local(local) => local.attrs(), - StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.attrs(), - StmtKind::Item(item) => item.attrs(), - StmtKind::Empty => &[], - StmtKind::MacCall(mac) => &mac.attrs, - } - } - - fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) { - match self { - StmtKind::Local(local) => local.visit_attrs(f), - StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.visit_attrs(f), - StmtKind::Item(item) => item.visit_attrs(f), - StmtKind::Empty => {} - StmtKind::MacCall(mac) => visit_attrvec(&mut mac.attrs, f), - } - } - fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> { - Some(match self { - StmtKind::Local(local) => &mut local.tokens, - StmtKind::Item(item) => &mut item.tokens, - StmtKind::Expr(expr) | StmtKind::Semi(expr) => &mut expr.tokens, - StmtKind::Empty => return None, - StmtKind::MacCall(mac) => &mut mac.tokens, - }) - } -} - -impl AstLike for Stmt { - const SUPPORTS_CUSTOM_INNER_ATTRS: bool = StmtKind::SUPPORTS_CUSTOM_INNER_ATTRS; - - fn attrs(&self) -> &[Attribute] { - self.kind.attrs() - } - - fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) { - self.kind.visit_attrs(f); - } - fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> { - self.kind.tokens_mut() - } -} - -impl AstLike for Attribute { - const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false; - - fn attrs(&self) -> &[Attribute] { - &[] - } - fn visit_attrs(&mut self, _f: impl FnOnce(&mut Vec<Attribute>)) {} - fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> { - Some(match &mut self.kind { - AttrKind::Normal(_, tokens) => tokens, - kind @ AttrKind::DocComment(..) => { - panic!("Called tokens_mut on doc comment attr {:?}", kind) - } - }) - } -} - -impl<T: AstLike> AstLike for Option<T> { - const SUPPORTS_CUSTOM_INNER_ATTRS: bool = T::SUPPORTS_CUSTOM_INNER_ATTRS; - - fn attrs(&self) -> &[Attribute] { - self.as_ref().map(|inner| inner.attrs()).unwrap_or(&[]) - } - fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) { - if let Some(inner) = self.as_mut() { - inner.visit_attrs(f); - } - } - fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> { - self.as_mut().and_then(|inner| inner.tokens_mut()) - } -} - -/// Helper trait for the macros below. Abstracts over -/// the two types of attribute fields that AST nodes -/// may have (`Vec<Attribute>` or `AttrVec`) -trait VecOrAttrVec { - fn visit(&mut self, f: impl FnOnce(&mut Vec<Attribute>)); -} - -impl VecOrAttrVec for Vec<Attribute> { - fn visit(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) { - f(self) - } -} - -impl VecOrAttrVec for AttrVec { - fn visit(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) { - visit_attrvec(self, f) - } -} - -macro_rules! derive_has_tokens_and_attrs { - ( - const SUPPORTS_CUSTOM_INNER_ATTRS: bool = $inner_attrs:literal; - $($ty:path),* - ) => { $( - impl AstLike for $ty { - const SUPPORTS_CUSTOM_INNER_ATTRS: bool = $inner_attrs; - - fn attrs(&self) -> &[Attribute] { - &self.attrs - } - - fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) { - VecOrAttrVec::visit(&mut self.attrs, f) - } - - fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> { - Some(&mut self.tokens) - } - - } - )* } -} - -macro_rules! derive_has_attrs_no_tokens { - ($($ty:path),*) => { $( - impl AstLike for $ty { - const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false; - - fn attrs(&self) -> &[Attribute] { - &self.attrs - } - - fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) { - VecOrAttrVec::visit(&mut self.attrs, f) - } - - fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> { - None - } - } - )* } -} - -macro_rules! derive_has_tokens_no_attrs { - ($($ty:path),*) => { $( - impl AstLike for $ty { - const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false; - - fn attrs(&self) -> &[Attribute] { - &[] - } - - fn visit_attrs(&mut self, _f: impl FnOnce(&mut Vec<Attribute>)) {} - fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> { - Some(&mut self.tokens) - } - } - )* } -} - -// These ast nodes support both active and inert attributes, -// so they have tokens collected to pass to proc macros -derive_has_tokens_and_attrs! { - // Both `Item` and `AssocItem` can have bodies, which - // can contain inner attributes - const SUPPORTS_CUSTOM_INNER_ATTRS: bool = true; - Item, AssocItem, ForeignItem -} - -derive_has_tokens_and_attrs! { - const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false; - Local, MacCallStmt, Expr -} - -// These ast nodes only support inert attributes, so they don't -// store tokens (since nothing can observe them) -derive_has_attrs_no_tokens! { - FieldDef, Arm, ExprField, PatField, Variant, Param, GenericParam, Crate -} - -// These AST nodes don't support attributes, but can -// be captured by a `macro_rules!` matcher. Therefore, -// they need to store tokens. -derive_has_tokens_no_attrs! { - Ty, Block, AttrItem, Pat, Path, Visibility -} - -/// A newtype around an `AstLike` node that implements `AstLike` itself. -pub struct AstLikeWrapper<Wrapped, Tag> { - pub wrapped: Wrapped, - pub tag: PhantomData<Tag>, -} - -impl<Wrapped, Tag> AstLikeWrapper<Wrapped, Tag> { - pub fn new(wrapped: Wrapped, _tag: Tag) -> AstLikeWrapper<Wrapped, Tag> { - AstLikeWrapper { wrapped, tag: Default::default() } - } -} - -impl<Wrapped: fmt::Debug, Tag> fmt::Debug for AstLikeWrapper<Wrapped, Tag> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("AstLikeWrapper") - .field("wrapped", &self.wrapped) - .field("tag", &self.tag) - .finish() - } -} - -impl<Wrapped: AstLike, Tag> AstLike for AstLikeWrapper<Wrapped, Tag> { - const SUPPORTS_CUSTOM_INNER_ATTRS: bool = Wrapped::SUPPORTS_CUSTOM_INNER_ATTRS; - fn attrs(&self) -> &[Attribute] { - self.wrapped.attrs() - } - fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) { - self.wrapped.visit_attrs(f) - } - fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> { - self.wrapped.tokens_mut() - } -} diff --git a/compiler/rustc_ast/src/ast_traits.rs b/compiler/rustc_ast/src/ast_traits.rs new file mode 100644 index 00000000000..5c30a75a140 --- /dev/null +++ b/compiler/rustc_ast/src/ast_traits.rs @@ -0,0 +1,442 @@ +//! A set of traits implemented for various AST nodes, +//! typically those used in AST fragments during macro expansion. +//! The traits are not implemented exhaustively, only when actually necessary. + +use crate::ptr::P; +use crate::token::Nonterminal; +use crate::tokenstream::LazyTokenStream; +use crate::{Arm, Crate, ExprField, FieldDef, GenericParam, Param, PatField, Variant}; +use crate::{AssocItem, Expr, ForeignItem, Item, NodeId}; +use crate::{AttrItem, AttrKind, Block, Pat, Path, Ty, Visibility}; +use crate::{AttrVec, Attribute, Stmt, StmtKind}; + +use rustc_span::Span; + +use std::fmt; +use std::marker::PhantomData; + +/// A utility trait to reduce boilerplate. +/// Standard `Deref(Mut)` cannot be reused due to coherence. +pub trait AstDeref { + type Target; + fn ast_deref(&self) -> &Self::Target; + fn ast_deref_mut(&mut self) -> &mut Self::Target; +} + +macro_rules! impl_not_ast_deref { + ($($T:ty),+ $(,)?) => { + $( + impl !AstDeref for $T {} + )+ + }; +} + +impl_not_ast_deref!(AssocItem, Expr, ForeignItem, Item, Stmt); + +impl<T> AstDeref for P<T> { + type Target = T; + fn ast_deref(&self) -> &Self::Target { + self + } + fn ast_deref_mut(&mut self) -> &mut Self::Target { + self + } +} + +/// A trait for AST nodes having an ID. +pub trait HasNodeId { + fn node_id(&self) -> NodeId; + fn node_id_mut(&mut self) -> &mut NodeId; +} + +macro_rules! impl_has_node_id { + ($($T:ty),+ $(,)?) => { + $( + impl HasNodeId for $T { + fn node_id(&self) -> NodeId { + self.id + } + fn node_id_mut(&mut self) -> &mut NodeId { + &mut self.id + } + } + )+ + }; +} + +impl_has_node_id!( + Arm, + AssocItem, + Crate, + Expr, + ExprField, + FieldDef, + ForeignItem, + GenericParam, + Item, + Param, + Pat, + PatField, + Stmt, + Ty, + Variant, +); + +impl<T: AstDeref<Target: HasNodeId>> HasNodeId for T { + fn node_id(&self) -> NodeId { + self.ast_deref().node_id() + } + fn node_id_mut(&mut self) -> &mut NodeId { + self.ast_deref_mut().node_id_mut() + } +} + +/// A trait for AST nodes having a span. +pub trait HasSpan { + fn span(&self) -> Span; +} + +macro_rules! impl_has_span { + ($($T:ty),+ $(,)?) => { + $( + impl HasSpan for $T { + fn span(&self) -> Span { + self.span + } + } + )+ + }; +} + +impl_has_span!(AssocItem, Block, Expr, ForeignItem, Item, Pat, Path, Stmt, Ty, Visibility); + +impl<T: AstDeref<Target: HasSpan>> HasSpan for T { + fn span(&self) -> Span { + self.ast_deref().span() + } +} + +impl HasSpan for AttrItem { + fn span(&self) -> Span { + self.span() + } +} + +/// A trait for AST nodes having (or not having) collected tokens. +pub trait HasTokens { + fn tokens(&self) -> Option<&LazyTokenStream>; + fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>>; +} + +macro_rules! impl_has_tokens { + ($($T:ty),+ $(,)?) => { + $( + impl HasTokens for $T { + fn tokens(&self) -> Option<&LazyTokenStream> { + self.tokens.as_ref() + } + fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> { + Some(&mut self.tokens) + } + } + )+ + }; +} + +macro_rules! impl_has_tokens_none { + ($($T:ty),+ $(,)?) => { + $( + impl HasTokens for $T { + fn tokens(&self) -> Option<&LazyTokenStream> { + None + } + fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> { + None + } + } + )+ + }; +} + +impl_has_tokens!(AssocItem, AttrItem, Block, Expr, ForeignItem, Item, Pat, Path, Ty, Visibility); +impl_has_tokens_none!(Arm, ExprField, FieldDef, GenericParam, Param, PatField, Variant); + +impl<T: AstDeref<Target: HasTokens>> HasTokens for T { + fn tokens(&self) -> Option<&LazyTokenStream> { + self.ast_deref().tokens() + } + fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> { + self.ast_deref_mut().tokens_mut() + } +} + +impl<T: HasTokens> HasTokens for Option<T> { + fn tokens(&self) -> Option<&LazyTokenStream> { + self.as_ref().and_then(|inner| inner.tokens()) + } + fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> { + self.as_mut().and_then(|inner| inner.tokens_mut()) + } +} + +impl HasTokens for StmtKind { + fn tokens(&self) -> Option<&LazyTokenStream> { + match self { + StmtKind::Local(local) => local.tokens.as_ref(), + StmtKind::Item(item) => item.tokens(), + StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.tokens(), + StmtKind::Empty => return None, + StmtKind::MacCall(mac) => mac.tokens.as_ref(), + } + } + fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> { + match self { + StmtKind::Local(local) => Some(&mut local.tokens), + StmtKind::Item(item) => item.tokens_mut(), + StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.tokens_mut(), + StmtKind::Empty => return None, + StmtKind::MacCall(mac) => Some(&mut mac.tokens), + } + } +} + +impl HasTokens for Stmt { + fn tokens(&self) -> Option<&LazyTokenStream> { + self.kind.tokens() + } + fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> { + self.kind.tokens_mut() + } +} + +impl HasTokens for Attribute { + fn tokens(&self) -> Option<&LazyTokenStream> { + match &self.kind { + AttrKind::Normal(_, tokens) => tokens.as_ref(), + kind @ AttrKind::DocComment(..) => { + panic!("Called tokens on doc comment attr {:?}", kind) + } + } + } + fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> { + Some(match &mut self.kind { + AttrKind::Normal(_, tokens) => tokens, + kind @ AttrKind::DocComment(..) => { + panic!("Called tokens_mut on doc comment attr {:?}", kind) + } + }) + } +} + +impl HasTokens for Nonterminal { + fn tokens(&self) -> Option<&LazyTokenStream> { + match self { + Nonterminal::NtItem(item) => item.tokens(), + Nonterminal::NtStmt(stmt) => stmt.tokens(), + Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.tokens(), + Nonterminal::NtPat(pat) => pat.tokens(), + Nonterminal::NtTy(ty) => ty.tokens(), + Nonterminal::NtMeta(attr_item) => attr_item.tokens(), + Nonterminal::NtPath(path) => path.tokens(), + Nonterminal::NtVis(vis) => vis.tokens(), + Nonterminal::NtBlock(block) => block.tokens(), + Nonterminal::NtIdent(..) | Nonterminal::NtLifetime(..) => None, + } + } + fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> { + match self { + Nonterminal::NtItem(item) => item.tokens_mut(), + Nonterminal::NtStmt(stmt) => stmt.tokens_mut(), + Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.tokens_mut(), + Nonterminal::NtPat(pat) => pat.tokens_mut(), + Nonterminal::NtTy(ty) => ty.tokens_mut(), + Nonterminal::NtMeta(attr_item) => attr_item.tokens_mut(), + Nonterminal::NtPath(path) => path.tokens_mut(), + Nonterminal::NtVis(vis) => vis.tokens_mut(), + Nonterminal::NtBlock(block) => block.tokens_mut(), + Nonterminal::NtIdent(..) | Nonterminal::NtLifetime(..) => None, + } + } +} + +/// A trait for AST nodes having (or not having) attributes. +pub trait HasAttrs { + /// This is `true` if this `HasAttrs` might support 'custom' (proc-macro) inner + /// attributes. Attributes like `#![cfg]` and `#![cfg_attr]` are not + /// considered 'custom' attributes. + /// + /// If this is `false`, then this `HasAttrs` definitely does + /// not support 'custom' inner attributes, which enables some optimizations + /// during token collection. + const SUPPORTS_CUSTOM_INNER_ATTRS: bool; + fn attrs(&self) -> &[Attribute]; + fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)); +} + +macro_rules! impl_has_attrs { + (const SUPPORTS_CUSTOM_INNER_ATTRS: bool = $inner:literal, $($T:ty),+ $(,)?) => { + $( + impl HasAttrs for $T { + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = $inner; + + fn attrs(&self) -> &[Attribute] { + &self.attrs + } + + fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) { + VecOrAttrVec::visit(&mut self.attrs, f) + } + } + )+ + }; +} + +macro_rules! impl_has_attrs_none { + ($($T:ty),+ $(,)?) => { + $( + impl HasAttrs for $T { + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false; + fn attrs(&self) -> &[Attribute] { + &[] + } + fn visit_attrs(&mut self, _f: impl FnOnce(&mut Vec<Attribute>)) {} + } + )+ + }; +} + +impl_has_attrs!( + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = true, + AssocItem, + ForeignItem, + Item, +); +impl_has_attrs!( + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false, + Arm, + Crate, + Expr, + ExprField, + FieldDef, + GenericParam, + Param, + PatField, + Variant, +); +impl_has_attrs_none!(Attribute, AttrItem, Block, Pat, Path, Ty, Visibility); + +impl<T: AstDeref<Target: HasAttrs>> HasAttrs for T { + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = T::Target::SUPPORTS_CUSTOM_INNER_ATTRS; + fn attrs(&self) -> &[Attribute] { + self.ast_deref().attrs() + } + fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) { + self.ast_deref_mut().visit_attrs(f) + } +} + +impl<T: HasAttrs> HasAttrs for Option<T> { + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = T::SUPPORTS_CUSTOM_INNER_ATTRS; + fn attrs(&self) -> &[Attribute] { + self.as_ref().map(|inner| inner.attrs()).unwrap_or(&[]) + } + fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) { + if let Some(inner) = self.as_mut() { + inner.visit_attrs(f); + } + } +} + +impl HasAttrs for StmtKind { + // This might be a `StmtKind::Item`, which contains + // an item that supports inner attrs. + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = true; + + fn attrs(&self) -> &[Attribute] { + match self { + StmtKind::Local(local) => &local.attrs, + StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.attrs(), + StmtKind::Item(item) => item.attrs(), + StmtKind::Empty => &[], + StmtKind::MacCall(mac) => &mac.attrs, + } + } + + fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) { + match self { + StmtKind::Local(local) => visit_attrvec(&mut local.attrs, f), + StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.visit_attrs(f), + StmtKind::Item(item) => item.visit_attrs(f), + StmtKind::Empty => {} + StmtKind::MacCall(mac) => visit_attrvec(&mut mac.attrs, f), + } + } +} + +impl HasAttrs for Stmt { + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = StmtKind::SUPPORTS_CUSTOM_INNER_ATTRS; + fn attrs(&self) -> &[Attribute] { + self.kind.attrs() + } + fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) { + self.kind.visit_attrs(f); + } +} + +/// Helper trait for the impls above. Abstracts over +/// the two types of attribute fields that AST nodes +/// may have (`Vec<Attribute>` or `AttrVec`). +trait VecOrAttrVec { + fn visit(&mut self, f: impl FnOnce(&mut Vec<Attribute>)); +} + +impl VecOrAttrVec for Vec<Attribute> { + fn visit(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) { + f(self) + } +} + +impl VecOrAttrVec for AttrVec { + fn visit(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) { + visit_attrvec(self, f) + } +} + +fn visit_attrvec(attrs: &mut AttrVec, f: impl FnOnce(&mut Vec<Attribute>)) { + crate::mut_visit::visit_clobber(attrs, |attrs| { + let mut vec = attrs.into(); + f(&mut vec); + vec.into() + }); +} + +/// A newtype around an AST node that implements the traits above if the node implements them. +pub struct AstNodeWrapper<Wrapped, Tag> { + pub wrapped: Wrapped, + pub tag: PhantomData<Tag>, +} + +impl<Wrapped, Tag> AstNodeWrapper<Wrapped, Tag> { + pub fn new(wrapped: Wrapped, _tag: Tag) -> AstNodeWrapper<Wrapped, Tag> { + AstNodeWrapper { wrapped, tag: Default::default() } + } +} + +impl<Wrapped, Tag> AstDeref for AstNodeWrapper<Wrapped, Tag> { + type Target = Wrapped; + fn ast_deref(&self) -> &Self::Target { + &self.wrapped + } + fn ast_deref_mut(&mut self) -> &mut Self::Target { + &mut self.wrapped + } +} + +impl<Wrapped: fmt::Debug, Tag> fmt::Debug for AstNodeWrapper<Wrapped, Tag> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("AstNodeWrapper") + .field("wrapped", &self.wrapped) + .field("tag", &self.tag) + .finish() + } +} diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 84654a9f737..988918b0505 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -340,7 +340,7 @@ pub fn mk_nested_word_item(ident: Ident) -> NestedMetaItem { NestedMetaItem::MetaItem(mk_word_item(ident)) } -crate fn mk_attr_id() -> AttrId { +pub(crate) fn mk_attr_id() -> AttrId { use std::sync::atomic::AtomicU32; use std::sync::atomic::Ordering; @@ -552,7 +552,7 @@ impl MetaItemKind { ) -> Option<MetaItemKind> { match tokens.next() { Some(TokenTree::Delimited(_, Delimiter::Invisible, inner_tokens)) => { - MetaItemKind::name_value_from_tokens(&mut inner_tokens.trees()) + MetaItemKind::name_value_from_tokens(&mut inner_tokens.into_trees()) } Some(TokenTree::Token(token)) => { Lit::from_token(&token).ok().map(MetaItemKind::NameValue) diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index a7c23dbb79c..2015d635e56 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -8,14 +8,15 @@ html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", test(attr(deny(warnings))) )] +#![feature(associated_type_bounds)] #![feature(box_patterns)] #![feature(const_default_impls)] #![feature(const_trait_impl)] -#![feature(crate_visibility_modifier)] #![feature(if_let_guard)] #![feature(label_break_value)] #![feature(let_chains)] #![feature(min_specialization)] +#![feature(negative_impls)] #![feature(nll)] #![feature(slice_internals)] #![feature(stmt_expr_attributes)] @@ -33,7 +34,7 @@ pub mod util { } pub mod ast; -pub mod ast_like; +pub mod ast_traits; pub mod attr; pub mod entry; pub mod expand; @@ -45,7 +46,7 @@ pub mod tokenstream; pub mod visit; pub use self::ast::*; -pub use self::ast_like::{AstLike, AstLikeWrapper}; +pub use self::ast_traits::{AstDeref, AstNodeWrapper, HasAttrs, HasNodeId, HasSpan, HasTokens}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index b425b5e2cca..1a93da8788a 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -460,10 +460,11 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) { vis.visit_mt(mt); } TyKind::BareFn(bft) => { - let BareFnTy { unsafety, ext: _, generic_params, decl } = bft.deref_mut(); + let BareFnTy { unsafety, ext: _, generic_params, decl, decl_span } = bft.deref_mut(); visit_unsafety(unsafety, vis); generic_params.flat_map_in_place(|param| vis.flat_map_generic_param(param)); vis.visit_fn_decl(decl); + vis.visit_span(decl_span); } TyKind::Tup(tys) => visit_vec(tys, |ty| vis.visit_ty(ty)), TyKind::Paren(ty) => vis.visit_ty(ty), diff --git a/compiler/rustc_ast/src/ptr.rs b/compiler/rustc_ast/src/ptr.rs index 89a0857992e..bab85a3019d 100644 --- a/compiler/rustc_ast/src/ptr.rs +++ b/compiler/rustc_ast/src/ptr.rs @@ -10,7 +10,7 @@ //! //! * **Immutability**: `P<T>` disallows mutating its inner `T`, unlike `Box<T>` //! (unless it contains an `Unsafe` interior, but that may be denied later). -//! This mainly prevents mistakes, but can also enforces a kind of "purity". +//! This mainly prevents mistakes, but also enforces a kind of "purity". //! //! * **Efficiency**: folding can reuse allocation space for `P<T>` and `Vec<T>`, //! the latter even when the input and output types differ (as it would be the diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 35eca23a116..1522d12cbf9 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -134,7 +134,7 @@ impl LitKind { } } - crate fn may_have_suffix(self) -> bool { + pub(crate) fn may_have_suffix(self) -> bool { matches!(self, Integer | Float | Err) } } diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index a8f29f33407..c58fe7287bf 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -13,7 +13,9 @@ //! and a borrowed `TokenStream` is sufficient to build an owned `TokenStream` without taking //! ownership of the original. -use crate::token::{self, Delimiter, Token, TokenKind}; +use crate::ast::StmtKind; +use crate::ast_traits::{HasAttrs, HasSpan, HasTokens}; +use crate::token::{self, Delimiter, Nonterminal, Token, TokenKind}; use crate::AttrVec; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; @@ -45,12 +47,6 @@ pub enum TokenTree { Delimited(DelimSpan, Delimiter, TokenStream), } -#[derive(Copy, Clone)] -pub enum CanSynthesizeMissingTokens { - Yes, - No, -} - // Ensure all fields of `TokenTree` is `Send` and `Sync`. #[cfg(parallel_compiler)] fn _dummy() @@ -442,8 +438,8 @@ impl TokenStream { } } - pub fn trees(&self) -> Cursor { - self.clone().into_trees() + pub fn trees(&self) -> CursorRef<'_> { + CursorRef::new(self) } pub fn into_trees(self) -> Cursor { @@ -471,6 +467,89 @@ impl TokenStream { .collect(), )) } + + fn opt_from_ast(node: &(impl HasAttrs + HasTokens)) -> Option<TokenStream> { + let tokens = node.tokens()?; + let attrs = node.attrs(); + let attr_annotated = if attrs.is_empty() { + tokens.create_token_stream() + } else { + let attr_data = AttributesData { attrs: attrs.to_vec().into(), tokens: tokens.clone() }; + AttrAnnotatedTokenStream::new(vec![( + AttrAnnotatedTokenTree::Attributes(attr_data), + Spacing::Alone, + )]) + }; + Some(attr_annotated.to_tokenstream()) + } + + pub fn from_ast(node: &(impl HasAttrs + HasSpan + HasTokens + fmt::Debug)) -> TokenStream { + TokenStream::opt_from_ast(node) + .unwrap_or_else(|| panic!("missing tokens for node at {:?}: {:?}", node.span(), node)) + } + + pub fn from_nonterminal_ast(nt: &Nonterminal) -> TokenStream { + match nt { + Nonterminal::NtIdent(ident, is_raw) => { + TokenTree::token(token::Ident(ident.name, *is_raw), ident.span).into() + } + Nonterminal::NtLifetime(ident) => { + TokenTree::token(token::Lifetime(ident.name), ident.span).into() + } + Nonterminal::NtItem(item) => TokenStream::from_ast(item), + Nonterminal::NtBlock(block) => TokenStream::from_ast(block), + Nonterminal::NtStmt(stmt) if let StmtKind::Empty = stmt.kind => { + // FIXME: Properly collect tokens for empty statements. + TokenTree::token(token::Semi, stmt.span).into() + } + Nonterminal::NtStmt(stmt) => TokenStream::from_ast(stmt), + Nonterminal::NtPat(pat) => TokenStream::from_ast(pat), + Nonterminal::NtTy(ty) => TokenStream::from_ast(ty), + Nonterminal::NtMeta(attr) => TokenStream::from_ast(attr), + Nonterminal::NtPath(path) => TokenStream::from_ast(path), + Nonterminal::NtVis(vis) => TokenStream::from_ast(vis), + Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => TokenStream::from_ast(expr), + } + } + + fn flatten_token(token: &Token) -> TokenTree { + match &token.kind { + token::Interpolated(nt) if let token::NtIdent(ident, is_raw) = **nt => { + TokenTree::token(token::Ident(ident.name, is_raw), ident.span) + } + token::Interpolated(nt) => TokenTree::Delimited( + DelimSpan::from_single(token.span), + Delimiter::Invisible, + TokenStream::from_nonterminal_ast(&nt).flattened(), + ), + _ => TokenTree::Token(token.clone()), + } + } + + fn flatten_token_tree(tree: &TokenTree) -> TokenTree { + match tree { + TokenTree::Token(token) => TokenStream::flatten_token(token), + TokenTree::Delimited(span, delim, tts) => { + TokenTree::Delimited(*span, *delim, tts.flattened()) + } + } + } + + #[must_use] + pub fn flattened(&self) -> TokenStream { + fn can_skip(stream: &TokenStream) -> bool { + stream.trees().all(|tree| match tree { + TokenTree::Token(token) => !matches!(token.kind, token::Interpolated(_)), + TokenTree::Delimited(_, _, inner) => can_skip(inner), + }) + } + + if can_skip(self) { + return self.clone(); + } + + self.trees().map(|tree| TokenStream::flatten_token_tree(tree)).collect() + } } // 99.5%+ of the time we have 1 or 2 elements in this vector. @@ -538,12 +617,21 @@ pub struct CursorRef<'t> { } impl<'t> CursorRef<'t> { + fn new(stream: &'t TokenStream) -> Self { + CursorRef { stream, index: 0 } + } + + #[inline] fn next_with_spacing(&mut self) -> Option<&'t TreeAndSpacing> { self.stream.0.get(self.index).map(|tree| { self.index += 1; tree }) } + + pub fn look_ahead(&self, n: usize) -> Option<&TokenTree> { + self.stream.0[self.index..].get(n).map(|(tree, _)| tree) + } } impl<'t> Iterator for CursorRef<'t> { diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index cc772ac74f2..2ce8590d771 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -89,6 +89,16 @@ impl<'a> FnKind<'a> { } } +#[derive(Copy, Clone, Debug)] +pub enum LifetimeCtxt { + /// Appears in a reference type. + Rptr, + /// Appears as a bound on a type or another lifetime. + Bound, + /// Appears as a generic argument. + GenericArg, +} + /// Each method of the `Visitor` trait is a hook to be potentially /// overridden. Each method's default implementation recursively visits /// the substructure of the input via the corresponding `walk` method; @@ -184,7 +194,7 @@ pub trait Visitor<'ast>: Sized { fn visit_label(&mut self, label: &'ast Label) { walk_label(self, label) } - fn visit_lifetime(&mut self, lifetime: &'ast Lifetime) { + fn visit_lifetime(&mut self, lifetime: &'ast Lifetime, _: LifetimeCtxt) { walk_lifetime(self, lifetime) } fn visit_mac_call(&mut self, mac: &'ast MacCall) { @@ -326,7 +336,7 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) { ItemKind::ForeignMod(ref foreign_module) => { walk_list!(visitor, visit_foreign_item, &foreign_module.items); } - ItemKind::GlobalAsm(ref asm) => walk_inline_asm(visitor, asm), + ItemKind::GlobalAsm(ref asm) => visitor.visit_inline_asm(asm), ItemKind::TyAlias(box TyAlias { ref generics, ref bounds, ref ty, .. }) => { visitor.visit_generics(generics); walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound); @@ -414,7 +424,7 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) { TyKind::Slice(ref ty) | TyKind::Paren(ref ty) => visitor.visit_ty(ty), TyKind::Ptr(ref mutable_type) => visitor.visit_ty(&mutable_type.ty), TyKind::Rptr(ref opt_lifetime, ref mutable_type) => { - walk_list!(visitor, visit_lifetime, opt_lifetime); + walk_list!(visitor, visit_lifetime, opt_lifetime, LifetimeCtxt::Rptr); visitor.visit_ty(&mutable_type.ty) } TyKind::Tup(ref tuple_element_types) => { @@ -507,7 +517,7 @@ where V: Visitor<'a>, { match generic_arg { - GenericArg::Lifetime(lt) => visitor.visit_lifetime(lt), + GenericArg::Lifetime(lt) => visitor.visit_lifetime(lt, LifetimeCtxt::GenericArg), GenericArg::Type(ty) => visitor.visit_ty(ty), GenericArg::Const(ct) => visitor.visit_anon_const(ct), } @@ -599,7 +609,9 @@ pub fn walk_foreign_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a ForeignI pub fn walk_param_bound<'a, V: Visitor<'a>>(visitor: &mut V, bound: &'a GenericBound) { match *bound { GenericBound::Trait(ref typ, ref modifier) => visitor.visit_poly_trait_ref(typ, modifier), - GenericBound::Outlives(ref lifetime) => visitor.visit_lifetime(lifetime), + GenericBound::Outlives(ref lifetime) => { + visitor.visit_lifetime(lifetime, LifetimeCtxt::Bound) + } } } @@ -639,7 +651,7 @@ pub fn walk_where_predicate<'a, V: Visitor<'a>>(visitor: &mut V, predicate: &'a WherePredicate::RegionPredicate(WhereRegionPredicate { ref lifetime, ref bounds, .. }) => { - visitor.visit_lifetime(lifetime); + visitor.visit_lifetime(lifetime, LifetimeCtxt::Bound); walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound); } WherePredicate::EqPredicate(WhereEqPredicate { ref lhs_ty, ref rhs_ty, .. }) => { @@ -897,7 +909,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { } ExprKind::MacCall(ref mac) => visitor.visit_mac_call(mac), ExprKind::Paren(ref subexpression) => visitor.visit_expr(subexpression), - ExprKind::InlineAsm(ref asm) => walk_inline_asm(visitor, asm), + ExprKind::InlineAsm(ref asm) => visitor.visit_inline_asm(asm), ExprKind::Yield(ref optional_expression) => { walk_list!(visitor, visit_expr, optional_expression); } diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index ae3e3675962..6c055645ef3 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -17,7 +17,11 @@ use std::collections::hash_map::Entry; use std::fmt::Write; impl<'a, 'hir> LoweringContext<'a, 'hir> { - crate fn lower_inline_asm(&mut self, sp: Span, asm: &InlineAsm) -> &'hir hir::InlineAsm<'hir> { + pub(crate) fn lower_inline_asm( + &mut self, + sp: Span, + asm: &InlineAsm, + ) -> &'hir hir::InlineAsm<'hir> { // Rustdoc needs to support asm! from foreign architectures: don't try // lowering the register constraints in this case. let asm_arch = if self.sess.opts.actually_rustdoc { None } else { self.sess.asm_arch }; diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index a1d994c2f90..3aff04f78fb 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -505,8 +505,14 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_arm(&mut self, arm: &Arm) -> hir::Arm<'hir> { let pat = self.lower_pat(&arm.pat); let guard = arm.guard.as_ref().map(|cond| { - if let ExprKind::Let(ref pat, ref scrutinee, _) = cond.kind { - hir::Guard::IfLet(self.lower_pat(pat), self.lower_expr(scrutinee)) + if let ExprKind::Let(ref pat, ref scrutinee, span) = cond.kind { + hir::Guard::IfLet(self.arena.alloc(hir::Let { + hir_id: self.next_id(), + span: self.lower_span(span), + pat: self.lower_pat(pat), + ty: None, + init: self.lower_expr(scrutinee), + })) } else { hir::Guard::If(self.lower_expr(cond)) } @@ -616,7 +622,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } /// Desugar `<expr>.await` into: - /// ```rust + /// ```ignore (pseudo-rust) /// match ::std::future::IntoFuture::into_future(<expr>) { /// mut __awaitee => loop { /// match unsafe { ::std::future::Future::poll( @@ -1363,7 +1369,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } /// Desugar `ExprForLoop` from: `[opt_ident]: for <pat> in <head> <body>` into: - /// ```rust + /// ```ignore (pseudo-rust) /// { /// let result = match IntoIterator::into_iter(<head>) { /// mut iter => { @@ -1474,7 +1480,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } /// Desugar `ExprKind::Try` from: `<expr>?` into: - /// ```rust + /// ```ignore (pseudo-rust) /// match Try::branch(<expr>) { /// ControlFlow::Continue(val) => #[allow(unreachable_code)] val,, /// ControlFlow::Break(residual) => diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 5a95e5b084a..12d1b8404eb 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -11,8 +11,8 @@ use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID}; +use rustc_hir::PredicateOrigin; use rustc_index::vec::{Idx, IndexVec}; -use rustc_session::utils::NtToTokenstream; use rustc_session::Session; use rustc_span::source_map::DesugaringKind; use rustc_span::symbol::{kw, sym, Ident}; @@ -26,7 +26,6 @@ use std::iter; pub(super) struct ItemLowerer<'a, 'hir> { pub(super) sess: &'a Session, pub(super) resolver: &'a mut dyn ResolverAstLowering, - pub(super) nt_to_tokenstream: NtToTokenstream, pub(super) arena: &'hir Arena<'hir>, pub(super) ast_index: &'a IndexVec<LocalDefId, AstOwner<'a>>, pub(super) owners: &'a mut IndexVec<LocalDefId, hir::MaybeOwner<&'hir hir::OwnerInfo<'hir>>>, @@ -62,7 +61,6 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> { // Pseudo-globals. sess: &self.sess, resolver: self.resolver, - nt_to_tokenstream: self.nt_to_tokenstream, arena: self.arena, // HirId handling. @@ -847,7 +845,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } /// Construct `ExprKind::Err` for the given `span`. - crate fn expr_err(&mut self, span: Span) -> hir::Expr<'hir> { + pub(crate) fn expr_err(&mut self, span: Span) -> hir::Expr<'hir> { self.expr(span, hir::ExprKind::Err, AttrVec::new()) } @@ -1346,7 +1344,13 @@ impl<'hir> LoweringContext<'_, 'hir> { let mut predicates = SmallVec::new(); predicates.extend(generics.params.iter().filter_map(|param| { let bounds = self.lower_param_bounds(¶m.bounds, itctx.reborrow()); - self.lower_generic_bound_predicate(param.ident, param.id, ¶m.kind, bounds) + self.lower_generic_bound_predicate( + param.ident, + param.id, + ¶m.kind, + bounds, + PredicateOrigin::GenericParam, + ) })); predicates.extend( generics @@ -1380,6 +1384,7 @@ impl<'hir> LoweringContext<'_, 'hir> { id: NodeId, kind: &GenericParamKind, bounds: &'hir [hir::GenericBound<'hir>], + origin: PredicateOrigin, ) -> Option<hir::WherePredicate<'hir>> { // Do not create a clause if we do not have anything inside it. if bounds.is_empty() { @@ -1419,7 +1424,7 @@ impl<'hir> LoweringContext<'_, 'hir> { bounds, span, bound_generic_params: &[], - in_where_clause: false, + origin, })) } GenericParamKind::Lifetime => { @@ -1458,7 +1463,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ) })), span: self.lower_span(span), - in_where_clause: true, + origin: PredicateOrigin::WhereClause, }), WherePredicate::RegionPredicate(WhereRegionPredicate { ref lifetime, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index c70ef501361..e59bc9aa6b3 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -30,7 +30,6 @@ //! get confused if the spans from leaf AST nodes occur in multiple places //! in the HIR, especially for multiple identifiers. -#![feature(crate_visibility_modifier)] #![feature(box_patterns)] #![feature(let_chains)] #![feature(let_else)] @@ -38,7 +37,6 @@ #![recursion_limit = "256"] #![allow(rustc::potential_query_instability)] -use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, TokenStream}; use rustc_ast::visit; use rustc_ast::{self as ast, *}; use rustc_ast_pretty::pprust; @@ -57,7 +55,6 @@ use rustc_hir::{ConstArg, GenericArg, ItemLocalId, ParamName, TraitCandidate}; use rustc_index::vec::{Idx, IndexVec}; use rustc_query_system::ich::StableHashingContext; use rustc_session::parse::feature_err; -use rustc_session::utils::{FlattenNonterminals, NtToTokenstream}; use rustc_session::Session; use rustc_span::hygiene::{ExpnId, MacroKind}; use rustc_span::source_map::DesugaringKind; @@ -90,11 +87,6 @@ struct LoweringContext<'a, 'hir: 'a> { resolver: &'a mut dyn ResolverAstLowering, - /// HACK(Centril): there is a cyclic dependency between the parser and lowering - /// if we don't have this function pointer. To avoid that dependency so that - /// `rustc_middle` is independent of the parser, we use dynamic dispatch here. - nt_to_tokenstream: NtToTokenstream, - /// Used to allocate HIR nodes. arena: &'hir Arena<'hir>, @@ -437,7 +429,6 @@ pub fn lower_crate<'a, 'hir>( sess: &'a Session, krate: &'a Crate, resolver: &'a mut dyn ResolverAstLowering, - nt_to_tokenstream: NtToTokenstream, arena: &'hir Arena<'hir>, ) -> &'hir hir::Crate<'hir> { let _prof_timer = sess.prof.verbose_generic_activity("hir_lowering"); @@ -448,15 +439,8 @@ pub fn lower_crate<'a, 'hir>( IndexVec::from_fn_n(|_| hir::MaybeOwner::Phantom, resolver.definitions().def_index_count()); for def_id in ast_index.indices() { - item::ItemLowerer { - sess, - resolver, - nt_to_tokenstream, - arena, - ast_index: &ast_index, - owners: &mut owners, - } - .lower_node(def_id); + item::ItemLowerer { sess, resolver, arena, ast_index: &ast_index, owners: &mut owners } + .lower_node(def_id); } let hir_hash = compute_hir_hash(resolver, &owners); @@ -876,11 +860,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // ``` // // In both cases, we don't want to synthesize any tokens - MacArgs::Delimited( - dspan, - delim, - self.lower_token_stream(tokens.clone(), CanSynthesizeMissingTokens::No), - ) + MacArgs::Delimited(dspan, delim, tokens.flattened()) } // 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 @@ -905,22 +885,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } - fn lower_token_stream( - &self, - tokens: TokenStream, - synthesize_tokens: CanSynthesizeMissingTokens, - ) -> TokenStream { - FlattenNonterminals { - parse_sess: &self.sess.parse_sess, - synthesize_tokens, - nt_to_tokenstream: self.nt_to_tokenstream, - } - .process_token_stream(tokens) - } - /// Given an associated type constraint like one of these: /// - /// ``` + /// ```ignore (illustrative) /// T: Iterator<Item: Debug> /// ^^^^^^^^^^^ /// T: Iterator<Item = Debug> @@ -1169,15 +1136,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { TyKind::Ptr(ref mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)), TyKind::Rptr(ref region, ref mt) => { let region = region.unwrap_or_else(|| { - let Some(LifetimeRes::ElidedAnchor { start, end }) = self.resolver.get_lifetime_res(t.id) else { - panic!() + let id = if let Some(LifetimeRes::ElidedAnchor { start, end }) = + self.resolver.get_lifetime_res(t.id) + { + debug_assert_eq!(start.plus(1), end); + start + } else { + self.resolver.next_node_id() }; - debug_assert_eq!(start.plus(1), end); let span = self.sess.source_map().next_point(t.span.shrink_to_lo()); - Lifetime { - ident: Ident::new(kw::UnderscoreLifetime, span), - id: start, - } + Lifetime { ident: Ident::new(kw::UnderscoreLifetime, span), id } }); let lifetime = self.lower_lifetime(®ion); hir::TyKind::Rptr(lifetime, self.lower_mt(mt, itctx)) @@ -1298,6 +1266,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { def_node_id, &GenericParamKind::Type { default: None }, hir_bounds, + hir::PredicateOrigin::ImplTrait, ) { in_band_ty_bounds.push(preds) } @@ -1835,10 +1804,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lower_lifetime(&mut self, l: &Lifetime) -> hir::Lifetime { let span = self.lower_span(l.ident.span); let ident = self.lower_ident(l.ident); - let res = self - .resolver - .get_lifetime_res(l.id) - .unwrap_or_else(|| panic!("Missing resolution for lifetime {:?} at {:?}", l, span)); + let res = self.resolver.get_lifetime_res(l.id).unwrap_or(LifetimeRes::Error); self.new_named_lifetime_with_res(l.id, span, ident, res) } diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index 2c331767b89..e27bc7a0f47 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -12,11 +12,11 @@ use rustc_span::symbol::Ident; use rustc_span::{source_map::Spanned, Span}; impl<'a, 'hir> LoweringContext<'a, 'hir> { - crate fn lower_pat(&mut self, pattern: &Pat) -> &'hir hir::Pat<'hir> { + pub(crate) fn lower_pat(&mut self, pattern: &Pat) -> &'hir hir::Pat<'hir> { self.arena.alloc(self.lower_pat_mut(pattern)) } - crate fn lower_pat_mut(&mut self, mut pattern: &Pat) -> hir::Pat<'hir> { + pub(crate) fn lower_pat_mut(&mut self, mut pattern: &Pat) -> hir::Pat<'hir> { ensure_sufficient_stack(|| { // loop here to avoid recursion let node = loop { @@ -290,7 +290,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } /// Emit a friendly error for extra `..` patterns in a tuple/tuple struct/slice pattern. - crate fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) { + pub(crate) fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) { self.diagnostic() .struct_span_err(sp, &format!("`..` can only be used once per {} pattern", ctx)) .span_label(sp, &format!("can only be used once per {} pattern", ctx)) diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 3c9399c1fdf..7fc8aac5116 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -15,7 +15,7 @@ use smallvec::smallvec; use tracing::debug; impl<'a, 'hir> LoweringContext<'a, 'hir> { - crate fn lower_qpath( + pub(crate) fn lower_qpath( &mut self, id: NodeId, qself: &Option<QSelf>, @@ -142,7 +142,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ); } - crate fn lower_path_extra( + pub(crate) fn lower_path_extra( &mut self, res: Res, p: &Path, @@ -163,7 +163,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }) } - crate fn lower_path( + pub(crate) fn lower_path( &mut self, id: NodeId, p: &Path, @@ -174,7 +174,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.lower_path_extra(res, p, param_mode) } - crate fn lower_path_segment( + pub(crate) fn lower_path_segment( &mut self, path_span: Span, segment: &PathSegment, @@ -381,7 +381,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } /// An associated type binding `Output = $ty`. - crate fn output_ty_binding( + pub(crate) fn output_ty_binding( &mut self, span: Span, ty: &'hir hir::Ty<'hir>, diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 058a0f975a7..14e5d2ae623 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1070,7 +1070,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { visit::walk_label(self, label); } - fn visit_lifetime(&mut self, lifetime: &'a Lifetime) { + fn visit_lifetime(&mut self, lifetime: &'a Lifetime, _: visit::LifetimeCtxt) { self.check_lifetime(lifetime.ident); visit::walk_lifetime(self, lifetime); } diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 0e8af549692..2774bda24f1 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -396,37 +396,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } } - // Check for unstable modifiers on `#[link(..)]` attribute - if attr.has_name(sym::link) { - for nested_meta in attr.meta_item_list().unwrap_or_default() { - if nested_meta.has_name(sym::modifiers) { - if let Some(modifiers) = nested_meta.value_str() { - for modifier in modifiers.as_str().split(',') { - if let Some(modifier) = modifier.strip_prefix(&['+', '-']) { - macro_rules! gate_modifier { ($($name:literal => $feature:ident)*) => { - $(if modifier == $name { - let msg = concat!("`#[link(modifiers=\"", $name, "\")]` is unstable"); - gate_feature_post!( - self, - $feature, - nested_meta.name_value_literal_span().unwrap(), - msg - ); - })* - }} - - gate_modifier!( - "bundle" => native_link_modifiers_bundle - "verbatim" => native_link_modifiers_verbatim - "as-needed" => native_link_modifiers_as_needed - ); - } - } - } - } - } - } - // Emit errors for non-staged-api crates. if !self.features.staged_api { if attr.has_name(sym::rustc_deprecated) diff --git a/compiler/rustc_ast_passes/src/node_count.rs b/compiler/rustc_ast_passes/src/node_count.rs index 48b79809c1b..ee166f75703 100644 --- a/compiler/rustc_ast_passes/src/node_count.rs +++ b/compiler/rustc_ast_passes/src/node_count.rs @@ -106,7 +106,7 @@ impl<'ast> Visitor<'ast> for NodeCounter { self.count += 1; walk_variant(self, v) } - fn visit_lifetime(&mut self, lifetime: &Lifetime) { + fn visit_lifetime(&mut self, lifetime: &Lifetime, _: visit::LifetimeCtxt) { self.count += 1; walk_lifetime(self, lifetime) } diff --git a/compiler/rustc_ast_pretty/src/lib.rs b/compiler/rustc_ast_pretty/src/lib.rs index 517ab30b2a4..79178830bf9 100644 --- a/compiler/rustc_ast_pretty/src/lib.rs +++ b/compiler/rustc_ast_pretty/src/lib.rs @@ -1,5 +1,6 @@ -#![feature(crate_visibility_modifier)] +#![feature(associated_type_bounds)] #![feature(box_patterns)] +#![feature(with_negative_coherence)] #![recursion_limit = "256"] mod helpers; diff --git a/compiler/rustc_ast_pretty/src/pp.rs b/compiler/rustc_ast_pretty/src/pp.rs index ddce86f2165..c93022308a3 100644 --- a/compiler/rustc_ast_pretty/src/pp.rs +++ b/compiler/rustc_ast_pretty/src/pp.rs @@ -68,20 +68,20 @@ //! will be made to flow subsequent breaks together onto lines. Inconsistent //! is the opposite. Inconsistent breaking example would be, say: //! -//! ``` +//! ```ignore (illustrative) //! foo(hello, there, good, friends) //! ``` //! //! breaking inconsistently to become //! -//! ``` +//! ```ignore (illustrative) //! foo(hello, there, //! good, friends); //! ``` //! //! whereas a consistent breaking would yield: //! -//! ``` +//! ```ignore (illustrative) //! foo(hello, //! there, //! good, @@ -153,14 +153,14 @@ enum IndentStyle { /// Vertically aligned under whatever column this block begins at. /// /// fn demo(arg1: usize, - /// arg2: usize); + /// arg2: usize) {} Visual, /// Indented relative to the indentation level of the previous line. /// /// fn demo( /// arg1: usize, /// arg2: usize, - /// ); + /// ) {} Block { offset: isize }, } diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index c02cdc29561..7357ddf2134 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -95,7 +95,7 @@ pub struct State<'a> { ann: &'a (dyn PpAnn + 'a), } -crate const INDENT_UNIT: isize = 4; +pub(crate) const INDENT_UNIT: isize = 4; /// Requires you to pass an input filename and reader so that /// it can scan the input text for comments to copy forward. @@ -550,9 +550,9 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere fn print_tts(&mut self, tts: &TokenStream, convert_dollar_crate: bool) { let mut iter = tts.trees().peekable(); while let Some(tt) = iter.next() { - self.print_tt(&tt, convert_dollar_crate); + self.print_tt(tt, convert_dollar_crate); if let Some(next) = iter.peek() { - if tt_prepend_space(next, &tt) { + if tt_prepend_space(next, tt) { self.space(); } } @@ -858,6 +858,14 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere Self::to_string(|s| s.print_item(i)) } + fn assoc_item_to_string(&self, i: &ast::AssocItem) -> String { + Self::to_string(|s| s.print_assoc_item(i)) + } + + fn foreign_item_to_string(&self, i: &ast::ForeignItem) -> String { + Self::to_string(|s| s.print_foreign_item(i)) + } + fn generic_params_to_string(&self, generic_params: &[ast::GenericParam]) -> String { Self::to_string(|s| s.print_generic_params(generic_params)) } @@ -947,8 +955,13 @@ impl<'a> State<'a> { State { s: pp::Printer::new(), comments: None, ann: &NoAnn } } - crate fn commasep_cmnt<T, F, G>(&mut self, b: Breaks, elts: &[T], mut op: F, mut get_span: G) - where + pub(crate) fn commasep_cmnt<T, F, G>( + &mut self, + b: Breaks, + elts: &[T], + mut op: F, + mut get_span: G, + ) where F: FnMut(&mut State<'_>, &T), G: FnMut(&T) -> rustc_span::Span, { @@ -968,7 +981,7 @@ impl<'a> State<'a> { self.end(); } - crate fn commasep_exprs(&mut self, b: Breaks, exprs: &[P<ast::Expr>]) { + pub(crate) fn commasep_exprs(&mut self, b: Breaks, exprs: &[P<ast::Expr>]) { self.commasep_cmnt(b, exprs, |s, e| s.print_expr(e), |e| e.span) } @@ -1101,7 +1114,7 @@ impl<'a> State<'a> { self.print_trait_ref(&t.trait_ref) } - crate fn print_stmt(&mut self, st: &ast::Stmt) { + pub(crate) fn print_stmt(&mut self, st: &ast::Stmt) { self.maybe_print_comment(st.span.lo()); match st.kind { ast::StmtKind::Local(ref loc) => { @@ -1156,19 +1169,19 @@ impl<'a> State<'a> { self.maybe_print_trailing_comment(st.span, None) } - crate fn print_block(&mut self, blk: &ast::Block) { + pub(crate) fn print_block(&mut self, blk: &ast::Block) { self.print_block_with_attrs(blk, &[]) } - crate fn print_block_unclosed_indent(&mut self, blk: &ast::Block) { + pub(crate) fn print_block_unclosed_indent(&mut self, blk: &ast::Block) { self.print_block_maybe_unclosed(blk, &[], false) } - crate fn print_block_with_attrs(&mut self, blk: &ast::Block, attrs: &[ast::Attribute]) { + pub(crate) fn print_block_with_attrs(&mut self, blk: &ast::Block, attrs: &[ast::Attribute]) { self.print_block_maybe_unclosed(blk, attrs, true) } - crate fn print_block_maybe_unclosed( + pub(crate) fn print_block_maybe_unclosed( &mut self, blk: &ast::Block, attrs: &[ast::Attribute], @@ -1202,7 +1215,7 @@ impl<'a> State<'a> { } /// Print a `let pat = expr` expression. - crate fn print_let(&mut self, pat: &ast::Pat, expr: &ast::Expr) { + pub(crate) fn print_let(&mut self, pat: &ast::Pat, expr: &ast::Expr) { self.word("let "); self.print_pat(pat); self.space(); @@ -1211,7 +1224,7 @@ impl<'a> State<'a> { self.print_expr_cond_paren(expr, Self::cond_needs_par(expr) || npals()) } - crate fn print_mac(&mut self, m: &ast::MacCall) { + pub(crate) fn print_mac(&mut self, m: &ast::MacCall) { self.print_mac_common( Some(MacHeader::Path(&m.path)), true, @@ -1352,7 +1365,7 @@ impl<'a> State<'a> { self.pclose(); } - crate fn print_local_decl(&mut self, loc: &ast::Local) { + pub(crate) fn print_local_decl(&mut self, loc: &ast::Local) { self.print_pat(&loc.pat); if let Some(ref ty) = loc.ty { self.word_space(":"); @@ -1360,7 +1373,7 @@ impl<'a> State<'a> { } } - crate fn print_name(&mut self, name: Symbol) { + pub(crate) fn print_name(&mut self, name: Symbol) { self.word(name.to_string()); self.ann.post(self, AnnNode::Name(&name)) } @@ -1384,7 +1397,7 @@ impl<'a> State<'a> { } } - crate fn print_pat(&mut self, pat: &ast::Pat) { + pub(crate) fn print_pat(&mut self, pat: &ast::Pat) { self.maybe_print_comment(pat.span.lo()); self.ann.pre(self, AnnNode::Pat(pat)); /* Pat isn't normalized, but the beauty of it @@ -1543,7 +1556,7 @@ impl<'a> State<'a> { } } - crate fn print_asyncness(&mut self, asyncness: ast::Async) { + pub(crate) fn print_asyncness(&mut self, asyncness: ast::Async) { if asyncness.is_async() { self.word_nbsp("async"); } @@ -1576,11 +1589,11 @@ impl<'a> State<'a> { } } - crate fn print_lifetime(&mut self, lifetime: ast::Lifetime) { + pub(crate) fn print_lifetime(&mut self, lifetime: ast::Lifetime) { self.print_name(lifetime.ident.name) } - crate fn print_lifetime_bounds( + pub(crate) fn print_lifetime_bounds( &mut self, lifetime: ast::Lifetime, bounds: &ast::GenericBounds, @@ -1600,7 +1613,7 @@ impl<'a> State<'a> { } } - crate fn print_generic_params(&mut self, generic_params: &[ast::GenericParam]) { + pub(crate) fn print_generic_params(&mut self, generic_params: &[ast::GenericParam]) { if generic_params.is_empty() { return; } @@ -1654,12 +1667,12 @@ impl<'a> State<'a> { } } - crate fn print_mt(&mut self, mt: &ast::MutTy, print_const: bool) { + pub(crate) fn print_mt(&mut self, mt: &ast::MutTy, print_const: bool) { self.print_mutability(mt.mutbl, print_const); self.print_type(&mt.ty) } - crate fn print_param(&mut self, input: &ast::Param, is_closure: bool) { + pub(crate) fn print_param(&mut self, input: &ast::Param, is_closure: bool) { self.ibox(INDENT_UNIT); self.print_outer_attributes_inline(&input.attrs); @@ -1687,7 +1700,7 @@ impl<'a> State<'a> { self.end(); } - crate fn print_fn_ret_ty(&mut self, fn_ret_ty: &ast::FnRetTy) { + pub(crate) fn print_fn_ret_ty(&mut self, fn_ret_ty: &ast::FnRetTy) { if let ast::FnRetTy::Ty(ty) = fn_ret_ty { self.space_if_not_bol(); self.ibox(INDENT_UNIT); @@ -1698,7 +1711,7 @@ impl<'a> State<'a> { } } - crate fn print_ty_fn( + pub(crate) fn print_ty_fn( &mut self, ext: ast::Extern, unsafety: ast::Unsafe, @@ -1722,7 +1735,7 @@ impl<'a> State<'a> { self.end(); } - crate fn print_fn_header_info(&mut self, header: ast::FnHeader) { + pub(crate) fn print_fn_header_info(&mut self, header: ast::FnHeader) { self.print_constness(header.constness); self.print_asyncness(header.asyncness); self.print_unsafety(header.unsafety); @@ -1742,21 +1755,21 @@ impl<'a> State<'a> { self.word("fn") } - crate fn print_unsafety(&mut self, s: ast::Unsafe) { + pub(crate) fn print_unsafety(&mut self, s: ast::Unsafe) { match s { ast::Unsafe::No => {} ast::Unsafe::Yes(_) => self.word_nbsp("unsafe"), } } - crate fn print_constness(&mut self, s: ast::Const) { + pub(crate) fn print_constness(&mut self, s: ast::Const) { match s { ast::Const::No => {} ast::Const::Yes(_) => self.word_nbsp("const"), } } - crate fn print_is_auto(&mut self, s: ast::IsAuto) { + pub(crate) fn print_is_auto(&mut self, s: ast::IsAuto) { match s { ast::IsAuto::Yes => self.word_nbsp("auto"), ast::IsAuto::No => {} diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 2a35dd1006e..0de5e2099fd 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -19,7 +19,7 @@ impl<'a> State<'a> { } } - fn print_foreign_item(&mut self, item: &ast::ForeignItem) { + pub(crate) fn print_foreign_item(&mut self, item: &ast::ForeignItem) { let ast::Item { id, span, ident, ref attrs, ref kind, ref vis, tokens: _ } = *item; self.ann.pre(self, AnnNode::SubItem(id)); self.hardbreak_if_not_bol(); @@ -128,7 +128,7 @@ impl<'a> State<'a> { } /// Pretty-prints an item. - crate fn print_item(&mut self, item: &ast::Item) { + pub(crate) fn print_item(&mut self, item: &ast::Item) { self.hardbreak_if_not_bol(); self.maybe_print_comment(item.span.lo()); self.print_outer_attributes(&item.attrs); @@ -400,7 +400,7 @@ impl<'a> State<'a> { self.bclose(span, empty) } - crate fn print_visibility(&mut self, vis: &ast::Visibility) { + pub(crate) fn print_visibility(&mut self, vis: &ast::Visibility) { match vis.kind { ast::VisibilityKind::Public => self.word_nbsp("pub"), ast::VisibilityKind::Crate(sugar) => match sugar { @@ -484,7 +484,7 @@ impl<'a> State<'a> { } } - crate fn print_variant(&mut self, v: &ast::Variant) { + pub(crate) fn print_variant(&mut self, v: &ast::Variant) { self.head(""); self.print_visibility(&v.vis); let generics = ast::Generics::default(); @@ -496,7 +496,7 @@ impl<'a> State<'a> { } } - fn print_assoc_item(&mut self, item: &ast::AssocItem) { + pub(crate) fn print_assoc_item(&mut self, item: &ast::AssocItem) { let ast::Item { id, span, ident, ref attrs, ref kind, ref vis, tokens: _ } = *item; self.ann.pre(self, AnnNode::SubItem(id)); self.hardbreak_if_not_bol(); @@ -562,7 +562,7 @@ impl<'a> State<'a> { } } - crate fn print_fn( + pub(crate) fn print_fn( &mut self, decl: &ast::FnDecl, header: ast::FnHeader, @@ -579,7 +579,7 @@ impl<'a> State<'a> { self.print_where_clause(&generics.where_clause) } - crate fn print_fn_params_and_ret(&mut self, decl: &ast::FnDecl, is_closure: bool) { + pub(crate) fn print_fn_params_and_ret(&mut self, decl: &ast::FnDecl, is_closure: bool) { let (open, close) = if is_closure { ("|", "|") } else { ("(", ")") }; self.word(open); self.commasep(Inconsistent, &decl.inputs, |s, param| s.print_param(param, is_closure)); @@ -591,7 +591,7 @@ impl<'a> State<'a> { self.print_where_clause_parts(where_clause.has_where_token, &where_clause.predicates); } - crate fn print_where_clause_parts( + pub(crate) fn print_where_clause_parts( &mut self, has_where_token: bool, predicates: &[ast::WherePredicate], diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 8e748aaa58b..2704cb8d785 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -101,6 +101,16 @@ pub struct Stability { 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() + } +} + /// Represents the `#[rustc_const_unstable]` and `#[rustc_const_stable]` attributes. #[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] #[derive(HashStable_Generic)] @@ -111,6 +121,16 @@ pub struct ConstStability { 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() + } +} + /// The available stability levels. #[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)] #[derive(HashStable_Generic)] @@ -679,12 +699,12 @@ where continue; } - if let Some((_, span)) = &depr { - struct_span_err!(diagnostic, attr.span, E0550, "multiple deprecated attributes") - .span_label(attr.span, "repeated deprecation attribute") - .span_label(*span, "first deprecation attribute") + // FIXME(jhpratt) remove this eventually + if attr.has_name(sym::rustc_deprecated) { + diagnostic + .struct_span_err(attr.span, "`#[rustc_deprecated]` has been removed") + .help("use `#[deprecated]` instead") .emit(); - break; } let Some(meta) = attr.meta() else { @@ -742,12 +762,24 @@ where continue 'outer; } } - // FIXME(jhpratt) remove this after a bootstrap occurs. Emitting an - // error specific to the renaming would be a good idea as well. + // FIXME(jhpratt) remove this eventually sym::reason if attr.has_name(sym::rustc_deprecated) => { if !get(mi, &mut note) { continue 'outer; } + + let mut diag = diagnostic + .struct_span_err(mi.span, "`reason` has been renamed"); + match note { + Some(note) => diag.span_suggestion( + mi.span, + "use `note` instead", + format!("note = \"{note}\""), + Applicability::MachineApplicable, + ), + None => diag.span_help(mi.span, "use `note` instead"), + }; + diag.emit(); } sym::suggestion => { if !sess.features_untracked().deprecated_suggestion { @@ -856,177 +888,180 @@ impl IntType { /// 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> { - use 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 diagnostic = &sess.parse_sess.span_diagnostic; - if attr.has_name(sym::repr) { - 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::C => Some(ReprC), - sym::packed => Some(ReprPacked(1)), - sym::simd => Some(ReprSimd), - sym::transparent => Some(ReprTransparent), - sym::no_niche => Some(ReprNoNiche), - sym::align => { - let mut err = struct_span_err!( - diagnostic, - item.span(), - E0589, - "invalid `repr(align)` attribute: `align` needs an argument" - ); - err.span_suggestion( - item.span(), - "supply an argument here", - "align(...)".to_string(), - Applicability::HasPlaceholders, - ); - err.emit(); - recognised = true; - None - } - name => int_type_of_word(name).map(ReprInt), - }; - if let Some(h) = hint { + 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::C => Some(ReprC), + sym::packed => Some(ReprPacked(1)), + sym::simd => Some(ReprSimd), + sym::transparent => Some(ReprTransparent), + sym::no_niche => Some(ReprNoNiche), + sym::align => { + let mut err = struct_span_err!( + diagnostic, + item.span(), + E0589, + "invalid `repr(align)` attribute: `align` needs an argument" + ); + err.span_suggestion( + item.span(), + "supply an argument here", + "align(...)".to_string(), + Applicability::HasPlaceholders, + ); + err.emit(); recognised = true; - acc.push(h); + None } - } else if let Some((name, value)) = item.name_value_literal() { - let mut literal_error = None; - if name == sym::align { - recognised = true; - match parse_alignment(&value.kind) { - Ok(literal) => acc.push(ReprAlign(literal)), - Err(message) => literal_error = Some(message), - }; - } else if name == sym::packed { - recognised = true; - match parse_alignment(&value.kind) { - Ok(literal) => acc.push(ReprPacked(literal)), - Err(message) => literal_error = Some(message), - }; - } else if matches!(name, sym::C | sym::simd | sym::transparent | sym::no_niche) - || int_type_of_word(name).is_some() - { - recognised = true; - struct_span_err!( + 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.name_value_literal() { + let mut literal_error = None; + if name == sym::align { + recognised = true; + match parse_alignment(&value.kind) { + Ok(literal) => acc.push(ReprAlign(literal)), + Err(message) => literal_error = Some(message), + }; + } else if name == sym::packed { + recognised = true; + match parse_alignment(&value.kind) { + Ok(literal) => acc.push(ReprPacked(literal)), + Err(message) => literal_error = Some(message), + }; + } else if matches!(name, sym::C | sym::simd | sym::transparent | sym::no_niche) + || int_type_of_word(name).is_some() + { + recognised = true; + struct_span_err!( diagnostic, item.span(), E0552, "invalid representation hint: `{}` does not take a parenthesized argument list", name.to_ident_string(), ).emit(); - } - if let Some(literal_error) = literal_error { - struct_span_err!( + } + if let Some(literal_error) = literal_error { + struct_span_err!( + diagnostic, + item.span(), + E0589, + "invalid `repr({})` attribute: {}", + name.to_ident_string(), + literal_error + ) + .emit(); + } + } else if let Some(meta_item) = item.meta_item() { + if let MetaItemKind::NameValue(ref value) = meta_item.kind { + 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; + let mut err = struct_span_err!( diagnostic, item.span(), - E0589, - "invalid `repr({})` attribute: {}", - name.to_ident_string(), - literal_error - ) - .emit(); - } - } else if let Some(meta_item) = item.meta_item() { - if let MetaItemKind::NameValue(ref value) = meta_item.kind { - 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; - let mut err = struct_span_err!( - diagnostic, - item.span(), - E0693, - "incorrect `repr({})` attribute format", - name, - ); - match value.kind { - ast::LitKind::Int(int, ast::LitIntType::Unsuffixed) => { - err.span_suggestion( - item.span(), - "use parentheses instead", - format!("{}({})", name, int), - Applicability::MachineApplicable, - ); - } - ast::LitKind::Str(s, _) => { - err.span_suggestion( - item.span(), - "use parentheses instead", - format!("{}({})", name, s), - Applicability::MachineApplicable, - ); - } - _ => {} + E0693, + "incorrect `repr({})` attribute format", + name, + ); + match value.kind { + ast::LitKind::Int(int, ast::LitIntType::Unsuffixed) => { + err.span_suggestion( + item.span(), + "use parentheses instead", + format!("{}({})", name, int), + Applicability::MachineApplicable, + ); } - err.emit(); - } else { - if matches!( - meta_item.name_or_empty(), - sym::C | sym::simd | sym::transparent | sym::no_niche - ) || int_type_of_word(meta_item.name_or_empty()).is_some() - { - recognised = true; - struct_span_err!( - diagnostic, - meta_item.span, - E0552, - "invalid representation hint: `{}` does not take a value", - meta_item.name_or_empty().to_ident_string(), - ) - .emit(); + ast::LitKind::Str(s, _) => { + err.span_suggestion( + item.span(), + "use parentheses instead", + format!("{}({})", name, s), + Applicability::MachineApplicable, + ); } + _ => {} } - } else if let MetaItemKind::List(_) = meta_item.kind { - if meta_item.has_name(sym::align) { + err.emit(); + } else { + if matches!( + meta_item.name_or_empty(), + sym::C | sym::simd | sym::transparent | sym::no_niche + ) || int_type_of_word(meta_item.name_or_empty()).is_some() + { recognised = true; struct_span_err!( diagnostic, meta_item.span, - E0693, - "incorrect `repr(align)` attribute format: \ - `align` takes exactly one argument in parentheses" + E0552, + "invalid representation hint: `{}` does not take a value", + meta_item.name_or_empty().to_ident_string(), ) .emit(); - } else if meta_item.has_name(sym::packed) { - recognised = true; - struct_span_err!( - diagnostic, - meta_item.span, - E0552, - "incorrect `repr(packed)` attribute format: \ + } + } + } else if let MetaItemKind::List(_) = meta_item.kind { + if meta_item.has_name(sym::align) { + recognised = true; + struct_span_err!( + diagnostic, + meta_item.span, + E0693, + "incorrect `repr(align)` attribute format: \ + `align` takes exactly one argument in parentheses" + ) + .emit(); + } else if meta_item.has_name(sym::packed) { + recognised = true; + struct_span_err!( + diagnostic, + meta_item.span, + E0552, + "incorrect `repr(packed)` attribute format: \ `packed` takes exactly one parenthesized argument, \ or no parentheses at all" - ) - .emit(); - } else if matches!( - meta_item.name_or_empty(), - sym::C | sym::simd | sym::transparent | sym::no_niche - ) || int_type_of_word(meta_item.name_or_empty()).is_some() - { - recognised = true; - struct_span_err!( + ) + .emit(); + } else if matches!( + meta_item.name_or_empty(), + sym::C | sym::simd | sym::transparent | sym::no_niche + ) || int_type_of_word(meta_item.name_or_empty()).is_some() + { + recognised = true; + struct_span_err!( diagnostic, meta_item.span, E0552, "invalid representation hint: `{}` does not take a parenthesized argument list", meta_item.name_or_empty().to_ident_string(), ).emit(); - } } } - 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 `delay_span_bug` call as follows: - if sess.opts.pretty.map_or(true, |pp| pp.needs_analysis()) { - diagnostic.delay_span_bug(item.span(), "unrecognized representation hint"); - } + } + 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 `delay_span_bug` call as follows: + if sess.opts.pretty.map_or(true, |pp| pp.needs_analysis()) { + diagnostic.delay_span_bug(item.span(), "unrecognized representation hint"); } } } diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs index 4a9904891ec..c7d0e336133 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs @@ -29,7 +29,7 @@ pub struct BorrowSet<'tcx> { /// Map from local to all the borrows on that local. pub local_map: FxHashMap<mir::Local, FxHashSet<BorrowIndex>>, - crate locals_state_at_exit: LocalsStateAtExit, + pub(crate) locals_state_at_exit: LocalsStateAtExit, } impl<'tcx> Index<BorrowIndex> for BorrowSet<'tcx> { @@ -148,29 +148,25 @@ impl<'tcx> BorrowSet<'tcx> { } } - crate fn activations_at_location(&self, location: Location) -> &[BorrowIndex] { + pub(crate) fn activations_at_location(&self, location: Location) -> &[BorrowIndex] { self.activation_map.get(&location).map_or(&[], |activations| &activations[..]) } - crate fn len(&self) -> usize { + pub(crate) fn len(&self) -> usize { self.location_map.len() } - crate fn indices(&self) -> impl Iterator<Item = BorrowIndex> { + pub(crate) fn indices(&self) -> impl Iterator<Item = BorrowIndex> { BorrowIndex::from_usize(0)..BorrowIndex::from_usize(self.len()) } - crate fn iter_enumerated(&self) -> impl Iterator<Item = (BorrowIndex, &BorrowData<'tcx>)> { + pub(crate) fn iter_enumerated(&self) -> impl Iterator<Item = (BorrowIndex, &BorrowData<'tcx>)> { self.indices().zip(self.location_map.values()) } - crate fn get_index_of(&self, location: &Location) -> Option<BorrowIndex> { + pub(crate) fn get_index_of(&self, location: &Location) -> Option<BorrowIndex> { self.location_map.get_index_of(location).map(BorrowIndex::from) } - - crate fn contains(&self, location: &Location) -> bool { - self.location_map.contains_key(location) - } } struct GatherBorrows<'a, 'tcx> { diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs index 70f7f1e493e..a1233d62cb0 100644 --- a/compiler/rustc_borrowck/src/borrowck_errors.rs +++ b/compiler/rustc_borrowck/src/borrowck_errors.rs @@ -3,7 +3,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Span; impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { - crate fn cannot_move_when_borrowed( + pub(crate) fn cannot_move_when_borrowed( &self, span: Span, desc: &str, @@ -11,7 +11,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { struct_span_err!(self, span, E0505, "cannot move out of {} because it is borrowed", desc,) } - crate fn cannot_use_when_mutably_borrowed( + pub(crate) fn cannot_use_when_mutably_borrowed( &self, span: Span, desc: &str, @@ -31,7 +31,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { err } - crate fn cannot_act_on_uninitialized_variable( + pub(crate) fn cannot_act_on_uninitialized_variable( &self, span: Span, verb: &str, @@ -47,7 +47,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { ) } - crate fn cannot_mutably_borrow_multiply( + pub(crate) fn cannot_mutably_borrow_multiply( &self, new_loan_span: Span, desc: &str, @@ -97,7 +97,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { err } - crate fn cannot_uniquely_borrow_by_two_closures( + pub(crate) fn cannot_uniquely_borrow_by_two_closures( &self, new_loan_span: Span, desc: &str, @@ -126,7 +126,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { err } - crate fn cannot_uniquely_borrow_by_one_closure( + pub(crate) fn cannot_uniquely_borrow_by_one_closure( &self, new_loan_span: Span, container_name: &str, @@ -157,7 +157,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { err } - crate fn cannot_reborrow_already_uniquely_borrowed( + pub(crate) fn cannot_reborrow_already_uniquely_borrowed( &self, new_loan_span: Span, container_name: &str, @@ -193,7 +193,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { err } - crate fn cannot_reborrow_already_borrowed( + pub(crate) fn cannot_reborrow_already_borrowed( &self, span: Span, desc_new: &str, @@ -242,7 +242,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { err } - crate fn cannot_assign_to_borrowed( + pub(crate) fn cannot_assign_to_borrowed( &self, span: Span, borrow_span: Span, @@ -261,7 +261,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { err } - crate fn cannot_reassign_immutable( + pub(crate) fn cannot_reassign_immutable( &self, span: Span, desc: &str, @@ -271,7 +271,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { struct_span_err!(self, span, E0384, "cannot assign {} {}", msg, desc) } - crate fn cannot_assign( + pub(crate) fn cannot_assign( &self, span: Span, desc: &str, @@ -279,7 +279,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { struct_span_err!(self, span, E0594, "cannot assign to {}", desc) } - crate fn cannot_move_out_of( + pub(crate) fn cannot_move_out_of( &self, move_from_span: Span, move_from_desc: &str, @@ -290,7 +290,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { /// Signal an error due to an attempt to move out of the interior /// of an array or slice. `is_index` is None when error origin /// didn't capture whether there was an indexing operation or not. - crate fn cannot_move_out_of_interior_noncopy( + pub(crate) fn cannot_move_out_of_interior_noncopy( &self, move_from_span: Span, ty: Ty<'_>, @@ -313,7 +313,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { err } - crate fn cannot_move_out_of_interior_of_drop( + pub(crate) fn cannot_move_out_of_interior_of_drop( &self, move_from_span: Span, container_ty: Ty<'_>, @@ -329,7 +329,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { err } - crate fn cannot_act_on_moved_value( + pub(crate) fn cannot_act_on_moved_value( &self, use_span: Span, verb: &str, @@ -349,7 +349,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { ) } - crate fn cannot_borrow_path_as_mutable_because( + pub(crate) fn cannot_borrow_path_as_mutable_because( &self, span: Span, path: &str, @@ -358,7 +358,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { struct_span_err!(self, span, E0596, "cannot borrow {} as mutable{}", path, reason,) } - crate fn cannot_mutate_in_immutable_section( + pub(crate) fn cannot_mutate_in_immutable_section( &self, mutate_span: Span, immutable_span: Span, @@ -380,7 +380,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { err } - crate fn cannot_borrow_across_generator_yield( + pub(crate) fn cannot_borrow_across_generator_yield( &self, span: Span, yield_span: Span, @@ -395,7 +395,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { err } - crate fn cannot_borrow_across_destructor( + pub(crate) fn cannot_borrow_across_destructor( &self, borrow_span: Span, ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { @@ -407,7 +407,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { ) } - crate fn path_does_not_live_long_enough( + pub(crate) fn path_does_not_live_long_enough( &self, span: Span, path: &str, @@ -415,7 +415,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { struct_span_err!(self, span, E0597, "{} does not live long enough", path,) } - crate fn cannot_return_reference_to_local( + pub(crate) fn cannot_return_reference_to_local( &self, span: Span, return_kind: &str, @@ -440,7 +440,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { err } - crate fn cannot_capture_in_long_lived_closure( + pub(crate) fn cannot_capture_in_long_lived_closure( &self, closure_span: Span, closure_kind: &str, @@ -462,14 +462,14 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { err } - crate fn thread_local_value_does_not_live_long_enough( + pub(crate) fn thread_local_value_does_not_live_long_enough( &self, span: Span, ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { struct_span_err!(self, span, E0712, "thread-local variable borrowed past end of function",) } - crate fn temporary_value_borrowed_for_too_long( + pub(crate) fn temporary_value_borrowed_for_too_long( &self, span: Span, ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { @@ -486,7 +486,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { } } -crate fn borrowed_data_escapes_closure<'tcx>( +pub(crate) fn borrowed_data_escapes_closure<'tcx>( tcx: TyCtxt<'tcx>, escape_span: Span, escapes_from: &str, diff --git a/compiler/rustc_borrowck/src/constraints/graph.rs b/compiler/rustc_borrowck/src/constraints/graph.rs index c19a39c393f..609fbc2bc15 100644 --- a/compiler/rustc_borrowck/src/constraints/graph.rs +++ b/compiler/rustc_borrowck/src/constraints/graph.rs @@ -13,19 +13,19 @@ use crate::{ /// The construct graph organizes the constraints by their end-points. /// It can be used to view a `R1: R2` constraint as either an edge `R1 /// -> R2` or `R2 -> R1` depending on the direction type `D`. -crate struct ConstraintGraph<D: ConstraintGraphDirecton> { +pub(crate) struct ConstraintGraph<D: ConstraintGraphDirecton> { _direction: D, first_constraints: IndexVec<RegionVid, Option<OutlivesConstraintIndex>>, next_constraints: IndexVec<OutlivesConstraintIndex, Option<OutlivesConstraintIndex>>, } -crate type NormalConstraintGraph = ConstraintGraph<Normal>; +pub(crate) type NormalConstraintGraph = ConstraintGraph<Normal>; -crate type ReverseConstraintGraph = ConstraintGraph<Reverse>; +pub(crate) type ReverseConstraintGraph = ConstraintGraph<Reverse>; /// Marker trait that controls whether a `R1: R2` constraint /// represents an edge `R1 -> R2` or `R2 -> R1`. -crate trait ConstraintGraphDirecton: Copy + 'static { +pub(crate) trait ConstraintGraphDirecton: Copy + 'static { fn start_region(c: &OutlivesConstraint<'_>) -> RegionVid; fn end_region(c: &OutlivesConstraint<'_>) -> RegionVid; fn is_normal() -> bool; @@ -36,7 +36,7 @@ crate trait ConstraintGraphDirecton: Copy + 'static { /// inference. This is because we compute the value of R1 by union'ing /// all the things that it relies on. #[derive(Copy, Clone, Debug)] -crate struct Normal; +pub(crate) struct Normal; impl ConstraintGraphDirecton for Normal { fn start_region(c: &OutlivesConstraint<'_>) -> RegionVid { @@ -57,7 +57,7 @@ impl ConstraintGraphDirecton for Normal { /// we wish to iterate from a region (e.g., R2) to all the regions /// that will outlive it (e.g., R1). #[derive(Copy, Clone, Debug)] -crate struct Reverse; +pub(crate) struct Reverse; impl ConstraintGraphDirecton for Reverse { fn start_region(c: &OutlivesConstraint<'_>) -> RegionVid { @@ -78,7 +78,11 @@ impl<D: ConstraintGraphDirecton> ConstraintGraph<D> { /// R2` is treated as an edge `R1 -> R2`. We use this graph to /// construct SCCs for region inference but also for error /// reporting. - crate fn new(direction: D, set: &OutlivesConstraintSet<'_>, num_region_vars: usize) -> Self { + pub(crate) fn new( + direction: D, + set: &OutlivesConstraintSet<'_>, + num_region_vars: usize, + ) -> Self { let mut first_constraints = IndexVec::from_elem_n(None, num_region_vars); let mut next_constraints = IndexVec::from_elem(None, &set.outlives); @@ -96,7 +100,7 @@ impl<D: ConstraintGraphDirecton> ConstraintGraph<D> { /// Given the constraint set from which this graph was built /// creates a region graph so that you can iterate over *regions* /// and not constraints. - crate fn region_graph<'rg, 'tcx>( + pub(crate) fn region_graph<'rg, 'tcx>( &'rg self, set: &'rg OutlivesConstraintSet<'tcx>, static_region: RegionVid, @@ -105,7 +109,7 @@ impl<D: ConstraintGraphDirecton> ConstraintGraph<D> { } /// Given a region `R`, iterate over all constraints `R: R1`. - crate fn outgoing_edges<'a, 'tcx>( + pub(crate) fn outgoing_edges<'a, 'tcx>( &'a self, region_sup: RegionVid, constraints: &'a OutlivesConstraintSet<'tcx>, @@ -129,7 +133,7 @@ impl<D: ConstraintGraphDirecton> ConstraintGraph<D> { } } -crate struct Edges<'s, 'tcx, D: ConstraintGraphDirecton> { +pub(crate) struct Edges<'s, 'tcx, D: ConstraintGraphDirecton> { graph: &'s ConstraintGraph<D>, constraints: &'s OutlivesConstraintSet<'tcx>, pointer: Option<OutlivesConstraintIndex>, @@ -169,7 +173,7 @@ impl<'s, 'tcx, D: ConstraintGraphDirecton> Iterator for Edges<'s, 'tcx, D> { /// This struct brings together a constraint set and a (normal, not /// reverse) constraint graph. It implements the graph traits and is /// usd for doing the SCC computation. -crate struct RegionGraph<'s, 'tcx, D: ConstraintGraphDirecton> { +pub(crate) struct RegionGraph<'s, 'tcx, D: ConstraintGraphDirecton> { set: &'s OutlivesConstraintSet<'tcx>, constraint_graph: &'s ConstraintGraph<D>, static_region: RegionVid, @@ -180,7 +184,7 @@ impl<'s, 'tcx, D: ConstraintGraphDirecton> RegionGraph<'s, 'tcx, D> { /// R2` is treated as an edge `R1 -> R2`. We use this graph to /// construct SCCs for region inference but also for error /// reporting. - crate fn new( + pub(crate) fn new( set: &'s OutlivesConstraintSet<'tcx>, constraint_graph: &'s ConstraintGraph<D>, static_region: RegionVid, @@ -190,14 +194,14 @@ impl<'s, 'tcx, D: ConstraintGraphDirecton> RegionGraph<'s, 'tcx, D> { /// Given a region `R`, iterate over all regions `R1` such that /// there exists a constraint `R: R1`. - crate fn outgoing_regions(&self, region_sup: RegionVid) -> Successors<'s, 'tcx, D> { + pub(crate) fn outgoing_regions(&self, region_sup: RegionVid) -> Successors<'s, 'tcx, D> { Successors { edges: self.constraint_graph.outgoing_edges(region_sup, self.set, self.static_region), } } } -crate struct Successors<'s, 'tcx, D: ConstraintGraphDirecton> { +pub(crate) struct Successors<'s, 'tcx, D: ConstraintGraphDirecton> { edges: Edges<'s, 'tcx, D>, } diff --git a/compiler/rustc_borrowck/src/constraints/mod.rs b/compiler/rustc_borrowck/src/constraints/mod.rs index 14f0e5f620a..6d5466c0c41 100644 --- a/compiler/rustc_borrowck/src/constraints/mod.rs +++ b/compiler/rustc_borrowck/src/constraints/mod.rs @@ -8,19 +8,19 @@ use std::ops::Index; use crate::type_check::Locations; -crate mod graph; +pub(crate) mod graph; /// A set of NLL region constraints. These include "outlives" /// constraints of the form `R1: R2`. Each constraint is identified by /// a unique `OutlivesConstraintIndex` and you can index into the set /// (`constraint_set[i]`) to access the constraint details. #[derive(Clone, Default)] -crate struct OutlivesConstraintSet<'tcx> { +pub(crate) struct OutlivesConstraintSet<'tcx> { outlives: IndexVec<OutlivesConstraintIndex, OutlivesConstraint<'tcx>>, } impl<'tcx> OutlivesConstraintSet<'tcx> { - crate fn push(&mut self, constraint: OutlivesConstraint<'tcx>) { + pub(crate) fn push(&mut self, constraint: OutlivesConstraint<'tcx>) { debug!( "OutlivesConstraintSet::push({:?}: {:?} @ {:?}", constraint.sup, constraint.sub, constraint.locations @@ -38,20 +38,20 @@ impl<'tcx> OutlivesConstraintSet<'tcx> { /// N.B., this graph contains a "frozen" view of the current /// constraints. Any new constraints added to the `OutlivesConstraintSet` /// after the graph is built will not be present in the graph. - crate fn graph(&self, num_region_vars: usize) -> graph::NormalConstraintGraph { + pub(crate) fn graph(&self, num_region_vars: usize) -> graph::NormalConstraintGraph { graph::ConstraintGraph::new(graph::Normal, self, num_region_vars) } /// Like `graph`, but constraints a reverse graph where `R1: R2` /// represents an edge `R2 -> R1`. - crate fn reverse_graph(&self, num_region_vars: usize) -> graph::ReverseConstraintGraph { + pub(crate) fn reverse_graph(&self, num_region_vars: usize) -> graph::ReverseConstraintGraph { graph::ConstraintGraph::new(graph::Reverse, self, num_region_vars) } /// Computes cycles (SCCs) in the graph of regions. In particular, /// find all regions R1, R2 such that R1: R2 and R2: R1 and group /// them into an SCC, and find the relationships between SCCs. - crate fn compute_sccs( + pub(crate) fn compute_sccs( &self, constraint_graph: &graph::NormalConstraintGraph, static_region: RegionVid, @@ -60,7 +60,7 @@ impl<'tcx> OutlivesConstraintSet<'tcx> { Sccs::new(region_graph) } - crate fn outlives(&self) -> &IndexVec<OutlivesConstraintIndex, OutlivesConstraint<'tcx>> { + pub(crate) fn outlives(&self) -> &IndexVec<OutlivesConstraintIndex, OutlivesConstraint<'tcx>> { &self.outlives } } diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index d38e89cd79e..97d5a8d158e 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -199,7 +199,7 @@ impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> { // Add successor BBs to the work list, if necessary. let bb_data = &self.body[bb]; debug_assert!(hi == bb_data.statements.len()); - for &succ_bb in bb_data.terminator().successors() { + for succ_bb in bb_data.terminator().successors() { if !self.visited.insert(succ_bb) { if succ_bb == location.block && first_lo > 0 { // `succ_bb` has been seen before. If it wasn't @@ -233,7 +233,7 @@ impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> { } impl<'a, 'tcx> Borrows<'a, 'tcx> { - crate fn new( + pub(crate) fn new( tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, nonlexical_regioncx: &'a RegionInferenceContext<'tcx>, diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index 8601fbe27f3..07f182102f3 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -22,7 +22,7 @@ use crate::region_infer::values::RegionElement; use crate::MirBorrowckCtxt; #[derive(Clone)] -crate struct UniverseInfo<'tcx>(UniverseInfoInner<'tcx>); +pub(crate) struct UniverseInfo<'tcx>(UniverseInfoInner<'tcx>); /// What operation a universe was created for. #[derive(Clone)] @@ -36,15 +36,15 @@ enum UniverseInfoInner<'tcx> { } impl<'tcx> UniverseInfo<'tcx> { - crate fn other() -> UniverseInfo<'tcx> { + pub(crate) fn other() -> UniverseInfo<'tcx> { UniverseInfo(UniverseInfoInner::Other) } - crate fn relate(expected: Ty<'tcx>, found: Ty<'tcx>) -> UniverseInfo<'tcx> { + pub(crate) fn relate(expected: Ty<'tcx>, found: Ty<'tcx>) -> UniverseInfo<'tcx> { UniverseInfo(UniverseInfoInner::RelateTys { expected, found }) } - crate fn report_error( + pub(crate) fn report_error( &self, mbcx: &mut MirBorrowckCtxt<'_, 'tcx>, placeholder: ty::PlaceholderRegion, @@ -76,7 +76,7 @@ impl<'tcx> UniverseInfo<'tcx> { } } -crate trait ToUniverseInfo<'tcx> { +pub(crate) trait ToUniverseInfo<'tcx> { fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx>; } diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 3b4e9e95b0e..a3c9da30212 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -13,7 +13,9 @@ use rustc_middle::mir::{ FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm, }; -use rustc_middle::ty::{self, subst::Subst, suggest_constraining_type_params, PredicateKind, Ty}; +use rustc_middle::ty::{ + self, subst::Subst, suggest_constraining_type_params, EarlyBinder, PredicateKind, Ty, +}; use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex}; use rustc_span::symbol::sym; use rustc_span::{BytePos, Span}; @@ -336,7 +338,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let find_fn_kind_from_did = |predicates: &[(ty::Predicate<'tcx>, Span)], substs| { predicates.iter().find_map(|(pred, _)| { let pred = if let Some(substs) = substs { - pred.subst(tcx, substs).kind().skip_binder() + EarlyBinder(*pred).subst(tcx, substs).kind().skip_binder() } else { pred.kind().skip_binder() }; @@ -896,20 +898,23 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { /// /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is /// attempted while a shared borrow is live, then this function will return: - /// - /// ("x", "", "") - /// + /// ``` + /// ("x", "", "") + /// # ; + /// ``` /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while /// a shared borrow of another field `x.y`, then this function will return: - /// - /// ("x", "x.z", "x.y") - /// + /// ``` + /// ("x", "x.z", "x.y") + /// # ; + /// ``` /// In the more complex union case, where the union is a field of a struct, then if a mutable /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of /// another field `x.u.y`, then this function will return: - /// - /// ("x.u", "x.u.z", "x.u.y") - /// + /// ``` + /// ("x.u", "x.u.z", "x.u.y") + /// # ; + /// ``` /// This is used when creating error messages like below: /// /// ```text diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index ffea15bdc33..6ec6b76bb5f 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -467,7 +467,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { block .terminator() .successors() - .map(|bb| Location { statement_index: 0, block: *bb }) + .map(|bb| Location { statement_index: 0, block: bb }) .filter(|s| visited_locations.insert(*s)) .map(|s| { if self.is_back_edge(location, s) { @@ -526,7 +526,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } else { for bb in block.terminator().successors() { - let successor = Location { statement_index: 0, block: *bb }; + let successor = Location { statement_index: 0, block: bb }; if !visited_locations.contains(&successor) && self.find_loop_head_dfs(successor, loop_head, visited_locations) diff --git a/compiler/rustc_borrowck/src/diagnostics/find_use.rs b/compiler/rustc_borrowck/src/diagnostics/find_use.rs index ab4536f00fc..06fca4db0cf 100644 --- a/compiler/rustc_borrowck/src/diagnostics/find_use.rs +++ b/compiler/rustc_borrowck/src/diagnostics/find_use.rs @@ -11,7 +11,7 @@ use rustc_middle::mir::visit::{MirVisitable, PlaceContext, Visitor}; use rustc_middle::mir::{Body, Local, Location}; use rustc_middle::ty::{RegionVid, TyCtxt}; -crate fn find<'tcx>( +pub(crate) fn find<'tcx>( body: &Body<'tcx>, regioncx: &Rc<RegionInferenceContext<'tcx>>, tcx: TyCtxt<'tcx>, @@ -67,8 +67,8 @@ impl<'cx, 'tcx> UseFinder<'cx, 'tcx> { block_data .terminator() .successors() - .filter(|&bb| Some(&Some(*bb)) != block_data.terminator().unwind()) - .map(|&bb| Location { statement_index: 0, block: bb }), + .filter(|&bb| Some(&Some(bb)) != block_data.terminator().unwind()) + .map(|bb| Location { statement_index: 0, block: bb }), ); } } diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 368c0be794b..9581bb65236 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -1,5 +1,6 @@ //! Borrow checker diagnostics. +use itertools::Itertools; use rustc_const_eval::util::{call_kind, CallDesugaringKind}; use rustc_errors::{Applicability, Diagnostic}; use rustc_hir as hir; @@ -34,12 +35,12 @@ mod move_errors; mod mutability_errors; mod region_errors; -crate use bound_region_errors::{ToUniverseInfo, UniverseInfo}; -crate use mutability_errors::AccessKind; -crate use outlives_suggestion::OutlivesSuggestionBuilder; -crate use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors}; -crate use region_name::{RegionName, RegionNameSource}; -crate use rustc_const_eval::util::CallKind; +pub(crate) use bound_region_errors::{ToUniverseInfo, UniverseInfo}; +pub(crate) use mutability_errors::AccessKind; +pub(crate) use outlives_suggestion::OutlivesSuggestionBuilder; +pub(crate) use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors}; +pub(crate) use region_name::{RegionName, RegionNameSource}; +pub(crate) use rustc_const_eval::util::CallKind; use rustc_middle::mir::tcx::PlaceTy; pub(super) struct IncludingDowncast(pub(super) bool); @@ -161,158 +162,103 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } /// End-user visible description of `place` if one can be found. - /// If the place is a temporary for instance, None will be returned. + /// If the place is a temporary for instance, `None` will be returned. pub(super) fn describe_place(&self, place_ref: PlaceRef<'tcx>) -> Option<String> { self.describe_place_with_options(place_ref, IncludingDowncast(false)) } - /// End-user visible description of `place` if one can be found. If the - /// place is a temporary for instance, None will be returned. - /// `IncludingDowncast` parameter makes the function return `Err` if `ProjectionElem` is + /// End-user visible description of `place` if one can be found. If the place is a temporary + /// for instance, `None` will be returned. + /// `IncludingDowncast` parameter makes the function return `None` if `ProjectionElem` is /// `Downcast` and `IncludingDowncast` is true pub(super) fn describe_place_with_options( &self, place: PlaceRef<'tcx>, including_downcast: IncludingDowncast, ) -> Option<String> { + let local = place.local; + let mut autoderef_index = None; let mut buf = String::new(); - match self.append_place_to_string(place, &mut buf, false, &including_downcast) { - Ok(()) => Some(buf), - Err(()) => None, - } - } - - /// Appends end-user visible description of `place` to `buf`. - fn append_place_to_string( - &self, - place: PlaceRef<'tcx>, - buf: &mut String, - mut autoderef: bool, - including_downcast: &IncludingDowncast, - ) -> Result<(), ()> { - match place { - PlaceRef { local, projection: [] } => { - self.append_local_to_string(local, buf)?; - } - PlaceRef { local, projection: [ProjectionElem::Deref] } - if self.body.local_decls[local].is_ref_for_guard() => - { - self.append_place_to_string( - PlaceRef { local, projection: &[] }, - buf, - autoderef, - &including_downcast, - )?; - } - PlaceRef { local, projection: [ProjectionElem::Deref] } - if self.body.local_decls[local].is_ref_to_static() => - { - let local_info = &self.body.local_decls[local].local_info; - if let Some(box LocalInfo::StaticRef { def_id, .. }) = *local_info { - buf.push_str(self.infcx.tcx.item_name(def_id).as_str()); - } else { - unreachable!(); - } - } - PlaceRef { local, projection: [proj_base @ .., elem] } => { - match elem { - ProjectionElem::Deref => { - let upvar_field_projection = self.is_upvar_field_projection(place); - if let Some(field) = upvar_field_projection { - let var_index = field.index(); - let name = self.upvars[var_index].place.to_string(self.infcx.tcx); - if self.upvars[var_index].by_ref { - buf.push_str(&name); - } else { - buf.push('*'); - buf.push_str(&name); - } - } else { - if autoderef { - // FIXME turn this recursion into iteration - self.append_place_to_string( - PlaceRef { local, projection: proj_base }, - buf, - autoderef, - &including_downcast, - )?; - } else { - buf.push('*'); - self.append_place_to_string( - PlaceRef { local, projection: proj_base }, - buf, - autoderef, - &including_downcast, - )?; - } + let mut ok = self.append_local_to_string(local, &mut buf); + + for (index, elem) in place.projection.into_iter().enumerate() { + match elem { + ProjectionElem::Deref => { + if index == 0 { + if self.body.local_decls[local].is_ref_for_guard() { + continue; } - } - ProjectionElem::Downcast(..) => { - self.append_place_to_string( - PlaceRef { local, projection: proj_base }, - buf, - autoderef, - &including_downcast, - )?; - if including_downcast.0 { - return Err(()); + if let Some(box LocalInfo::StaticRef { def_id, .. }) = + &self.body.local_decls[local].local_info + { + buf.push_str(self.infcx.tcx.item_name(*def_id).as_str()); + ok = Ok(()); + continue; } } - ProjectionElem::Field(field, _ty) => { - autoderef = true; - - // FIXME(project-rfc_2229#36): print capture precisely here. - let upvar_field_projection = self.is_upvar_field_projection(place); - if let Some(field) = upvar_field_projection { - let var_index = field.index(); - let name = self.upvars[var_index].place.to_string(self.infcx.tcx); - buf.push_str(&name); - } else { - let field_name = self - .describe_field(PlaceRef { local, projection: proj_base }, *field); - self.append_place_to_string( - PlaceRef { local, projection: proj_base }, - buf, - autoderef, - &including_downcast, - )?; - buf.push('.'); - buf.push_str(&field_name); + if let Some(field) = self.is_upvar_field_projection(PlaceRef { + local, + projection: place.projection.split_at(index + 1).0, + }) { + let var_index = field.index(); + buf = self.upvars[var_index].place.to_string(self.infcx.tcx); + ok = Ok(()); + if !self.upvars[var_index].by_ref { + buf.insert(0, '*'); } - } - ProjectionElem::Index(index) => { - autoderef = true; - - self.append_place_to_string( - PlaceRef { local, projection: proj_base }, - buf, - autoderef, - &including_downcast, - )?; - buf.push('['); - if self.append_local_to_string(*index, buf).is_err() { - buf.push('_'); + } else { + if autoderef_index.is_none() { + autoderef_index = + match place.projection.into_iter().rev().find_position(|elem| { + !matches!( + elem, + ProjectionElem::Deref | ProjectionElem::Downcast(..) + ) + }) { + Some((index, _)) => Some(place.projection.len() - index), + None => Some(0), + }; + } + if index >= autoderef_index.unwrap() { + buf.insert(0, '*'); } - buf.push(']'); } - ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { - autoderef = true; - // Since it isn't possible to borrow an element on a particular index and - // then use another while the borrow is held, don't output indices details - // to avoid confusing the end-user - self.append_place_to_string( - PlaceRef { local, projection: proj_base }, - buf, - autoderef, - &including_downcast, - )?; - buf.push_str("[..]"); + } + ProjectionElem::Downcast(..) if including_downcast.0 => return None, + ProjectionElem::Downcast(..) => (), + ProjectionElem::Field(field, _ty) => { + // FIXME(project-rfc_2229#36): print capture precisely here. + if let Some(field) = self.is_upvar_field_projection(PlaceRef { + local, + projection: place.projection.split_at(index + 1).0, + }) { + buf = self.upvars[field.index()].place.to_string(self.infcx.tcx); + ok = Ok(()); + } else { + let field_name = self.describe_field( + PlaceRef { local, projection: place.projection.split_at(index).0 }, + *field, + ); + buf.push('.'); + buf.push_str(&field_name); } - }; + } + ProjectionElem::Index(index) => { + buf.push('['); + if self.append_local_to_string(*index, &mut buf).is_err() { + buf.push('_'); + } + buf.push(']'); + } + ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { + // Since it isn't possible to borrow an element on a particular index and + // then use another while the borrow is held, don't output indices details + // to avoid confusing the end-user + buf.push_str("[..]"); + } } } - - Ok(()) + ok.ok().map(|_| buf) } /// Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have diff --git a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs index ab9c206a46f..1688d1259fa 100644 --- a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs +++ b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs @@ -149,7 +149,7 @@ impl OutlivesSuggestionBuilder { } /// Add the outlives constraint `fr: outlived_fr` to the set of constraints we need to suggest. - crate fn collect_constraint(&mut self, fr: RegionVid, outlived_fr: RegionVid) { + pub(crate) fn collect_constraint(&mut self, fr: RegionVid, outlived_fr: RegionVid) { debug!("Collected {:?}: {:?}", fr, outlived_fr); // Add to set of constraints for final help note. @@ -158,7 +158,7 @@ impl OutlivesSuggestionBuilder { /// Emit an intermediate note on the given `Diagnostic` if the involved regions are /// suggestable. - crate fn intermediate_suggestion( + pub(crate) fn intermediate_suggestion( &mut self, mbcx: &MirBorrowckCtxt<'_, '_>, errci: &ErrorConstraintInfo, @@ -179,7 +179,7 @@ impl OutlivesSuggestionBuilder { /// If there is a suggestion to emit, add a diagnostic to the buffer. This is the final /// suggestion including all collected constraints. - crate fn add_suggestion(&self, mbcx: &mut MirBorrowckCtxt<'_, '_>) { + pub(crate) fn add_suggestion(&self, mbcx: &mut MirBorrowckCtxt<'_, '_>) { // No constraints to add? Done. if self.constraints_to_add.is_empty() { debug!("No constraints to suggest."); diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 30fe4ea8662..f2b5c83c5c1 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -58,10 +58,10 @@ impl ConstraintDescription for ConstraintCategory { /// /// Usually we expect this to either be empty or contain a small number of items, so we can avoid /// allocation most of the time. -crate type RegionErrors<'tcx> = Vec<RegionErrorKind<'tcx>>; +pub(crate) type RegionErrors<'tcx> = Vec<RegionErrorKind<'tcx>>; #[derive(Clone, Debug)] -crate enum RegionErrorKind<'tcx> { +pub(crate) enum RegionErrorKind<'tcx> { /// A generic bound failure for a type test (`T: 'a`). TypeTestError { type_test: TypeTest<'tcx> }, @@ -259,7 +259,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { /// Report an error because the universal region `fr` was required to outlive /// `outlived_fr` but it is not known to do so. For example: /// - /// ``` + /// ```compile_fail,E0312 /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x } /// ``` /// diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 459d4a783e4..4d2a16aa609 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -15,18 +15,18 @@ use crate::{nll::ToRegionVid, universal_regions::DefiningTy, MirBorrowckCtxt}; /// A name for a particular region used in emitting diagnostics. This name could be a generated /// name like `'1`, a name used by the user like `'a`, or a name like `'static`. #[derive(Debug, Clone)] -crate struct RegionName { +pub(crate) struct RegionName { /// The name of the region (interned). - crate name: Symbol, + pub(crate) name: Symbol, /// Where the region comes from. - crate source: RegionNameSource, + pub(crate) source: RegionNameSource, } /// Denotes the source of a region that is named by a `RegionName`. For example, a free region that /// was named by the user would get `NamedFreeRegion` and `'static` lifetime would get `Static`. /// This helps to print the right kinds of diagnostics. #[derive(Debug, Clone)] -crate enum RegionNameSource { +pub(crate) enum RegionNameSource { /// A bound (not free) region that was substituted at the def site (not an HRTB). NamedEarlyBoundRegion(Span), /// A free region that the user has a name (`'a`) for. @@ -50,7 +50,7 @@ crate enum RegionNameSource { /// Describes what to highlight to explain to the user that we're giving an anonymous region a /// synthesized name, and how to highlight it. #[derive(Debug, Clone)] -crate enum RegionNameHighlight { +pub(crate) enum RegionNameHighlight { /// The anonymous region corresponds to a reference that was found by traversing the type in the HIR. MatchedHirTy(Span), /// The anonymous region corresponds to a `'_` in the generics list of a struct/enum/union. @@ -65,7 +65,7 @@ crate enum RegionNameHighlight { } impl RegionName { - crate fn was_named(&self) -> bool { + pub(crate) fn was_named(&self) -> bool { match self.source { RegionNameSource::NamedEarlyBoundRegion(..) | RegionNameSource::NamedFreeRegion(..) @@ -79,7 +79,7 @@ impl RegionName { } } - crate fn span(&self) -> Option<Span> { + pub(crate) fn span(&self) -> Option<Span> { match self.source { RegionNameSource::Static => None, RegionNameSource::NamedEarlyBoundRegion(span) @@ -98,7 +98,7 @@ impl RegionName { } } - crate fn highlight_region_name(&self, diag: &mut Diagnostic) { + pub(crate) fn highlight_region_name(&self, diag: &mut Diagnostic) { match &self.source { RegionNameSource::NamedFreeRegion(span) | RegionNameSource::NamedEarlyBoundRegion(span) => { @@ -178,11 +178,11 @@ impl Display for RegionName { } impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { - crate fn mir_def_id(&self) -> hir::def_id::LocalDefId { + pub(crate) fn mir_def_id(&self) -> hir::def_id::LocalDefId { self.body.source.def_id().as_local().unwrap() } - crate fn mir_hir_id(&self) -> hir::HirId { + pub(crate) fn mir_hir_id(&self) -> hir::HirId { self.infcx.tcx.hir().local_def_id_to_hir_id(self.mir_def_id()) } @@ -210,7 +210,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { /// Suppose we are trying to give a name to the lifetime of the /// reference `x`: /// - /// ``` + /// ```ignore (pseudo-rust) /// fn foo(x: &u32) { .. } /// ``` /// @@ -222,7 +222,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { /// ``` /// /// and then return the name `'1` for us to use. - crate fn give_region_a_name(&self, fr: RegionVid) -> Option<RegionName> { + pub(crate) fn give_region_a_name(&self, fr: RegionVid) -> Option<RegionName> { debug!( "give_region_a_name(fr={:?}, counter={:?})", fr, @@ -746,7 +746,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { /// e.g. given the function: /// /// ``` - /// async fn foo() -> i32 {} + /// async fn foo() -> i32 { 2 } /// ``` /// /// this function, given the lowered return type of `foo`, an [`OpaqueDef`] that implements `Future<Output=i32>`, diff --git a/compiler/rustc_borrowck/src/diagnostics/var_name.rs b/compiler/rustc_borrowck/src/diagnostics/var_name.rs index 00f62806753..9ba29f04b1a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/var_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/var_name.rs @@ -7,7 +7,7 @@ use rustc_span::source_map::Span; use rustc_span::symbol::Symbol; impl<'tcx> RegionInferenceContext<'tcx> { - crate fn get_var_name_and_span_for_region( + pub(crate) fn get_var_name_and_span_for_region( &self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>, @@ -34,7 +34,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { } /// Search the upvars (if any) to find one that references fr. Return its index. - crate fn get_upvar_index_for_region(&self, tcx: TyCtxt<'tcx>, fr: RegionVid) -> Option<usize> { + pub(crate) fn get_upvar_index_for_region( + &self, + tcx: TyCtxt<'tcx>, + fr: RegionVid, + ) -> Option<usize> { let upvar_index = self.universal_regions().defining_ty.upvar_tys().position(|upvar_ty| { debug!("get_upvar_index_for_region: upvar_ty={:?}", upvar_ty); @@ -57,7 +61,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// Given the index of an upvar, finds its name and the span from where it was /// declared. - crate fn get_upvar_name_and_span_for_region( + pub(crate) fn get_upvar_name_and_span_for_region( &self, tcx: TyCtxt<'tcx>, upvars: &[Upvar<'tcx>], @@ -81,7 +85,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// /// N.B., in the case of a closure, the index is indexing into the signature as seen by the /// user - in particular, index 0 is not the implicit self parameter. - crate fn get_argument_index_for_region( + pub(crate) fn get_argument_index_for_region( &self, tcx: TyCtxt<'tcx>, fr: RegionVid, @@ -107,7 +111,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// Given the index of an argument, finds its name (if any) and the span from where it was /// declared. - crate fn get_argument_name_and_span_for_region( + pub(crate) fn get_argument_name_and_span_for_region( &self, body: &Body<'tcx>, local_names: &IndexVec<Local, Option<Symbol>>, diff --git a/compiler/rustc_borrowck/src/facts.rs b/compiler/rustc_borrowck/src/facts.rs index 86b719bdfa0..7f0a637c9d3 100644 --- a/compiler/rustc_borrowck/src/facts.rs +++ b/compiler/rustc_borrowck/src/facts.rs @@ -25,7 +25,7 @@ impl polonius_engine::FactTypes for RustcFacts { pub type AllFacts = PoloniusFacts<RustcFacts>; -crate trait AllFactsExt { +pub(crate) trait AllFactsExt { /// Returns `true` if there is a need to gather `AllFacts` given the /// current `-Z` flags. fn enabled(tcx: TyCtxt<'_>) -> bool; diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 533439a24aa..a3e7c953ee3 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -2,7 +2,6 @@ #![allow(rustc::potential_query_instability)] #![feature(box_patterns)] -#![feature(crate_visibility_modifier)] #![feature(let_chains)] #![feature(let_else)] #![feature(min_specialization)] @@ -34,14 +33,13 @@ use rustc_middle::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, Stat use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, CapturedPlace, ParamEnv, RegionVid, TyCtxt}; -use rustc_session::lint::builtin::{MUTABLE_BORROW_RESERVATION_CONFLICT, UNUSED_MUT}; -use rustc_span::{Span, Symbol, DUMMY_SP}; +use rustc_session::lint::builtin::UNUSED_MUT; +use rustc_span::{Span, Symbol}; use either::Either; use smallvec::SmallVec; use std::cell::RefCell; use std::collections::BTreeMap; -use std::mem; use std::rc::Rc; use rustc_mir_dataflow::impls::{ @@ -312,7 +310,6 @@ fn do_mir_borrowck<'a, 'tcx>( locals_are_invalidated_at_exit, access_place_error_reported: Default::default(), reservation_error_reported: Default::default(), - reservation_warnings: Default::default(), uninitialized_error_reported: Default::default(), regioncx: regioncx.clone(), used_mut: Default::default(), @@ -344,7 +341,6 @@ fn do_mir_borrowck<'a, 'tcx>( fn_self_span_reported: Default::default(), access_place_error_reported: Default::default(), reservation_error_reported: Default::default(), - reservation_warnings: Default::default(), uninitialized_error_reported: Default::default(), regioncx: Rc::clone(®ioncx), used_mut: Default::default(), @@ -377,34 +373,6 @@ fn do_mir_borrowck<'a, 'tcx>( &mut mbcx, ); - // Convert any reservation warnings into lints. - let reservation_warnings = mem::take(&mut mbcx.reservation_warnings); - for (_, (place, span, location, bk, borrow)) in reservation_warnings { - let initial_diag = mbcx.report_conflicting_borrow(location, (place, span), bk, &borrow); - - let scope = mbcx.body.source_info(location).scope; - let lint_root = match &mbcx.body.source_scopes[scope].local_data { - ClearCrossCrate::Set(data) => data.lint_root, - _ => tcx.hir().local_def_id_to_hir_id(def.did), - }; - - // Span and message don't matter; we overwrite them below anyway - mbcx.infcx.tcx.struct_span_lint_hir( - MUTABLE_BORROW_RESERVATION_CONFLICT, - lint_root, - DUMMY_SP, - |lint| { - let mut diag = lint.build(""); - - diag.message = initial_diag.styled_message().clone(); - diag.span = initial_diag.span.clone(); - - mbcx.buffer_non_error_diag(diag); - }, - ); - initial_diag.cancel(); - } - // For each non-user used mutable variable, check if it's been assigned from // a user-declared local. If so, then put that local into the used_mut set. // Note that this set is expected to be small - only upvars from closures @@ -539,11 +507,6 @@ struct MirBorrowckCtxt<'cx, 'tcx> { /// used to report extra information for `FnSelfUse`, to avoid /// unnecessarily verbose errors. fn_self_span_reported: FxHashSet<Span>, - /// Migration warnings to be reported for #56254. We delay reporting these - /// so that we can suppress the warning if there's a corresponding error - /// for the activation of the borrow. - reservation_warnings: - FxHashMap<BorrowIndex, (Place<'tcx>, Span, Location, BorrowKind, BorrowData<'tcx>)>, /// This field keeps track of errors reported in the checking of uninitialized variables, /// so that we don't report seemingly duplicate errors. uninitialized_error_reported: FxHashSet<PlaceRef<'tcx>>, @@ -995,12 +958,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let conflict_error = self.check_access_for_conflict(location, place_span, sd, rw, flow_state); - if let (Activation(_, borrow_idx), true) = (kind.1, conflict_error) { - // Suppress this warning when there's an error being emitted for the - // same borrow: fixing the error is likely to fix the warning. - self.reservation_warnings.remove(&borrow_idx); - } - if conflict_error || mutability_error { debug!("access_place: logging error place_span=`{:?}` kind=`{:?}`", place_span, kind); self.access_place_error_reported.insert((place_span.0, place_span.1)); @@ -1067,6 +1024,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { BorrowKind::Unique | BorrowKind::Mut { .. }, ) => Control::Continue, + (Reservation(_), BorrowKind::Shallow | BorrowKind::Shared) => { + // This used to be a future compatibility warning (to be + // disallowed on NLL). See rust-lang/rust#56254 + Control::Continue + } + (Write(WriteKind::Move), BorrowKind::Shallow) => { // Handled by initialization checks. Control::Continue @@ -1095,27 +1058,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { Control::Break } - ( - Reservation(WriteKind::MutableBorrow(bk)), - BorrowKind::Shallow | BorrowKind::Shared, - ) if { tcx.migrate_borrowck() && this.borrow_set.contains(&location) } => { - let bi = this.borrow_set.get_index_of(&location).unwrap(); - debug!( - "recording invalid reservation of place: {:?} with \ - borrow index {:?} as warning", - place_span.0, bi, - ); - // rust-lang/rust#56254 - This was previously permitted on - // the 2018 edition so we emit it as a warning. We buffer - // these separately so that we only emit a warning if borrow - // checking was otherwise successful. - this.reservation_warnings - .insert(bi, (place_span.0, place_span.1, location, bk, borrow.clone())); - - // Don't suppress actual errors. - Control::Continue - } - (Reservation(kind) | Activation(kind, _) | Write(kind), _) => { match rw { Reservation(..) => { diff --git a/compiler/rustc_borrowck/src/location.rs b/compiler/rustc_borrowck/src/location.rs index c89da5514fd..70a31169498 100644 --- a/compiler/rustc_borrowck/src/location.rs +++ b/compiler/rustc_borrowck/src/location.rs @@ -30,7 +30,7 @@ pub enum RichLocation { } impl LocationTable { - crate fn new(body: &Body<'_>) -> Self { + pub(crate) fn new(body: &Body<'_>) -> Self { let mut num_points = 0; let statements_before_block = body .basic_blocks() diff --git a/compiler/rustc_borrowck/src/member_constraints.rs b/compiler/rustc_borrowck/src/member_constraints.rs index 0fe44328fd9..61838c41e39 100644 --- a/compiler/rustc_borrowck/src/member_constraints.rs +++ b/compiler/rustc_borrowck/src/member_constraints.rs @@ -8,7 +8,7 @@ use std::ops::Index; /// Compactly stores a set of `R0 member of [R1...Rn]` constraints, /// indexed by the region `R0`. -crate struct MemberConstraintSet<'tcx, R> +pub(crate) struct MemberConstraintSet<'tcx, R> where R: Copy + Eq, { @@ -28,17 +28,17 @@ where } /// Represents a `R0 member of [R1..Rn]` constraint -crate struct NllMemberConstraint<'tcx> { +pub(crate) struct NllMemberConstraint<'tcx> { next_constraint: Option<NllMemberConstraintIndex>, /// The span where the hidden type was instantiated. - crate definition_span: Span, + pub(crate) definition_span: Span, /// The hidden type in which `R0` appears. (Used in error reporting.) - crate hidden_ty: Ty<'tcx>, + pub(crate) hidden_ty: Ty<'tcx>, /// The region `R0`. - crate member_region_vid: ty::RegionVid, + pub(crate) member_region_vid: ty::RegionVid, /// Index of `R1` in `choice_regions` vector from `MemberConstraintSet`. start_index: usize, @@ -48,7 +48,7 @@ crate struct NllMemberConstraint<'tcx> { } rustc_index::newtype_index! { - crate struct NllMemberConstraintIndex { + pub(crate) struct NllMemberConstraintIndex { DEBUG_FORMAT = "MemberConstraintIndex({})" } } @@ -73,7 +73,7 @@ impl<'tcx> MemberConstraintSet<'tcx, ty::RegionVid> { /// within into `RegionVid` format -- it typically consults the /// `UniversalRegions` data structure that is known to the caller /// (but which this code is unaware of). - crate fn push_constraint( + pub(crate) fn push_constraint( &mut self, m_c: &MemberConstraint<'tcx>, mut to_region_vid: impl FnMut(ty::Region<'tcx>) -> ty::RegionVid, @@ -106,7 +106,7 @@ where /// the original `RegionVid` to an scc index. In some cases, we /// may have multiple `R1` values mapping to the same `R2` key -- that /// is ok, the two sets will be merged. - crate fn into_mapped<R2>( + pub(crate) fn into_mapped<R2>( self, mut map_fn: impl FnMut(R1) -> R2, ) -> MemberConstraintSet<'tcx, R2> @@ -144,14 +144,14 @@ impl<R> MemberConstraintSet<'_, R> where R: Copy + Hash + Eq, { - crate fn all_indices(&self) -> impl Iterator<Item = NllMemberConstraintIndex> + '_ { + pub(crate) fn all_indices(&self) -> impl Iterator<Item = NllMemberConstraintIndex> + '_ { self.constraints.indices() } /// Iterate down the constraint indices associated with a given /// peek-region. You can then use `choice_regions` and other /// methods to access data. - crate fn indices( + pub(crate) fn indices( &self, member_region_vid: R, ) -> impl Iterator<Item = NllMemberConstraintIndex> + '_ { @@ -169,10 +169,10 @@ where /// Returns the "choice regions" for a given member /// constraint. This is the `R1..Rn` from a constraint like: /// - /// ``` + /// ```text /// R0 member of [R1..Rn] /// ``` - crate fn choice_regions(&self, pci: NllMemberConstraintIndex) -> &[ty::RegionVid] { + pub(crate) fn choice_regions(&self, pci: NllMemberConstraintIndex) -> &[ty::RegionVid] { let NllMemberConstraint { start_index, end_index, .. } = &self.constraints[pci]; &self.choice_regions[*start_index..*end_index] } @@ -195,14 +195,14 @@ where /// /// Before: /// -/// ``` +/// ```text /// target_list: A -> B -> C -> (None) /// source_list: D -> E -> F -> (None) /// ``` /// /// After: /// -/// ``` +/// ```text /// target_list: A -> B -> C -> D -> E -> F -> (None) /// ``` fn append_list( diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 927eb080b20..2440ae9780d 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -42,7 +42,7 @@ pub type PoloniusOutput = Output<RustcFacts>; /// The output of `nll::compute_regions`. This includes the computed `RegionInferenceContext`, any /// closure requirements to propagate, and any generated errors. -crate struct NllOutput<'tcx> { +pub(crate) struct NllOutput<'tcx> { pub regioncx: RegionInferenceContext<'tcx>, pub opaque_type_values: VecMap<DefId, OpaqueHiddenType<'tcx>>, pub polonius_input: Option<Box<AllFacts>>, @@ -108,7 +108,7 @@ fn populate_polonius_move_facts( // 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() { + for successor in block_data.terminator().successors() { if body[successor].is_cleanup { continue; } @@ -457,6 +457,6 @@ impl ToRegionVid for RegionVid { } } -crate trait ConstraintDescription { +pub(crate) trait ConstraintDescription { fn description(&self) -> &'static str; } diff --git a/compiler/rustc_borrowck/src/place_ext.rs b/compiler/rustc_borrowck/src/place_ext.rs index 83ff1595b0b..93d202e49a1 100644 --- a/compiler/rustc_borrowck/src/place_ext.rs +++ b/compiler/rustc_borrowck/src/place_ext.rs @@ -5,7 +5,7 @@ use rustc_middle::mir::{Body, Mutability, Place}; use rustc_middle::ty::{self, TyCtxt}; /// Extension methods for the `Place` type. -crate trait PlaceExt<'tcx> { +pub(crate) trait PlaceExt<'tcx> { /// Returns `true` if we can safely ignore borrows of this place. /// This is true whenever there is no action that the user can do /// to the place `self` that would invalidate the borrow. This is true diff --git a/compiler/rustc_borrowck/src/places_conflict.rs b/compiler/rustc_borrowck/src/places_conflict.rs index 5a935c3b8fb..97335fd0dff 100644 --- a/compiler/rustc_borrowck/src/places_conflict.rs +++ b/compiler/rustc_borrowck/src/places_conflict.rs @@ -14,7 +14,7 @@ use std::iter; /// being run in the calling context, the conservative choice is to assume the compared indices /// are disjoint (and therefore, do not overlap). #[derive(Copy, Clone, Debug, Eq, PartialEq)] -crate enum PlaceConflictBias { +pub(crate) enum PlaceConflictBias { Overlap, NoOverlap, } @@ -22,7 +22,7 @@ crate enum PlaceConflictBias { /// Helper function for checking if places conflict with a mutable borrow and deep access depth. /// This is used to check for places conflicting outside of the borrow checking code (such as in /// dataflow). -crate fn places_conflict<'tcx>( +pub(crate) fn places_conflict<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, borrow_place: Place<'tcx>, diff --git a/compiler/rustc_borrowck/src/region_infer/graphviz.rs b/compiler/rustc_borrowck/src/region_infer/graphviz.rs index 95048d50f11..f31ccd74ca6 100644 --- a/compiler/rustc_borrowck/src/region_infer/graphviz.rs +++ b/compiler/rustc_borrowck/src/region_infer/graphviz.rs @@ -11,12 +11,12 @@ use rustc_graphviz as dot; impl<'tcx> RegionInferenceContext<'tcx> { /// Write out the region constraint graph. - crate fn dump_graphviz_raw_constraints(&self, mut w: &mut dyn Write) -> io::Result<()> { + pub(crate) fn dump_graphviz_raw_constraints(&self, mut w: &mut dyn Write) -> io::Result<()> { dot::render(&RawConstraints { regioncx: self }, &mut w) } /// Write out the region constraint graph. - crate fn dump_graphviz_scc_constraints(&self, mut w: &mut dyn Write) -> io::Result<()> { + pub(crate) fn dump_graphviz_scc_constraints(&self, mut w: &mut dyn Write) -> io::Result<()> { let mut nodes_per_scc: IndexVec<ConstraintSccIndex, _> = self.constraint_sccs.all_sccs().map(|_| Vec::new()).collect(); diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index cf03f34a4ec..dc6337c54ed 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -436,14 +436,14 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// minimum values. /// /// For example: - /// - /// fn foo<'a, 'b>(..) where 'a: 'b - /// + /// ``` + /// fn foo<'a, 'b>( /* ... */ ) where 'a: 'b { /* ... */ } + /// ``` /// would initialize two variables like so: - /// - /// R0 = { CFG, R0 } // 'a - /// R1 = { CFG, R0, R1 } // 'b - /// + /// ```ignore (illustrative) + /// R0 = { CFG, R0 } // 'a + /// R1 = { CFG, R0, R1 } // 'b + /// ``` /// Here, R0 represents `'a`, and it contains (a) the entire CFG /// and (b) any universally quantified regions that it outlives, /// which in this case is just itself. R1 (`'b`) in contrast also @@ -513,26 +513,26 @@ impl<'tcx> RegionInferenceContext<'tcx> { } /// Adds annotations for `#[rustc_regions]`; see `UniversalRegions::annotate`. - crate fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut Diagnostic) { + pub(crate) fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut Diagnostic) { self.universal_regions.annotate(tcx, err) } /// Returns `true` if the region `r` contains the point `p`. /// /// Panics if called before `solve()` executes, - crate fn region_contains(&self, r: impl ToRegionVid, p: impl ToElementIndex) -> bool { + pub(crate) fn region_contains(&self, r: impl ToRegionVid, p: impl ToElementIndex) -> bool { let scc = self.constraint_sccs.scc(r.to_region_vid()); self.scc_values.contains(scc, p) } /// Returns access to the value of `r` for debugging purposes. - crate fn region_value_str(&self, r: RegionVid) -> String { + pub(crate) fn region_value_str(&self, r: RegionVid) -> String { let scc = self.constraint_sccs.scc(r.to_region_vid()); self.scc_values.region_value_str(scc) } /// Returns access to the value of `r` for debugging purposes. - crate fn region_universe(&self, r: RegionVid) -> ty::UniverseIndex { + pub(crate) fn region_universe(&self, r: RegionVid) -> ty::UniverseIndex { let scc = self.constraint_sccs.scc(r.to_region_vid()); self.scc_universes[scc] } @@ -1310,9 +1310,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// whether any of the constraints were too strong. In particular, /// we want to check for a case where a universally quantified /// region exceeded its bounds. Consider: - /// - /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x } - /// + /// ```compile_fail,E0312 + /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x } + /// ``` /// In this case, returning `x` requires `&'a u32 <: &'b u32` /// and hence we establish (transitively) a constraint that /// `'a: 'b`. The `propagate_constraints` code above will @@ -1366,9 +1366,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// <https://smallcultfollowing.com/babysteps/blog/2019/01/17/polonius-and-region-errors/> /// /// In the canonical example - /// - /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x } - /// + /// ```compile_fail,E0312 + /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x } + /// ``` /// returning `x` requires `&'a u32 <: &'b u32` and hence we establish (transitively) a /// constraint that `'a: 'b`. It is an error that we have no evidence that this /// constraint holds. @@ -1693,7 +1693,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// that cannot be named by `fr1`; in that case, we will require /// that `fr1: 'static` because it is the only way to `fr1: r` to /// be satisfied. (See `add_incompatible_universe`.) - crate fn provides_universal_region( + pub(crate) fn provides_universal_region( &self, r: RegionVid, fr1: RegionVid, @@ -1712,7 +1712,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// If `r2` represents a placeholder region, then this returns /// `true` if `r1` cannot name that placeholder in its /// value; otherwise, returns `false`. - crate fn cannot_name_placeholder(&self, r1: RegionVid, r2: RegionVid) -> bool { + pub(crate) fn cannot_name_placeholder(&self, r1: RegionVid, r2: RegionVid) -> bool { debug!("cannot_name_value_of(r1={:?}, r2={:?})", r1, r2); match self.definitions[r2].origin { @@ -1731,7 +1731,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } - crate fn retrieve_closure_constraint_info( + pub(crate) fn retrieve_closure_constraint_info( &self, _body: &Body<'tcx>, constraint: &OutlivesConstraint<'tcx>, @@ -1766,7 +1766,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } /// Finds a good `ObligationCause` to blame for the fact that `fr1` outlives `fr2`. - crate fn find_outlives_blame_span( + pub(crate) fn find_outlives_blame_span( &self, body: &Body<'tcx>, fr1: RegionVid, @@ -1788,7 +1788,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// /// Returns: a series of constraints as well as the region `R` /// that passed the target test. - crate fn find_constraint_paths_between_regions( + pub(crate) fn find_constraint_paths_between_regions( &self, from_region: RegionVid, target_test: impl Fn(RegionVid) -> bool, @@ -1882,7 +1882,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// Finds some region R such that `fr1: R` and `R` is live at `elem`. #[instrument(skip(self), level = "trace")] - crate fn find_sub_region_live_at(&self, fr1: RegionVid, elem: Location) -> RegionVid { + pub(crate) fn find_sub_region_live_at(&self, fr1: RegionVid, elem: Location) -> RegionVid { trace!(scc = ?self.constraint_sccs.scc(fr1)); trace!(universe = ?self.scc_universes[self.constraint_sccs.scc(fr1)]); self.find_constraint_paths_between_regions(fr1, |r| { @@ -1919,7 +1919,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } /// Get the region outlived by `longer_fr` and live at `element`. - crate fn region_from_element( + pub(crate) fn region_from_element( &self, longer_fr: RegionVid, element: &RegionElement, @@ -1939,17 +1939,17 @@ impl<'tcx> RegionInferenceContext<'tcx> { } /// Get the region definition of `r`. - crate fn region_definition(&self, r: RegionVid) -> &RegionDefinition<'tcx> { + pub(crate) fn region_definition(&self, r: RegionVid) -> &RegionDefinition<'tcx> { &self.definitions[r] } /// Check if the SCC of `r` contains `upper`. - crate fn upper_bound_in_region_scc(&self, r: RegionVid, upper: RegionVid) -> bool { + pub(crate) fn upper_bound_in_region_scc(&self, r: RegionVid, upper: RegionVid) -> bool { let r_scc = self.constraint_sccs.scc(r); self.scc_values.contains(r_scc, upper) } - crate fn universal_regions(&self) -> &UniversalRegions<'tcx> { + pub(crate) fn universal_regions(&self) -> &UniversalRegions<'tcx> { self.universal_regions.as_ref() } @@ -1959,7 +1959,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// creating a constraint path that forces `R` to outlive /// `from_region`, and then finding the best choices within that /// path to blame. - crate fn best_blame_constraint( + pub(crate) fn best_blame_constraint( &self, body: &Body<'tcx>, from_region: RegionVid, @@ -2171,7 +2171,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { categorized_path.remove(0) } - crate fn universe_info(&self, universe: ty::UniverseIndex) -> UniverseInfo<'tcx> { + pub(crate) fn universe_info(&self, universe: ty::UniverseIndex) -> UniverseInfo<'tcx> { self.universe_causes[&universe].clone() } } diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index fa07c4fa949..81073758791 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -1,11 +1,8 @@ -use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::vec_map::VecMap; use rustc_hir::def_id::DefId; use rustc_hir::OpaqueTyOrigin; use rustc_infer::infer::InferCtxt; -use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey, TyCtxt, TypeFoldable}; -use rustc_span::Span; use rustc_trait_selection::opaque_types::InferCtxtExt; use super::RegionInferenceContext; @@ -107,20 +104,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { let opaque_type_key = OpaqueTypeKey { def_id: opaque_type_key.def_id, substs: universal_substs }; - let remapped_type = infcx.infer_opaque_definition_from_instantiation( + let ty = infcx.infer_opaque_definition_from_instantiation( opaque_type_key, universal_concrete_type, - ); - let ty = if check_opaque_type_parameter_valid( - infcx.tcx, - opaque_type_key, origin, - concrete_type.span, - ) { - remapped_type - } else { - infcx.tcx.ty_error() - }; + ); // Sometimes two opaque types are the same only after we remap the generic parameters // back to the opaque type definition. E.g. we may have `OpaqueType<X, Y>` mapped to `(X, Y)` // and `OpaqueType<Y, X>` mapped to `(Y, X)`, and those are the same, but we only know that @@ -183,95 +171,3 @@ impl<'tcx> RegionInferenceContext<'tcx> { }) } } - -fn check_opaque_type_parameter_valid( - tcx: TyCtxt<'_>, - opaque_type_key: OpaqueTypeKey<'_>, - origin: OpaqueTyOrigin, - span: Span, -) -> bool { - match origin { - // No need to check return position impl trait (RPIT) - // because for type and const parameters they are correct - // by construction: we convert - // - // fn foo<P0..Pn>() -> impl Trait - // - // into - // - // type Foo<P0...Pn> - // fn foo<P0..Pn>() -> Foo<P0...Pn>. - // - // For lifetime parameters we convert - // - // fn foo<'l0..'ln>() -> impl Trait<'l0..'lm> - // - // into - // - // type foo::<'p0..'pn>::Foo<'q0..'qm> - // fn foo<l0..'ln>() -> foo::<'static..'static>::Foo<'l0..'lm>. - // - // which would error here on all of the `'static` args. - OpaqueTyOrigin::FnReturn(..) | OpaqueTyOrigin::AsyncFn(..) => return true, - // Check these - OpaqueTyOrigin::TyAlias => {} - } - let opaque_generics = tcx.generics_of(opaque_type_key.def_id); - let mut seen_params: FxHashMap<_, Vec<_>> = FxHashMap::default(); - for (i, arg) in opaque_type_key.substs.iter().enumerate() { - let arg_is_param = match arg.unpack() { - GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)), - GenericArgKind::Lifetime(lt) if lt.is_static() => { - tcx.sess - .struct_span_err(span, "non-defining opaque type use in defining scope") - .span_label( - tcx.def_span(opaque_generics.param_at(i, tcx).def_id), - "cannot use static lifetime; use a bound lifetime \ - instead or remove the lifetime parameter from the \ - opaque type", - ) - .emit(); - return false; - } - GenericArgKind::Lifetime(lt) => { - matches!(*lt, ty::ReEarlyBound(_) | ty::ReFree(_)) - } - GenericArgKind::Const(ct) => matches!(ct.val(), ty::ConstKind::Param(_)), - }; - - if arg_is_param { - seen_params.entry(arg).or_default().push(i); - } else { - // Prevent `fn foo() -> Foo<u32>` from being defining. - let opaque_param = opaque_generics.param_at(i, tcx); - tcx.sess - .struct_span_err(span, "non-defining opaque type use in defining scope") - .span_note( - tcx.def_span(opaque_param.def_id), - &format!( - "used non-generic {} `{}` for generic parameter", - opaque_param.kind.descr(), - arg, - ), - ) - .emit(); - return false; - } - } - - for (_, indices) in seen_params { - if indices.len() > 1 { - let descr = opaque_generics.param_at(indices[0], tcx).kind.descr(); - let spans: Vec<_> = indices - .into_iter() - .map(|i| tcx.def_span(opaque_generics.param_at(i, tcx).def_id)) - .collect(); - tcx.sess - .struct_span_err(span, "non-defining opaque type use in defining scope") - .span_note(spans, &format!("{} used multiple times", descr)) - .emit(); - return false; - } - } - true -} diff --git a/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs b/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs index 056907dcb16..1e6798eee3d 100644 --- a/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs +++ b/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs @@ -8,7 +8,7 @@ use rustc_middle::ty::RegionVid; use std::ops::Range; use std::rc::Rc; -crate struct ReverseSccGraph { +pub(crate) struct ReverseSccGraph { graph: VecGraph<ConstraintSccIndex>, /// For each SCC, the range of `universal_regions` that use that SCC as /// their value. diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index 4a70535c63b..c81ef10f7c7 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -10,7 +10,7 @@ use std::fmt::Debug; use std::rc::Rc; /// Maps between a `Location` and a `PointIndex` (and vice versa). -crate struct RegionValueElements { +pub(crate) struct RegionValueElements { /// For each basic block, how many points are contained within? statements_before_block: IndexVec<BasicBlock, usize>, @@ -22,7 +22,7 @@ crate struct RegionValueElements { } impl RegionValueElements { - crate fn new(body: &Body<'_>) -> Self { + pub(crate) fn new(body: &Body<'_>) -> Self { let mut num_points = 0; let statements_before_block: IndexVec<BasicBlock, usize> = body .basic_blocks() @@ -45,30 +45,30 @@ impl RegionValueElements { } /// Total number of point indices - crate fn num_points(&self) -> usize { + pub(crate) fn num_points(&self) -> usize { self.num_points } /// Converts a `Location` into a `PointIndex`. O(1). - crate fn point_from_location(&self, location: Location) -> PointIndex { + pub(crate) fn point_from_location(&self, location: Location) -> PointIndex { let Location { block, statement_index } = location; let start_index = self.statements_before_block[block]; PointIndex::new(start_index + statement_index) } /// Converts a `Location` into a `PointIndex`. O(1). - crate fn entry_point(&self, block: BasicBlock) -> PointIndex { + pub(crate) fn entry_point(&self, block: BasicBlock) -> PointIndex { let start_index = self.statements_before_block[block]; PointIndex::new(start_index) } /// Return the PointIndex for the block start of this index. - crate fn to_block_start(&self, index: PointIndex) -> PointIndex { + pub(crate) fn to_block_start(&self, index: PointIndex) -> PointIndex { PointIndex::new(self.statements_before_block[self.basic_blocks[index]]) } /// Converts a `PointIndex` back to a location. O(1). - crate fn to_location(&self, index: PointIndex) -> Location { + pub(crate) fn to_location(&self, index: PointIndex) -> Location { assert!(index.index() < self.num_points); let block = self.basic_blocks[index]; let start_index = self.statements_before_block[block]; @@ -80,7 +80,7 @@ impl RegionValueElements { /// out of range (because they round up to the nearest 2^N number /// of bits). Use this function to filter such points out if you /// like. - crate fn point_in_range(&self, index: PointIndex) -> bool { + pub(crate) fn point_in_range(&self, index: PointIndex) -> bool { index.index() < self.num_points } } @@ -99,7 +99,7 @@ rustc_index::newtype_index! { /// An individual element in a region value -- the value of a /// particular region variable consists of a set of these elements. #[derive(Debug, Clone)] -crate enum RegionElement { +pub(crate) enum RegionElement { /// A point in the control-flow graph. Location(Location), @@ -114,7 +114,7 @@ crate enum RegionElement { /// When we initially compute liveness, we use an interval matrix storing /// liveness ranges for each region-vid. -crate struct LivenessValues<N: Idx> { +pub(crate) struct LivenessValues<N: Idx> { elements: Rc<RegionValueElements>, points: SparseIntervalMatrix<N, PointIndex>, } @@ -123,18 +123,18 @@ impl<N: Idx> LivenessValues<N> { /// Creates a new set of "region values" that tracks causal information. /// Each of the regions in num_region_variables will be initialized with an /// empty set of points and no causal information. - crate fn new(elements: Rc<RegionValueElements>) -> Self { + pub(crate) fn new(elements: Rc<RegionValueElements>) -> Self { Self { points: SparseIntervalMatrix::new(elements.num_points), elements } } /// Iterate through each region that has a value in this set. - crate fn rows(&self) -> impl Iterator<Item = N> { + pub(crate) fn rows(&self) -> impl Iterator<Item = N> { self.points.rows() } /// Adds the given element to the value for the given region. Returns whether /// the element is newly added (i.e., was not already present). - crate fn add_element(&mut self, row: N, location: Location) -> bool { + pub(crate) fn add_element(&mut self, row: N, location: Location) -> bool { debug!("LivenessValues::add(r={:?}, location={:?})", row, location); let index = self.elements.point_from_location(location); self.points.insert(row, index) @@ -142,24 +142,24 @@ impl<N: Idx> LivenessValues<N> { /// Adds all the elements in the given bit array into the given /// region. Returns whether any of them are newly added. - crate fn add_elements(&mut self, row: N, locations: &IntervalSet<PointIndex>) -> bool { + pub(crate) fn add_elements(&mut self, row: N, locations: &IntervalSet<PointIndex>) -> bool { debug!("LivenessValues::add_elements(row={:?}, locations={:?})", row, locations); self.points.union_row(row, locations) } /// Adds all the control-flow points to the values for `r`. - crate fn add_all_points(&mut self, row: N) { + pub(crate) fn add_all_points(&mut self, row: N) { self.points.insert_all_into_row(row); } /// Returns `true` if the region `r` contains the given element. - crate fn contains(&self, row: N, location: Location) -> bool { + pub(crate) fn contains(&self, row: N, location: Location) -> bool { let index = self.elements.point_from_location(location); self.points.row(row).map_or(false, |r| r.contains(index)) } /// Returns an iterator of all the elements contained by the region `r` - crate fn get_elements(&self, row: N) -> impl Iterator<Item = Location> + '_ { + pub(crate) fn get_elements(&self, row: N) -> impl Iterator<Item = Location> + '_ { self.points .row(row) .into_iter() @@ -169,7 +169,7 @@ impl<N: Idx> LivenessValues<N> { } /// Returns a "pretty" string value of the region. Meant for debugging. - crate fn region_value_str(&self, r: N) -> String { + pub(crate) fn region_value_str(&self, r: N) -> String { region_value_str(self.get_elements(r).map(RegionElement::Location)) } } @@ -178,25 +178,28 @@ impl<N: Idx> LivenessValues<N> { /// rustc to the internal `PlaceholderIndex` values that are used in /// NLL. #[derive(Default)] -crate struct PlaceholderIndices { +pub(crate) struct PlaceholderIndices { indices: FxIndexSet<ty::PlaceholderRegion>, } impl PlaceholderIndices { - crate fn insert(&mut self, placeholder: ty::PlaceholderRegion) -> PlaceholderIndex { + pub(crate) fn insert(&mut self, placeholder: ty::PlaceholderRegion) -> PlaceholderIndex { let (index, _) = self.indices.insert_full(placeholder); index.into() } - crate fn lookup_index(&self, placeholder: ty::PlaceholderRegion) -> PlaceholderIndex { + pub(crate) fn lookup_index(&self, placeholder: ty::PlaceholderRegion) -> PlaceholderIndex { self.indices.get_index_of(&placeholder).unwrap().into() } - crate fn lookup_placeholder(&self, placeholder: PlaceholderIndex) -> ty::PlaceholderRegion { + pub(crate) fn lookup_placeholder( + &self, + placeholder: PlaceholderIndex, + ) -> ty::PlaceholderRegion { self.indices[placeholder.index()] } - crate fn len(&self) -> usize { + pub(crate) fn len(&self) -> usize { self.indices.len() } } @@ -220,7 +223,7 @@ impl PlaceholderIndices { /// because (since it is returned) it must live for at least `'a`. But /// it would also contain various points from within the function. #[derive(Clone)] -crate struct RegionValues<N: Idx> { +pub(crate) struct RegionValues<N: Idx> { elements: Rc<RegionValueElements>, placeholder_indices: Rc<PlaceholderIndices>, points: SparseIntervalMatrix<N, PointIndex>, @@ -235,7 +238,7 @@ impl<N: Idx> RegionValues<N> { /// Creates a new set of "region values" that tracks causal information. /// Each of the regions in num_region_variables will be initialized with an /// empty set of points and no causal information. - crate fn new( + pub(crate) fn new( elements: &Rc<RegionValueElements>, num_universal_regions: usize, placeholder_indices: &Rc<PlaceholderIndices>, @@ -252,33 +255,33 @@ impl<N: Idx> RegionValues<N> { /// Adds the given element to the value for the given region. Returns whether /// the element is newly added (i.e., was not already present). - crate fn add_element(&mut self, r: N, elem: impl ToElementIndex) -> bool { + pub(crate) fn add_element(&mut self, r: N, elem: impl ToElementIndex) -> bool { debug!("add(r={:?}, elem={:?})", r, elem); elem.add_to_row(self, r) } /// Adds all the control-flow points to the values for `r`. - crate fn add_all_points(&mut self, r: N) { + pub(crate) fn add_all_points(&mut self, r: N) { self.points.insert_all_into_row(r); } /// Adds all elements in `r_from` to `r_to` (because e.g., `r_to: /// r_from`). - crate fn add_region(&mut self, r_to: N, r_from: N) -> bool { + pub(crate) fn add_region(&mut self, r_to: N, r_from: N) -> bool { self.points.union_rows(r_from, r_to) | self.free_regions.union_rows(r_from, r_to) | self.placeholders.union_rows(r_from, r_to) } /// Returns `true` if the region `r` contains the given element. - crate fn contains(&self, r: N, elem: impl ToElementIndex) -> bool { + pub(crate) fn contains(&self, r: N, elem: impl ToElementIndex) -> bool { elem.contained_in_row(self, r) } /// `self[to] |= values[from]`, essentially: that is, take all the /// elements for the region `from` from `values` and add them to /// the region `to` in `self`. - crate fn merge_liveness<M: Idx>(&mut self, to: N, from: M, values: &LivenessValues<M>) { + pub(crate) fn merge_liveness<M: Idx>(&mut self, to: N, from: M, values: &LivenessValues<M>) { if let Some(set) = values.points.row(from) { self.points.union_row(to, set); } @@ -286,7 +289,7 @@ impl<N: Idx> RegionValues<N> { /// Returns `true` if `sup_region` contains all the CFG points that /// `sub_region` contains. Ignores universal regions. - crate fn contains_points(&self, sup_region: N, sub_region: N) -> bool { + pub(crate) fn contains_points(&self, sup_region: N, sub_region: N) -> bool { if let Some(sub_row) = self.points.row(sub_region) { if let Some(sup_row) = self.points.row(sup_region) { sup_row.superset(sub_row) @@ -301,7 +304,7 @@ impl<N: Idx> RegionValues<N> { } /// Returns the locations contained within a given region `r`. - crate fn locations_outlived_by<'a>(&'a self, r: N) -> impl Iterator<Item = Location> + 'a { + pub(crate) fn locations_outlived_by<'a>(&'a self, r: N) -> impl Iterator<Item = Location> + 'a { self.points.row(r).into_iter().flat_map(move |set| { set.iter() .take_while(move |&p| self.elements.point_in_range(p)) @@ -310,7 +313,7 @@ impl<N: Idx> RegionValues<N> { } /// Returns just the universal regions that are contained in a given region's value. - crate fn universal_regions_outlived_by<'a>( + pub(crate) fn universal_regions_outlived_by<'a>( &'a self, r: N, ) -> impl Iterator<Item = RegionVid> + 'a { @@ -318,7 +321,7 @@ impl<N: Idx> RegionValues<N> { } /// Returns all the elements contained in a given region's value. - crate fn placeholders_contained_in<'a>( + pub(crate) fn placeholders_contained_in<'a>( &'a self, r: N, ) -> impl Iterator<Item = ty::PlaceholderRegion> + 'a { @@ -330,7 +333,10 @@ impl<N: Idx> RegionValues<N> { } /// Returns all the elements contained in a given region's value. - crate fn elements_contained_in<'a>(&'a self, r: N) -> impl Iterator<Item = RegionElement> + 'a { + pub(crate) fn elements_contained_in<'a>( + &'a self, + r: N, + ) -> impl Iterator<Item = RegionElement> + 'a { let points_iter = self.locations_outlived_by(r).map(RegionElement::Location); let free_regions_iter = @@ -343,12 +349,12 @@ impl<N: Idx> RegionValues<N> { } /// Returns a "pretty" string value of the region. Meant for debugging. - crate fn region_value_str(&self, r: N) -> String { + pub(crate) fn region_value_str(&self, r: N) -> String { region_value_str(self.elements_contained_in(r)) } } -crate trait ToElementIndex: Debug + Copy { +pub(crate) trait ToElementIndex: Debug + Copy { fn add_to_row<N: Idx>(self, values: &mut RegionValues<N>, row: N) -> bool; fn contained_in_row<N: Idx>(self, values: &RegionValues<N>, row: N) -> bool; @@ -388,7 +394,7 @@ impl ToElementIndex for ty::PlaceholderRegion { } } -crate fn location_set_str( +pub(crate) fn location_set_str( elements: &RegionValueElements, points: impl IntoIterator<Item = PointIndex>, ) -> String { diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index 21190a850b7..f11a94d7ddd 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -18,7 +18,7 @@ use crate::{ universal_regions::UniversalRegions, }; -crate struct ConstraintConversion<'a, 'tcx> { +pub(crate) struct ConstraintConversion<'a, 'tcx> { infcx: &'a InferCtxt<'a, 'tcx>, tcx: TyCtxt<'tcx>, universal_regions: &'a UniversalRegions<'tcx>, @@ -32,7 +32,7 @@ crate struct ConstraintConversion<'a, 'tcx> { } impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { - crate fn new( + pub(crate) fn new( infcx: &'a InferCtxt<'a, 'tcx>, universal_regions: &'a UniversalRegions<'tcx>, region_bound_pairs: &'a RegionBoundPairs<'tcx>, diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index f08f2e1b12d..670b5549afc 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -19,7 +19,7 @@ use crate::{ }; #[derive(Debug)] -crate struct UniversalRegionRelations<'tcx> { +pub(crate) struct UniversalRegionRelations<'tcx> { universal_regions: Rc<UniversalRegions<'tcx>>, /// Stores the outlives relations that are known to hold from the @@ -52,13 +52,13 @@ type RegionBoundPairs<'tcx> = Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>; /// then the output type as the last element. type NormalizedInputsAndOutput<'tcx> = Vec<Ty<'tcx>>; -crate struct CreateResult<'tcx> { - crate universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>, - crate region_bound_pairs: RegionBoundPairs<'tcx>, - crate normalized_inputs_and_output: NormalizedInputsAndOutput<'tcx>, +pub(crate) struct CreateResult<'tcx> { + pub(crate) universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>, + pub(crate) region_bound_pairs: RegionBoundPairs<'tcx>, + pub(crate) normalized_inputs_and_output: NormalizedInputsAndOutput<'tcx>, } -crate fn create<'tcx>( +pub(crate) fn create<'tcx>( infcx: &InferCtxt<'_, 'tcx>, param_env: ty::ParamEnv<'tcx>, implicit_region_bound: Option<ty::Region<'tcx>>, @@ -96,7 +96,7 @@ impl UniversalRegionRelations<'_> { /// /// (See `TransitiveRelation::postdom_upper_bound` for details on /// the postdominating upper bound in general.) - crate fn postdom_upper_bound(&self, fr1: RegionVid, fr2: RegionVid) -> RegionVid { + pub(crate) fn postdom_upper_bound(&self, fr1: RegionVid, fr2: RegionVid) -> RegionVid { assert!(self.universal_regions.is_universal_region(fr1)); assert!(self.universal_regions.is_universal_region(fr2)); self.inverse_outlives @@ -109,7 +109,7 @@ impl UniversalRegionRelations<'_> { /// outlives `fr` and (b) is not local. /// /// (*) If there are multiple competing choices, we return all of them. - crate fn non_local_upper_bounds<'a>(&'a self, fr: RegionVid) -> Vec<RegionVid> { + pub(crate) fn non_local_upper_bounds<'a>(&'a self, fr: RegionVid) -> Vec<RegionVid> { debug!("non_local_upper_bound(fr={:?})", fr); let res = self.non_local_bounds(&self.inverse_outlives, fr); assert!(!res.is_empty(), "can't find an upper bound!?"); @@ -118,7 +118,7 @@ impl UniversalRegionRelations<'_> { /// Returns the "postdominating" bound of the set of /// `non_local_upper_bounds` for the given region. - crate fn non_local_upper_bound(&self, fr: RegionVid) -> RegionVid { + pub(crate) fn non_local_upper_bound(&self, fr: RegionVid) -> RegionVid { let upper_bounds = self.non_local_upper_bounds(fr); // In case we find more than one, reduce to one for @@ -147,7 +147,7 @@ impl UniversalRegionRelations<'_> { /// /// (*) If there are multiple competing choices, we pick the "postdominating" /// one. See `TransitiveRelation::postdom_upper_bound` for details. - crate fn non_local_lower_bound(&self, fr: RegionVid) -> Option<RegionVid> { + pub(crate) fn non_local_lower_bound(&self, fr: RegionVid) -> Option<RegionVid> { debug!("non_local_lower_bound(fr={:?})", fr); let lower_bounds = self.non_local_bounds(&self.outlives, fr); @@ -203,18 +203,18 @@ impl UniversalRegionRelations<'_> { /// Returns `true` if fr1 is known to outlive fr2. /// /// This will only ever be true for universally quantified regions. - crate fn outlives(&self, fr1: RegionVid, fr2: RegionVid) -> bool { + pub(crate) fn outlives(&self, fr1: RegionVid, fr2: RegionVid) -> bool { self.outlives.contains(fr1, fr2) } /// Returns a vector of free regions `x` such that `fr1: x` is /// known to hold. - crate fn regions_outlived_by(&self, fr1: RegionVid) -> Vec<RegionVid> { + pub(crate) fn regions_outlived_by(&self, fr1: RegionVid) -> Vec<RegionVid> { self.outlives.reachable_from(fr1) } /// Returns the _non-transitive_ set of known `outlives` constraints between free regions. - crate fn known_outlives(&self) -> impl Iterator<Item = (RegionVid, RegionVid)> + '_ { + pub(crate) fn known_outlives(&self) -> impl Iterator<Item = (RegionVid, RegionVid)> + '_ { self.outlives.base_edges() } } @@ -232,7 +232,7 @@ struct UniversalRegionRelationsBuilder<'this, 'tcx> { } impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { - crate fn create(mut self) -> CreateResult<'tcx> { + pub(crate) fn create(mut self) -> CreateResult<'tcx> { let unnormalized_input_output_tys = self .universal_regions .unnormalized_input_tys diff --git a/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs b/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs index dd23683fae8..b88f6e689cc 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs @@ -18,7 +18,7 @@ use crate::region_infer::values::{PointIndex, RegionValueElements}; /// (and code simplicity) was favored. The rationale is that we only keep /// a small number of `IndexVec`s throughout the entire analysis while, in /// contrast, we're accessing each `Local` *many* times. -crate struct LocalUseMap { +pub(crate) struct LocalUseMap { /// Head of a linked list of **definitions** of each variable -- /// definition in this context means assignment, e.g., `x` is /// defined in `x = y` but not `y`; that first def is the head of @@ -58,7 +58,11 @@ impl vll::LinkElem for Appearance { } impl LocalUseMap { - crate fn build(live_locals: &[Local], elements: &RegionValueElements, body: &Body<'_>) -> Self { + pub(crate) fn build( + live_locals: &[Local], + elements: &RegionValueElements, + body: &Body<'_>, + ) -> Self { let nones = IndexVec::from_elem_n(None, body.local_decls.len()); let mut local_use_map = LocalUseMap { first_def_at: nones.clone(), @@ -81,17 +85,17 @@ impl LocalUseMap { local_use_map } - crate fn defs(&self, local: Local) -> impl Iterator<Item = PointIndex> + '_ { + pub(crate) fn defs(&self, local: Local) -> impl Iterator<Item = PointIndex> + '_ { vll::iter(self.first_def_at[local], &self.appearances) .map(move |aa| self.appearances[aa].point_index) } - crate fn uses(&self, local: Local) -> impl Iterator<Item = PointIndex> + '_ { + pub(crate) fn uses(&self, local: Local) -> impl Iterator<Item = PointIndex> + '_ { vll::iter(self.first_use_at[local], &self.appearances) .map(move |aa| self.appearances[aa].point_index) } - crate fn drops(&self, local: Local) -> impl Iterator<Item = PointIndex> + '_ { + pub(crate) fn drops(&self, local: Local) -> impl Iterator<Item = PointIndex> + '_ { vll::iter(self.first_drop_at[local], &self.appearances) .map(move |aa| self.appearances[aa].point_index) } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index c4a190b44cb..405fd9198d3 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -94,7 +94,7 @@ mod canonical; mod constraint_conversion; pub mod free_region_relations; mod input_output; -crate mod liveness; +pub(crate) mod liveness; mod relate_tys; /// Type checks the given `mir` in the context of the inference @@ -897,28 +897,29 @@ struct BorrowCheckContext<'a, 'tcx> { upvars: &'a [Upvar<'tcx>], } -crate struct MirTypeckResults<'tcx> { - crate constraints: MirTypeckRegionConstraints<'tcx>, - crate universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>, - crate opaque_type_values: VecMap<OpaqueTypeKey<'tcx>, (OpaqueHiddenType<'tcx>, OpaqueTyOrigin)>, +pub(crate) struct MirTypeckResults<'tcx> { + pub(crate) constraints: MirTypeckRegionConstraints<'tcx>, + pub(crate) universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>, + pub(crate) opaque_type_values: + VecMap<OpaqueTypeKey<'tcx>, (OpaqueHiddenType<'tcx>, OpaqueTyOrigin)>, } /// A collection of region constraints that must be satisfied for the /// program to be considered well-typed. -crate struct MirTypeckRegionConstraints<'tcx> { +pub(crate) struct MirTypeckRegionConstraints<'tcx> { /// Maps from a `ty::Placeholder` to the corresponding /// `PlaceholderIndex` bit that we will use for it. /// /// To keep everything in sync, do not insert this set /// directly. Instead, use the `placeholder_region` helper. - crate placeholder_indices: PlaceholderIndices, + pub(crate) placeholder_indices: PlaceholderIndices, /// Each time we add a placeholder to `placeholder_indices`, we /// also create a corresponding "representative" region vid for /// that wraps it. This vector tracks those. This way, when we /// convert the same `ty::RePlaceholder(p)` twice, we can map to /// the same underlying `RegionVid`. - crate placeholder_index_to_region: IndexVec<PlaceholderIndex, ty::Region<'tcx>>, + pub(crate) placeholder_index_to_region: IndexVec<PlaceholderIndex, ty::Region<'tcx>>, /// In general, the type-checker is not responsible for enforcing /// liveness constraints; this job falls to the region inferencer, @@ -927,18 +928,18 @@ crate struct MirTypeckRegionConstraints<'tcx> { /// not otherwise appear in the MIR -- in particular, the /// late-bound regions that it instantiates at call-sites -- and /// hence it must report on their liveness constraints. - crate liveness_constraints: LivenessValues<RegionVid>, + pub(crate) liveness_constraints: LivenessValues<RegionVid>, - crate outlives_constraints: OutlivesConstraintSet<'tcx>, + pub(crate) outlives_constraints: OutlivesConstraintSet<'tcx>, - crate member_constraints: MemberConstraintSet<'tcx, RegionVid>, + pub(crate) member_constraints: MemberConstraintSet<'tcx, RegionVid>, - crate closure_bounds_mapping: + pub(crate) closure_bounds_mapping: FxHashMap<Location, FxHashMap<(RegionVid, RegionVid), (ConstraintCategory, Span)>>, - crate universe_causes: FxHashMap<ty::UniverseIndex, UniverseInfo<'tcx>>, + pub(crate) universe_causes: FxHashMap<ty::UniverseIndex, UniverseInfo<'tcx>>, - crate type_tests: Vec<TypeTest<'tcx>>, + pub(crate) type_tests: Vec<TypeTest<'tcx>>, } impl<'tcx> MirTypeckRegionConstraints<'tcx> { @@ -971,7 +972,7 @@ pub enum Locations { /// things like the type of the return slot. Consider this /// example: /// - /// ``` + /// ```compile_fail,E0515 /// fn foo<'a>(x: &'a u32) -> &'a u32 { /// let y = 22; /// return &y; // error diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index e26adba0d30..7b63ec516b8 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -187,12 +187,12 @@ pub enum RegionClassification { /// /// Consider this example: /// - /// ``` + /// ```ignore (pseudo-rust) /// fn foo<'a, 'b>(a: &'a u32, b: &'b u32, c: &'static u32) { /// let closure = for<'x> |x: &'x u32| { .. }; - /// ^^^^^^^ pretend this were legal syntax - /// for declaring a late-bound region in - /// a closure signature + /// // ^^^^^^^ pretend this were legal syntax + /// // for declaring a late-bound region in + /// // a closure signature /// } /// ``` /// @@ -336,7 +336,7 @@ impl<'tcx> UniversalRegions<'tcx> { /// that this region imposes on others. The methods in this file /// handle the part about dumping the inference context internal /// state. - crate fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut Diagnostic) { + pub(crate) fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut Diagnostic) { match self.defining_ty { DefiningTy::Closure(def_id, substs) => { err.note(&format!( @@ -477,8 +477,11 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { .infcx .tcx .mk_region(ty::ReVar(self.infcx.next_nll_region_var(FR).to_region_vid())); - let va_list_ty = - self.infcx.tcx.type_of(va_list_did).subst(self.infcx.tcx, &[region.into()]); + let va_list_ty = self + .infcx + .tcx + .bound_type_of(va_list_did) + .subst(self.infcx.tcx, &[region.into()]); unnormalized_input_tys = self.infcx.tcx.mk_type_list( unnormalized_input_tys.iter().copied().chain(iter::once(va_list_ty)), diff --git a/compiler/rustc_borrowck/src/used_muts.rs b/compiler/rustc_borrowck/src/used_muts.rs index 6022a980950..cdbb60b878a 100644 --- a/compiler/rustc_borrowck/src/used_muts.rs +++ b/compiler/rustc_borrowck/src/used_muts.rs @@ -22,7 +22,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { /// been assigned to - this set is used as a proxy for locals that were not initialized due to /// unreachable code. These locals are then considered "used" to silence the lint for them. /// See #55344 for context. - crate fn gather_used_muts( + pub(crate) fn gather_used_muts( &mut self, temporary_used_locals: FxHashSet<Local>, mut never_initialized_mut_locals: FxHashSet<Local>, diff --git a/compiler/rustc_builtin_macros/src/cfg_accessible.rs b/compiler/rustc_builtin_macros/src/cfg_accessible.rs index 7b7db3eaea6..cb5359dd1e2 100644 --- a/compiler/rustc_builtin_macros/src/cfg_accessible.rs +++ b/compiler/rustc_builtin_macros/src/cfg_accessible.rs @@ -7,7 +7,7 @@ use rustc_parse::validate_attr; use rustc_span::symbol::sym; use rustc_span::Span; -crate struct Expander; +pub(crate) struct Expander; fn validate_input<'a>(ecx: &mut ExtCtxt<'_>, mi: &'a ast::MetaItem) -> Option<&'a ast::Path> { match mi.meta_item_list() { diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index 7637bf7edc8..89b2c329236 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -3,23 +3,21 @@ use crate::util::{check_builtin_macro_attribute, warn_on_duplicate_attribute}; use rustc_ast as ast; use rustc_ast::mut_visit::MutVisitor; use rustc_ast::ptr::P; -use rustc_ast::tokenstream::CanSynthesizeMissingTokens; use rustc_ast::visit::Visitor; use rustc_ast::NodeId; use rustc_ast::{mut_visit, visit}; -use rustc_ast::{AstLike, Attribute}; +use rustc_ast::{Attribute, HasAttrs, HasTokens}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_expand::config::StripUnconfigured; use rustc_expand::configure; use rustc_feature::Features; use rustc_parse::parser::{ForceCollect, Parser}; -use rustc_session::utils::FlattenNonterminals; use rustc_session::Session; use rustc_span::symbol::sym; use rustc_span::Span; use smallvec::SmallVec; -crate fn expand( +pub(crate) fn expand( ecx: &mut ExtCtxt<'_>, _span: Span, meta_item: &ast::MetaItem, @@ -30,7 +28,7 @@ crate fn expand( vec![cfg_eval(ecx.sess, ecx.ecfg.features, annotatable, ecx.current_expansion.lint_node_id)] } -crate fn cfg_eval( +pub(crate) fn cfg_eval( sess: &Session, features: Option<&Features>, annotatable: Annotatable, @@ -125,7 +123,7 @@ impl<'ast> visit::Visitor<'ast> for CfgFinder { } impl CfgEval<'_, '_> { - fn configure<T: AstLike>(&mut self, node: T) -> Option<T> { + fn configure<T: HasAttrs + HasTokens>(&mut self, node: T) -> Option<T> { self.cfg.configure(node) } @@ -173,13 +171,6 @@ impl CfgEval<'_, '_> { } _ => unreachable!(), }; - let nt = annotatable.into_nonterminal(); - - let mut orig_tokens = rustc_parse::nt_to_tokenstream( - &nt, - &self.cfg.sess.parse_sess, - CanSynthesizeMissingTokens::No, - ); // 'Flatten' all nonterminals (i.e. `TokenKind::Interpolated`) // to `None`-delimited groups containing the corresponding tokens. This @@ -194,12 +185,7 @@ impl CfgEval<'_, '_> { // where `$item` is `#[cfg_attr] struct Foo {}`. We want to make // sure to evaluate *all* `#[cfg]` and `#[cfg_attr]` attributes - the simplest // way to do this is to do a single parse of a stream without any nonterminals. - let mut flatten = FlattenNonterminals { - nt_to_tokenstream: rustc_parse::nt_to_tokenstream, - parse_sess: &self.cfg.sess.parse_sess, - synthesize_tokens: CanSynthesizeMissingTokens::No, - }; - orig_tokens = flatten.process_token_stream(orig_tokens); + let orig_tokens = annotatable.to_tokens().flattened(); // Re-parse the tokens, setting the `capture_cfg` flag to save extra information // to the captured `AttrAnnotatedTokenStream` (specifically, we capture diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs index 391c46d1813..7f25b23734b 100644 --- a/compiler/rustc_builtin_macros/src/derive.rs +++ b/compiler/rustc_builtin_macros/src/derive.rs @@ -10,7 +10,7 @@ use rustc_session::Session; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; -crate struct Expander; +pub(crate) struct Expander; impl MultiItemModifier for Expander { fn expand( diff --git a/compiler/rustc_builtin_macros/src/deriving/encodable.rs b/compiler/rustc_builtin_macros/src/deriving/encodable.rs index c5f3a9d3379..6151a80a56d 100644 --- a/compiler/rustc_builtin_macros/src/deriving/encodable.rs +++ b/compiler/rustc_builtin_macros/src/deriving/encodable.rs @@ -5,14 +5,14 @@ //! //! For example, a type like: //! -//! ``` +//! ```ignore (old code) //! #[derive(RustcEncodable, RustcDecodable)] //! struct Node { id: usize } //! ``` //! //! would generate two implementations like: //! -//! ``` +//! ```ignore (old code) //! # struct Node { id: usize } //! impl<S: Encoder<E>, E> Encodable<S, E> for Node { //! fn encode(&self, s: &mut S) -> Result<(), E> { @@ -40,7 +40,7 @@ //! Other interesting scenarios are when the item has type parameters or //! references other non-built-in types. A type definition like: //! -//! ``` +//! ```ignore (old code) //! # #[derive(RustcEncodable, RustcDecodable)] //! # struct Span; //! #[derive(RustcEncodable, RustcDecodable)] @@ -49,7 +49,7 @@ //! //! would yield functions like: //! -//! ``` +//! ```ignore (old code) //! # #[derive(RustcEncodable, RustcDecodable)] //! # struct Span; //! # struct Spanned<T> { node: T, span: Span } diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index f87f4726d1c..0fd23fd281e 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -1006,9 +1006,11 @@ impl<'a> MethodDef<'a> { /// } /// } /// } - /// - /// // or if A is repr(packed) - note fields are matched by-value - /// // instead of by-reference. + /// ``` + /// or if A is repr(packed) - note fields are matched by-value + /// instead of by-reference. + /// ``` + /// # struct A { x: i32, y: i32 } /// impl PartialEq for A { /// fn eq(&self, other: &A) -> bool { /// match *self { @@ -1126,14 +1128,15 @@ impl<'a> MethodDef<'a> { /// // is equivalent to /// /// impl PartialEq for A { - /// fn eq(&self, other: &A) -> ::bool { + /// fn eq(&self, other: &A) -> bool { + /// use A::*; /// match (&*self, &*other) { /// (&A1, &A1) => true, /// (&A2(ref self_0), /// &A2(ref __arg_1_0)) => (*self_0).eq(&(*__arg_1_0)), /// _ => { - /// let __self_vi = match *self { A1(..) => 0, A2(..) => 1 }; - /// let __arg_1_vi = match *other { A1(..) => 0, A2(..) => 1 }; + /// let __self_vi = match *self { A1 => 0, A2(..) => 1 }; + /// let __arg_1_vi = match *other { A1 => 0, A2(..) => 1 }; /// false /// } /// } diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs index 812d86af6e8..c678c8cbd15 100644 --- a/compiler/rustc_builtin_macros/src/deriving/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -38,8 +38,8 @@ pub mod partial_ord; pub mod generic; -crate struct BuiltinDerive( - crate fn(&mut ExtCtxt<'_>, Span, &MetaItem, &Annotatable, &mut dyn FnMut(Annotatable)), +pub(crate) struct BuiltinDerive( + pub(crate) fn(&mut ExtCtxt<'_>, Span, &MetaItem, &Annotatable, &mut dyn FnMut(Annotatable)), ); impl MultiItemModifier for BuiltinDerive { diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 468ca7d7aa9..0c9e3c22bcf 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -4,7 +4,6 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(array_windows)] #![feature(box_patterns)] -#![feature(crate_visibility_modifier)] #![feature(decl_macro)] #![feature(is_sorted)] #![feature(nll)] diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index e2553ab40ca..db8dce804a3 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -249,7 +249,7 @@ fn generate_test_harness( /// /// By default this expands to /// -/// ``` +/// ```ignore UNSOLVED (I think I still need guidance for this one. Is it correct? Do we try to make it run? How do we nicely fill it out?) /// #[rustc_main] /// pub fn main() { /// extern crate test; diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml index 3aba528abfd..aa556a21bf8 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml @@ -10,7 +10,7 @@ jobs: timeout-minutes: 10 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install rustfmt run: | @@ -39,7 +39,7 @@ jobs: TARGET_TRIPLE: aarch64-unknown-linux-gnu steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Cache cargo installed crates uses: actions/cache@v2 @@ -127,7 +127,7 @@ jobs: timeout-minutes: 60 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 #- name: Cache cargo installed crates # uses: actions/cache@v2 diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/nightly-cranelift.yml b/compiler/rustc_codegen_cranelift/.github/workflows/nightly-cranelift.yml index a019793edd8..0a3e7ca073b 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/nightly-cranelift.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/nightly-cranelift.yml @@ -11,7 +11,7 @@ jobs: timeout-minutes: 60 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Cache cargo installed crates uses: actions/cache@v2 @@ -34,7 +34,7 @@ jobs: sed -i 's/cranelift-jit = { version = "\w*.\w*.\w*", optional = true }/cranelift-jit = { git = "https:\/\/github.com\/bytecodealliance\/wasmtime.git", optional = true }/' Cargo.toml sed -i 's/cranelift-object = "\w*.\w*.\w*"/cranelift-object = { git = "https:\/\/github.com\/bytecodealliance\/wasmtime.git" }/' Cargo.toml - sed -i 's/gimli = { version = "0.25.0", default-features = false, features = \["write"\]}/gimli = { version = "0.26.1", default-features = false, features = ["write"] }/' Cargo.toml + sed -i 's/object = { version = "0.27.0"/object = { version = "0.28.0"/' Cargo.toml cat Cargo.toml diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml b/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml index 1c08e5ece33..b8a98b83ebe 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Cache cargo installed crates uses: actions/cache@v2 @@ -46,7 +46,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Cache cargo installed crates uses: actions/cache@v2 diff --git a/compiler/rustc_codegen_cranelift/.vscode/settings.json b/compiler/rustc_codegen_cranelift/.vscode/settings.json index 74fde9c27c0..ecb20f22d8c 100644 --- a/compiler/rustc_codegen_cranelift/.vscode/settings.json +++ b/compiler/rustc_codegen_cranelift/.vscode/settings.json @@ -5,7 +5,7 @@ "rust-analyzer.assist.importEnforceGranularity": true, "rust-analyzer.assist.importPrefix": "crate", "rust-analyzer.cargo.runBuildScripts": true, - "rust-analyzer.cargo.features": ["unstable-features"] + "rust-analyzer.cargo.features": ["unstable-features"], "rust-analyzer.linkedProjects": [ "./Cargo.toml", //"./build_sysroot/sysroot_src/src/libstd/Cargo.toml", diff --git a/compiler/rustc_codegen_cranelift/Cargo.toml b/compiler/rustc_codegen_cranelift/Cargo.toml index 74f50808a98..18d7f41cf40 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.toml +++ b/compiler/rustc_codegen_cranelift/Cargo.toml @@ -41,15 +41,5 @@ unstable-features = ["jit", "inline_asm"] jit = ["cranelift-jit", "libloading"] inline_asm = [] -# Disable optimizations and debuginfo of build scripts and some of the heavy build deps, as the -# execution time of build scripts is so fast that optimizing them slows down the total build time. -[profile.release.build-override] -opt-level = 0 -debug = false - -[profile.release.package.cranelift-codegen-meta] -opt-level = 0 -debug = false - [package.metadata.rust-analyzer] rustc_private = true diff --git a/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock b/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock index 51ba0dbfcc7..efee6ef3f37 100644 --- a/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock +++ b/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock @@ -112,9 +112,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c21d40587b92fa6a6c6e3c1bdbf87d75511db5672f9c93175574b3a00df1758" +checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" dependencies = [ "compiler_builtins", "rustc-std-workspace-alloc", @@ -134,18 +134,18 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.124" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a41fed9d98f27ab1c6d161da622a4fa35e8a54a8adc24bbf3ddd0ef70b0e50" +checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" dependencies = [ "rustc-std-workspace-core", ] [[package]] name = "memchr" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" dependencies = [ "compiler_builtins", "rustc-std-workspace-core", diff --git a/compiler/rustc_codegen_cranelift/build_system/build_backend.rs b/compiler/rustc_codegen_cranelift/build_system/build_backend.rs index 0a56eb131ed..48faec8bc4b 100644 --- a/compiler/rustc_codegen_cranelift/build_system/build_backend.rs +++ b/compiler/rustc_codegen_cranelift/build_system/build_backend.rs @@ -34,18 +34,6 @@ pub(crate) fn build_backend( _ => unreachable!(), } - // Set the rpath to make the cg_clif executable find librustc_codegen_cranelift without changing - // LD_LIBRARY_PATH - if cfg!(unix) { - if cfg!(target_os = "macos") { - rustflags += " -Csplit-debuginfo=unpacked \ - -Clink-arg=-Wl,-rpath,@loader_path/../lib \ - -Zosx-rpath-install-name"; - } else { - rustflags += " -Clink-arg=-Wl,-rpath=$ORIGIN/../lib "; - } - } - cmd.env("RUSTFLAGS", rustflags); eprintln!("[BUILD] rustc_codegen_cranelift"); diff --git a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs index c9c003d4610..8682204f4fd 100644 --- a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs +++ b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs @@ -1,4 +1,3 @@ -use std::env; use std::fs; use std::path::{Path, PathBuf}; use std::process::{self, Command}; @@ -22,35 +21,28 @@ pub(crate) fn build_sysroot( fs::create_dir_all(target_dir.join("lib")).unwrap(); // Copy the backend - for file in ["cg_clif", "cg_clif_build_sysroot"] { - try_hard_link( - cg_clif_build_dir.join(get_file_name(file, "bin")), - target_dir.join("bin").join(get_file_name(file, "bin")), - ); - } - let cg_clif_dylib = get_file_name("rustc_codegen_cranelift", "dylib"); - try_hard_link( - cg_clif_build_dir.join(&cg_clif_dylib), - target_dir - .join(if cfg!(windows) { - // Windows doesn't have rpath support, so the cg_clif dylib needs to be next to the - // binaries. - "bin" - } else { - "lib" - }) - .join(cg_clif_dylib), - ); - - // Build and copy cargo wrapper - let mut build_cargo_wrapper_cmd = Command::new("rustc"); - build_cargo_wrapper_cmd - .arg("scripts/cargo-clif.rs") - .arg("-o") - .arg(target_dir.join("cargo-clif")) - .arg("-g"); - spawn_and_wait(build_cargo_wrapper_cmd); + let cg_clif_dylib_path = target_dir + .join(if cfg!(windows) { + // Windows doesn't have rpath support, so the cg_clif dylib needs to be next to the + // binaries. + "bin" + } else { + "lib" + }) + .join(&cg_clif_dylib); + try_hard_link(cg_clif_build_dir.join(cg_clif_dylib), &cg_clif_dylib_path); + + // Build and copy rustc and cargo wrappers + for wrapper in ["rustc-clif", "cargo-clif"] { + let mut build_cargo_wrapper_cmd = Command::new("rustc"); + build_cargo_wrapper_cmd + .arg(PathBuf::from("scripts").join(format!("{wrapper}.rs"))) + .arg("-o") + .arg(target_dir.join(wrapper)) + .arg("-g"); + spawn_and_wait(build_cargo_wrapper_cmd); + } let default_sysroot = super::rustc_info::get_default_sysroot(); @@ -117,7 +109,13 @@ pub(crate) fn build_sysroot( } } SysrootKind::Clif => { - build_clif_sysroot_for_triple(channel, target_dir, host_triple, None); + build_clif_sysroot_for_triple( + channel, + target_dir, + host_triple, + &cg_clif_dylib_path, + None, + ); if host_triple != target_triple { // When cross-compiling it is often necessary to manually pick the right linker @@ -126,14 +124,21 @@ pub(crate) fn build_sysroot( } else { None }; - build_clif_sysroot_for_triple(channel, target_dir, target_triple, linker); + build_clif_sysroot_for_triple( + channel, + target_dir, + target_triple, + &cg_clif_dylib_path, + linker, + ); } // Copy std for the host to the lib dir. This is necessary for the jit mode to find // libstd. for file in fs::read_dir(host_rustlib_lib).unwrap() { let file = file.unwrap().path(); - if file.file_name().unwrap().to_str().unwrap().contains("std-") { + let filename = file.file_name().unwrap().to_str().unwrap(); + if filename.contains("std-") && !filename.contains(".rlib") { try_hard_link(&file, target_dir.join("lib").join(file.file_name().unwrap())); } } @@ -145,6 +150,7 @@ fn build_clif_sysroot_for_triple( channel: &str, target_dir: &Path, triple: &str, + cg_clif_dylib_path: &Path, linker: Option<&str>, ) { match fs::read_to_string(Path::new("build_sysroot").join("rustc_version")) { @@ -168,18 +174,18 @@ fn build_clif_sysroot_for_triple( let build_dir = Path::new("build_sysroot").join("target").join(triple).join(channel); if !super::config::get_bool("keep_sysroot") { - // Cleanup the target dir with the exception of build scripts and the incremental cache - for dir in ["build", "deps", "examples", "native"] { - if build_dir.join(dir).exists() { - fs::remove_dir_all(build_dir.join(dir)).unwrap(); - } + // Cleanup the deps dir, but keep build scripts and the incremental cache for faster + // recompilation as they are not affected by changes in cg_clif. + if build_dir.join("deps").exists() { + fs::remove_dir_all(build_dir.join("deps")).unwrap(); } } // Build sysroot let mut build_cmd = Command::new("cargo"); build_cmd.arg("build").arg("--target").arg(triple).current_dir("build_sysroot"); - let mut rustflags = "--clif -Zforce-unstable-if-unmarked".to_string(); + let mut rustflags = "-Zforce-unstable-if-unmarked -Cpanic=abort".to_string(); + rustflags.push_str(&format!(" -Zcodegen-backend={}", cg_clif_dylib_path.to_str().unwrap())); if channel == "release" { build_cmd.arg("--release"); rustflags.push_str(" -Zmir-opt-level=3"); @@ -189,10 +195,6 @@ fn build_clif_sysroot_for_triple( write!(rustflags, " -Clinker={}", linker).unwrap(); } build_cmd.env("RUSTFLAGS", rustflags); - build_cmd.env( - "RUSTC", - env::current_dir().unwrap().join(target_dir).join("bin").join("cg_clif_build_sysroot"), - ); build_cmd.env("__CARGO_DEFAULT_LIB_METADATA", "cg_clif"); spawn_and_wait(build_cmd); diff --git a/compiler/rustc_codegen_cranelift/build_system/mod.rs b/compiler/rustc_codegen_cranelift/build_system/mod.rs index b228da3981f..b897b7fbacf 100644 --- a/compiler/rustc_codegen_cranelift/build_system/mod.rs +++ b/compiler/rustc_codegen_cranelift/build_system/mod.rs @@ -86,6 +86,7 @@ pub fn main() { arg => arg_error!("Unexpected argument {}", arg), } } + target_dir = std::env::current_dir().unwrap().join(target_dir); let host_triple = if let Ok(host_triple) = std::env::var("HOST_TRIPLE") { host_triple diff --git a/compiler/rustc_codegen_cranelift/docs/usage.md b/compiler/rustc_codegen_cranelift/docs/usage.md index 785c7383783..33f146e7ba2 100644 --- a/compiler/rustc_codegen_cranelift/docs/usage.md +++ b/compiler/rustc_codegen_cranelift/docs/usage.md @@ -19,7 +19,7 @@ This will build your project with rustc_codegen_cranelift instead of the usual L > You should prefer using the Cargo method. ```bash -$ $cg_clif_dir/build/bin/cg_clif my_crate.rs +$ $cg_clif_dir/build/rustc-clif my_crate.rs ``` ## Jit mode @@ -38,7 +38,7 @@ $ $cg_clif_dir/build/cargo-clif jit or ```bash -$ $cg_clif_dir/build/bin/cg_clif -Zunstable-features -Cllvm-args=mode=jit -Cprefer-dynamic my_crate.rs +$ $cg_clif_dir/build/rustc-clif -Zunstable-features -Cllvm-args=mode=jit -Cprefer-dynamic my_crate.rs ``` There is also an experimental lazy jit mode. In this mode functions are only compiled once they are @@ -54,7 +54,7 @@ These are a few functions that allow you to easily run rust code from the shell ```bash function jit_naked() { - echo "$@" | $cg_clif_dir/build/bin/cg_clif - -Zunstable-features -Cllvm-args=mode=jit -Cprefer-dynamic + echo "$@" | $cg_clif_dir/build/rustc-clif - -Zunstable-features -Cllvm-args=mode=jit -Cprefer-dynamic } function jit() { diff --git a/compiler/rustc_codegen_cranelift/patches/0027-sysroot-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-sysroot-128bit-atomic-operations.patch index 8e6652af374..ce1c6c99b40 100644 --- a/compiler/rustc_codegen_cranelift/patches/0027-sysroot-128bit-atomic-operations.patch +++ b/compiler/rustc_codegen_cranelift/patches/0027-sysroot-128bit-atomic-operations.patch @@ -21,7 +21,7 @@ index 092b7cf..158cf71 100644 -#[cfg(target_has_atomic_load_store = "128")] -#[unstable(feature = "integer_atomics", issue = "32976")] -impl RefUnwindSafe for crate::sync::atomic::AtomicI128 {} - + #[cfg(target_has_atomic_load_store = "ptr")] #[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] @@ -235,9 +232,6 @@ impl RefUnwindSafe for crate::sync::atomic::AtomicU32 {} @@ -31,14 +31,14 @@ index 092b7cf..158cf71 100644 -#[cfg(target_has_atomic_load_store = "128")] -#[unstable(feature = "integer_atomics", issue = "32976")] -impl RefUnwindSafe for crate::sync::atomic::AtomicU128 {} - + #[cfg(target_has_atomic_load_store = "8")] #[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index d9de37e..8293fce 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs -@@ -2234,44 +2234,6 @@ atomic_int! { +@@ -2234,46 +2234,6 @@ atomic_int! { "AtomicU64::new(0)", u64 AtomicU64 ATOMIC_U64_INIT } @@ -54,6 +54,7 @@ index d9de37e..8293fce 100644 - unstable(feature = "integer_atomics", issue = "32976"), - rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), - unstable(feature = "integer_atomics", issue = "32976"), +- cfg_attr(not(test), rustc_diagnostic_item = "AtomicI128"), - "i128", - "#![feature(integer_atomics)]\n\n", - atomic_min, atomic_max, @@ -73,6 +74,7 @@ index d9de37e..8293fce 100644 - unstable(feature = "integer_atomics", issue = "32976"), - rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), - unstable(feature = "integer_atomics", issue = "32976"), +- cfg_attr(not(test), rustc_diagnostic_item = "AtomicU128"), - "u128", - "#![feature(integer_atomics)]\n\n", - atomic_umin, atomic_umax, @@ -98,6 +100,6 @@ index b735957..ea728b6 100644 #[cfg(target_has_atomic = "ptr")] assert_eq!(align_of::<AtomicUsize>(), size_of::<AtomicUsize>()); #[cfg(target_has_atomic = "ptr")] --- +-- 2.26.2.7.g19db9cfb68 diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain index 966097c248b..e98e92e468e 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain +++ b/compiler/rustc_codegen_cranelift/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-04-21" +channel = "nightly-2022-05-15" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] diff --git a/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs b/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs index 41d82b581cd..9362b47fa6d 100644 --- a/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs +++ b/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs @@ -5,20 +5,11 @@ use std::path::PathBuf; use std::process::Command; fn main() { - if env::var("RUSTC_WRAPPER").map_or(false, |wrapper| wrapper.contains("sccache")) { - eprintln!( - "\x1b[1;93m=== Warning: Unsetting RUSTC_WRAPPER to prevent interference with sccache ===\x1b[0m" - ); - env::remove_var("RUSTC_WRAPPER"); - } - let sysroot = PathBuf::from(env::current_exe().unwrap().parent().unwrap()); - env::set_var("RUSTC", sysroot.join("bin/cg_clif".to_string() + env::consts::EXE_SUFFIX)); - - let mut rustdoc_flags = env::var("RUSTDOCFLAGS").unwrap_or(String::new()); - rustdoc_flags.push_str(" -Cpanic=abort -Zpanic-abort-tests -Zcodegen-backend="); - rustdoc_flags.push_str( + let mut rustflags = String::new(); + rustflags.push_str(" -Cpanic=abort -Zpanic-abort-tests -Zcodegen-backend="); + rustflags.push_str( sysroot .join(if cfg!(windows) { "bin" } else { "lib" }) .join( @@ -29,9 +20,10 @@ fn main() { .to_str() .unwrap(), ); - rustdoc_flags.push_str(" --sysroot "); - rustdoc_flags.push_str(sysroot.to_str().unwrap()); - env::set_var("RUSTDOCFLAGS", rustdoc_flags); + rustflags.push_str(" --sysroot "); + rustflags.push_str(sysroot.to_str().unwrap()); + env::set_var("RUSTFLAGS", env::var("RUSTFLAGS").unwrap_or(String::new()) + &rustflags); + env::set_var("RUSTDOCFLAGS", env::var("RUSTDOCFLAGS").unwrap_or(String::new()) + &rustflags); // Ensure that the right toolchain is used env::set_var("RUSTUP_TOOLCHAIN", env!("RUSTUP_TOOLCHAIN")); @@ -46,7 +38,7 @@ fn main() { .chain(env::args().skip(2)) .chain([ "--".to_string(), - "-Zunstable-features".to_string(), + "-Zunstable-options".to_string(), "-Cllvm-args=mode=jit".to_string(), ]) .collect() @@ -60,7 +52,7 @@ fn main() { .chain(env::args().skip(2)) .chain([ "--".to_string(), - "-Zunstable-features".to_string(), + "-Zunstable-options".to_string(), "-Cllvm-args=mode=jit-lazy".to_string(), ]) .collect() diff --git a/compiler/rustc_codegen_cranelift/scripts/config.sh b/compiler/rustc_codegen_cranelift/scripts/config.sh deleted file mode 100644 index 53ada369b08..00000000000 --- a/compiler/rustc_codegen_cranelift/scripts/config.sh +++ /dev/null @@ -1,6 +0,0 @@ -# Note to people running shellcheck: this file should only be sourced, not executed directly. - -set -e - -export LD_LIBRARY_PATH="$(rustc --print sysroot)/lib:$LD_LIBRARY_PATH" -export DYLD_LIBRARY_PATH="$(rustc --print sysroot)/lib:$DYLD_LIBRARY_PATH" diff --git a/compiler/rustc_codegen_cranelift/scripts/ext_config.sh b/compiler/rustc_codegen_cranelift/scripts/ext_config.sh deleted file mode 100644 index 11d6c4c8318..00000000000 --- a/compiler/rustc_codegen_cranelift/scripts/ext_config.sh +++ /dev/null @@ -1,32 +0,0 @@ -# Note to people running shellcheck: this file should only be sourced, not executed directly. - -# Various env vars that should only be set for the build system - -set -e - -export CG_CLIF_DISPLAY_CG_TIME=1 -export CG_CLIF_DISABLE_INCR_CACHE=1 - -export HOST_TRIPLE=$(rustc -vV | grep host | cut -d: -f2 | tr -d " ") -export TARGET_TRIPLE=${TARGET_TRIPLE:-$HOST_TRIPLE} - -export RUN_WRAPPER='' -export JIT_SUPPORTED=1 -if [[ "$HOST_TRIPLE" != "$TARGET_TRIPLE" ]]; then - export JIT_SUPPORTED=0 - if [[ "$TARGET_TRIPLE" == "aarch64-unknown-linux-gnu" ]]; then - # We are cross-compiling for aarch64. Use the correct linker and run tests in qemu. - export RUSTFLAGS='-Clinker=aarch64-linux-gnu-gcc '$RUSTFLAGS - export RUN_WRAPPER='qemu-aarch64 -L /usr/aarch64-linux-gnu' - elif [[ "$TARGET_TRIPLE" == "x86_64-pc-windows-gnu" ]]; then - # We are cross-compiling for Windows. Run tests in wine. - export RUN_WRAPPER='wine' - else - echo "Unknown non-native platform" - fi -fi - -# FIXME fix `#[linkage = "extern_weak"]` without this -if [[ "$(uname)" == 'Darwin' ]]; then - export RUSTFLAGS="$RUSTFLAGS -Clink-arg=-undefined -Clink-arg=dynamic_lookup" -fi diff --git a/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs b/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs index f4e863e5494..e6f60d1c0cb 100755 --- a/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs +++ b/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs @@ -2,8 +2,7 @@ #![forbid(unsafe_code)]/* This line is ignored by bash # This block is ignored by rustc pushd $(dirname "$0")/../ -source scripts/config.sh -RUSTC="$(pwd)/build/bin/cg_clif" +RUSTC="$(pwd)/build/rustc-clif" popd PROFILE=$1 OUTPUT=$2 exec $RUSTC -Zunstable-options -Cllvm-args=mode=jit -Cprefer-dynamic $0 #*/ diff --git a/compiler/rustc_codegen_cranelift/scripts/rustc-clif.rs b/compiler/rustc_codegen_cranelift/scripts/rustc-clif.rs new file mode 100644 index 00000000000..3abfcd8ddc8 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/scripts/rustc-clif.rs @@ -0,0 +1,36 @@ +use std::env; +use std::ffi::OsString; +#[cfg(unix)] +use std::os::unix::process::CommandExt; +use std::path::PathBuf; +use std::process::Command; + +fn main() { + let sysroot = PathBuf::from(env::current_exe().unwrap().parent().unwrap()); + + let cg_clif_dylib_path = sysroot.join(if cfg!(windows) { "bin" } else { "lib" }).join( + env::consts::DLL_PREFIX.to_string() + "rustc_codegen_cranelift" + env::consts::DLL_SUFFIX, + ); + + let mut args = std::env::args_os().skip(1).collect::<Vec<_>>(); + args.push(OsString::from("-Cpanic=abort")); + args.push(OsString::from("-Zpanic-abort-tests")); + let mut codegen_backend_arg = OsString::from("-Zcodegen-backend="); + codegen_backend_arg.push(cg_clif_dylib_path); + args.push(codegen_backend_arg); + if !args.contains(&OsString::from("--sysroot")) { + args.push(OsString::from("--sysroot")); + args.push(OsString::from(sysroot.to_str().unwrap())); + } + + // Ensure that the right toolchain is used + env::set_var("RUSTUP_TOOLCHAIN", env!("RUSTUP_TOOLCHAIN")); + + #[cfg(unix)] + Command::new("rustc").args(args).exec(); + + #[cfg(not(unix))] + std::process::exit( + Command::new("rustc").args(args).spawn().unwrap().wait().unwrap().code().unwrap_or(1), + ); +} diff --git a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh index cabbaaa8922..4d0dfa16c5e 100644 --- a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh +++ b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh @@ -2,7 +2,6 @@ set -e ./y.rs build --no-unstable-features -source scripts/config.sh echo "[SETUP] Rust fork" git clone https://github.com/rust-lang/rust.git || true @@ -26,21 +25,6 @@ index d95b5b7f17f..00b6f0e3635 100644 [dev-dependencies] rand = "0.7" rand_xorshift = "0.2" -diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs -index 887d27fd6dca4..2c2239f2b83d1 100644 ---- a/src/tools/compiletest/src/header.rs -+++ b/src/tools/compiletest/src/header.rs -@@ -806,8 +806,8 @@ pub fn make_test_description<R: Read>( - cfg: Option<&str>, - ) -> test::TestDesc { - let mut ignore = false; - #[cfg(not(bootstrap))] -- let ignore_message: Option<String> = None; -+ let ignore_message: Option<&str> = None; - let mut should_fail = false; - - let rustc_has_profiler_support = env::var_os("RUSTC_PROFILER_SUPPORT").is_some(); - diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 8431aa7b818..a3ff7e68ce5 100644 --- a/src/tools/compiletest/src/runtest.rs @@ -67,7 +51,7 @@ changelog-seen = 2 ninja = false [build] -rustc = "$(pwd)/../build/bin/cg_clif" +rustc = "$(pwd)/../build/rustc-clif" cargo = "$(rustup which cargo)" full-bootstrap = true local-rebuild = true diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index 4cf24c02235..9bdb9f22c54 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -101,11 +101,10 @@ rm src/test/incremental/spike-neg1.rs # errors out for some reason rm src/test/incremental/spike-neg2.rs # same rm src/test/ui/issues/issue-74564-if-expr-stack-overflow.rs # gives a stackoverflow before the backend runs rm src/test/ui/mir/ssa-analysis-regression-50041.rs # produces ICE +rm src/test/ui/type-alias-impl-trait/assoc-projection-ice.rs # produces ICE rm src/test/ui/simd/intrinsic/generic-reduction-pass.rs # simd_reduce_add_unordered doesn't accept an accumulator for integer vectors -rm src/test/ui/rfc-2091-track-caller/intrinsic-wrapper.rs # wrong result from `Location::caller()` - # bugs in the test suite # ====================== rm src/test/ui/backtrace.rs # TODO warning diff --git a/compiler/rustc_codegen_cranelift/scripts/tests.sh b/compiler/rustc_codegen_cranelift/scripts/tests.sh index aae626081f6..9b5ffa40960 100755 --- a/compiler/rustc_codegen_cranelift/scripts/tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/tests.sh @@ -2,10 +2,43 @@ set -e -source scripts/config.sh -source scripts/ext_config.sh -export RUSTC=false # ensure that cg_llvm isn't accidentally used -MY_RUSTC="$(pwd)/build/bin/cg_clif $RUSTFLAGS -L crate=target/out --out-dir target/out -Cdebuginfo=2" +export CG_CLIF_DISPLAY_CG_TIME=1 +export CG_CLIF_DISABLE_INCR_CACHE=1 + +export HOST_TRIPLE=$(rustc -vV | grep host | cut -d: -f2 | tr -d " ") +export TARGET_TRIPLE=${TARGET_TRIPLE:-$HOST_TRIPLE} + +export RUN_WRAPPER='' + +case "$TARGET_TRIPLE" in + x86_64*) + export JIT_SUPPORTED=1 + ;; + *) + export JIT_SUPPORTED=0 + ;; +esac + +if [[ "$HOST_TRIPLE" != "$TARGET_TRIPLE" ]]; then + export JIT_SUPPORTED=0 + if [[ "$TARGET_TRIPLE" == "aarch64-unknown-linux-gnu" ]]; then + # We are cross-compiling for aarch64. Use the correct linker and run tests in qemu. + export RUSTFLAGS='-Clinker=aarch64-linux-gnu-gcc '$RUSTFLAGS + export RUN_WRAPPER='qemu-aarch64 -L /usr/aarch64-linux-gnu' + elif [[ "$TARGET_TRIPLE" == "x86_64-pc-windows-gnu" ]]; then + # We are cross-compiling for Windows. Run tests in wine. + export RUN_WRAPPER='wine' + else + echo "Unknown non-native platform" + fi +fi + +# FIXME fix `#[linkage = "extern_weak"]` without this +if [[ "$(uname)" == 'Darwin' ]]; then + export RUSTFLAGS="$RUSTFLAGS -Clink-arg=-undefined -Clink-arg=dynamic_lookup" +fi + +MY_RUSTC="$(pwd)/build/rustc-clif $RUSTFLAGS -L crate=target/out --out-dir target/out -Cdebuginfo=2" function no_sysroot_tests() { echo "[BUILD] mini_core" @@ -39,7 +72,7 @@ function base_sysroot_tests() { $MY_RUSTC example/issue-91827-extern-types.rs --crate-name issue_91827_extern_types --crate-type bin --target "$TARGET_TRIPLE" $RUN_WRAPPER ./target/out/issue_91827_extern_types - echo "[AOT] alloc_system" + echo "[BUILD] alloc_system" $MY_RUSTC example/alloc_system.rs --crate-type lib --target "$TARGET_TRIPLE" echo "[AOT] alloc_example" @@ -56,14 +89,14 @@ function base_sysroot_tests() { echo "[JIT] std_example (skipped)" fi - echo "[AOT] dst_field_align" - $MY_RUSTC example/dst-field-align.rs --crate-name dst_field_align --crate-type bin --target "$TARGET_TRIPLE" - $RUN_WRAPPER ./target/out/dst_field_align || (echo $?; false) - echo "[AOT] std_example" $MY_RUSTC example/std_example.rs --crate-type bin --target "$TARGET_TRIPLE" $RUN_WRAPPER ./target/out/std_example arg + echo "[AOT] dst_field_align" + $MY_RUSTC example/dst-field-align.rs --crate-name dst_field_align --crate-type bin --target "$TARGET_TRIPLE" + $RUN_WRAPPER ./target/out/dst_field_align + echo "[AOT] subslice-patterns-const-eval" $MY_RUSTC example/subslice-patterns-const-eval.rs --crate-type bin -Cpanic=abort --target "$TARGET_TRIPLE" $RUN_WRAPPER ./target/out/subslice-patterns-const-eval @@ -97,7 +130,7 @@ function extended_sysroot_tests() { if [[ "$HOST_TRIPLE" = "$TARGET_TRIPLE" ]]; then echo "[BENCH COMPILE] ebobby/simple-raytracer" hyperfine --runs "${RUN_RUNS:-10}" --warmup 1 --prepare "../build/cargo-clif clean" \ - "RUSTC=rustc RUSTFLAGS='' cargo build" \ + "RUSTFLAGS='' cargo build" \ "../build/cargo-clif build" echo "[BENCH RUN] ebobby/simple-raytracer" diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index ef56fb191bf..b163a426191 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -309,7 +309,7 @@ fn codegen_call_argument_operand<'tcx>( pub(crate) fn codegen_terminator_call<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, - span: Span, + source_info: mir::SourceInfo, func: &Operand<'tcx>, args: &[Operand<'tcx>], mir_dest: Option<(Place<'tcx>, BasicBlock)>, @@ -340,7 +340,13 @@ pub(crate) fn codegen_terminator_call<'tcx>( match instance.def { InstanceDef::Intrinsic(_) => { - crate::intrinsics::codegen_intrinsic_call(fx, instance, args, destination, span); + crate::intrinsics::codegen_intrinsic_call( + fx, + instance, + args, + destination, + source_info, + ); return; } InstanceDef::DropGlue(_, None) => { @@ -402,7 +408,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( // Pass the caller location for `#[track_caller]`. if instance.map(|inst| inst.def.requires_caller_location(fx.tcx)).unwrap_or(false) { - let caller_location = fx.get_caller_location(span); + let caller_location = fx.get_caller_location(source_info); args.push(CallArgument { value: caller_location, is_owned: false }); } @@ -479,9 +485,10 @@ pub(crate) fn codegen_terminator_call<'tcx>( // FIXME find a cleaner way to support varargs if fn_sig.c_variadic { if !matches!(fn_sig.abi, Abi::C { .. }) { - fx.tcx - .sess - .span_fatal(span, &format!("Variadic call for non-C abi {:?}", fn_sig.abi)); + fx.tcx.sess.span_fatal( + source_info.span, + &format!("Variadic call for non-C abi {:?}", fn_sig.abi), + ); } let sig_ref = fx.bcx.func.dfg.call_signature(call_inst).unwrap(); let abi_params = call_args @@ -490,9 +497,10 @@ pub(crate) fn codegen_terminator_call<'tcx>( let ty = fx.bcx.func.dfg.value_type(arg); if !ty.is_int() { // FIXME set %al to upperbound on float args once floats are supported - fx.tcx - .sess - .span_fatal(span, &format!("Non int ty {:?} for variadic call", ty)); + fx.tcx.sess.span_fatal( + source_info.span, + &format!("Non int ty {:?} for variadic call", ty), + ); } AbiParam::new(ty) }) @@ -513,7 +521,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( pub(crate) fn codegen_drop<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, - span: Span, + source_info: mir::SourceInfo, drop_place: CPlace<'tcx>, ) { let ty = drop_place.layout().ty; @@ -560,7 +568,7 @@ pub(crate) fn codegen_drop<'tcx>( if drop_instance.def.requires_caller_location(fx.tcx) { // Pass the caller location for `#[track_caller]`. - let caller_location = fx.get_caller_location(span); + let caller_location = fx.get_caller_location(source_info); call_args.extend( adjust_arg_for_abi(fx, caller_location, &fn_abi.args[1], false).into_iter(), ); diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 65346cb3962..65e5812a8a5 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -325,7 +325,7 @@ fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, '_>) { AssertKind::BoundsCheck { ref len, ref index } => { let len = codegen_operand(fx, len).load_scalar(fx); let index = codegen_operand(fx, index).load_scalar(fx); - let location = fx.get_caller_location(source_info.span).load_scalar(fx); + let location = fx.get_caller_location(source_info).load_scalar(fx); codegen_panic_inner( fx, @@ -336,7 +336,7 @@ fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, '_>) { } _ => { let msg_str = msg.description(); - codegen_panic(fx, msg_str, source_info.span); + codegen_panic(fx, msg_str, source_info); } } } @@ -398,7 +398,13 @@ fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, '_>) { from_hir_call: _, } => { fx.tcx.sess.time("codegen call", || { - crate::abi::codegen_terminator_call(fx, *fn_span, func, args, *destination) + crate::abi::codegen_terminator_call( + fx, + mir::SourceInfo { span: *fn_span, ..source_info }, + func, + args, + *destination, + ) }); } TerminatorKind::InlineAsm { @@ -450,7 +456,7 @@ fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, '_>) { } TerminatorKind::Drop { place, target, unwind: _ } => { let drop_place = codegen_place(fx, *place); - crate::abi::codegen_drop(fx, source_info.span, drop_place); + crate::abi::codegen_drop(fx, source_info, drop_place); let target_block = fx.get_block(*target); fx.bcx.ins().jump(target_block, &[]); @@ -471,7 +477,7 @@ fn codegen_stmt<'tcx>( fx.set_debug_loc(stmt.source_info); - #[cfg(disabled)] + #[cfg(any())] // This is never true match &stmt.kind { StatementKind::StorageLive(..) | StatementKind::StorageDead(..) => {} // Those are not very useful _ => { @@ -898,14 +904,18 @@ pub(crate) fn codegen_operand<'tcx>( } } -pub(crate) fn codegen_panic<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, msg_str: &str, span: Span) { - let location = fx.get_caller_location(span).load_scalar(fx); +pub(crate) fn codegen_panic<'tcx>( + fx: &mut FunctionCx<'_, '_, 'tcx>, + msg_str: &str, + source_info: mir::SourceInfo, +) { + let location = fx.get_caller_location(source_info).load_scalar(fx); let msg_ptr = fx.anonymous_str(msg_str); let msg_len = fx.bcx.ins().iconst(fx.pointer_type, i64::try_from(msg_str.len()).unwrap()); let args = [msg_ptr, msg_len, location]; - codegen_panic_inner(fx, rustc_hir::LangItem::Panic, &args, span); + codegen_panic_inner(fx, rustc_hir::LangItem::Panic, &args, source_info.span); } pub(crate) fn codegen_panic_inner<'tcx>( diff --git a/compiler/rustc_codegen_cranelift/src/bin/cg_clif.rs b/compiler/rustc_codegen_cranelift/src/bin/cg_clif.rs deleted file mode 100644 index 5984ec8412a..00000000000 --- a/compiler/rustc_codegen_cranelift/src/bin/cg_clif.rs +++ /dev/null @@ -1,94 +0,0 @@ -#![feature(rustc_private)] -#![warn(rust_2018_idioms)] -#![warn(unused_lifetimes)] -#![warn(unreachable_pub)] - -extern crate rustc_data_structures; -extern crate rustc_driver; -extern crate rustc_interface; -extern crate rustc_session; -extern crate rustc_target; - -use std::panic; - -use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry}; -use rustc_interface::interface; -use rustc_session::config::{ErrorOutputType, TrimmedDefPaths}; -use rustc_session::early_error; -use rustc_target::spec::PanicStrategy; - -// FIXME use std::lazy::SyncLazy once it stabilizes -use once_cell::sync::Lazy; - -const BUG_REPORT_URL: &str = "https://github.com/bjorn3/rustc_codegen_cranelift/issues/new"; - -static DEFAULT_HOOK: Lazy<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> = - Lazy::new(|| { - let hook = panic::take_hook(); - panic::set_hook(Box::new(|info| { - // Invoke the default handler, which prints the actual panic message and optionally a backtrace - (*DEFAULT_HOOK)(info); - - // Separate the output with an empty line - eprintln!(); - - // Print the ICE message - rustc_driver::report_ice(info, BUG_REPORT_URL); - })); - hook - }); - -#[derive(Default)] -pub struct CraneliftPassesCallbacks { - time_passes: bool, -} - -impl rustc_driver::Callbacks for CraneliftPassesCallbacks { - fn config(&mut self, config: &mut interface::Config) { - // If a --prints=... option has been given, we don't print the "total" - // time because it will mess up the --prints output. See #64339. - self.time_passes = config.opts.prints.is_empty() - && (config.opts.debugging_opts.time_passes || config.opts.debugging_opts.time); - - config.opts.cg.panic = Some(PanicStrategy::Abort); - config.opts.debugging_opts.panic_abort_tests = true; - config.opts.maybe_sysroot = Some(config.opts.maybe_sysroot.clone().unwrap_or_else(|| { - std::env::current_exe().unwrap().parent().unwrap().parent().unwrap().to_owned() - })); - - config.opts.trimmed_def_paths = TrimmedDefPaths::GoodPath; - } -} - -fn main() { - let start_time = std::time::Instant::now(); - let start_rss = get_resident_set_size(); - rustc_driver::init_rustc_env_logger(); - let mut callbacks = CraneliftPassesCallbacks::default(); - Lazy::force(&DEFAULT_HOOK); // Install ice hook - let exit_code = rustc_driver::catch_with_exit_code(|| { - let args = std::env::args_os() - .enumerate() - .map(|(i, arg)| { - arg.into_string().unwrap_or_else(|arg| { - early_error( - ErrorOutputType::default(), - &format!("Argument {} is not valid Unicode: {:?}", i, arg), - ) - }) - }) - .collect::<Vec<_>>(); - let mut run_compiler = rustc_driver::RunCompiler::new(&args, &mut callbacks); - run_compiler.set_make_codegen_backend(Some(Box::new(move |_| { - Box::new(rustc_codegen_cranelift::CraneliftCodegenBackend { config: None }) - }))); - run_compiler.run() - }); - - if callbacks.time_passes { - let end_rss = get_resident_set_size(); - print_time_passes_entry("total", start_time.elapsed(), start_rss, end_rss); - } - - std::process::exit(exit_code) -} diff --git a/compiler/rustc_codegen_cranelift/src/bin/cg_clif_build_sysroot.rs b/compiler/rustc_codegen_cranelift/src/bin/cg_clif_build_sysroot.rs deleted file mode 100644 index bde4d71b9a3..00000000000 --- a/compiler/rustc_codegen_cranelift/src/bin/cg_clif_build_sysroot.rs +++ /dev/null @@ -1,93 +0,0 @@ -//! The only difference between this and cg_clif.rs is that this binary defaults to using cg_llvm -//! instead of cg_clif and requires `--clif` to use cg_clif and that this binary doesn't have JIT -//! support. -//! This is necessary as with Cargo `RUSTC` applies to both target crates and host crates. The host -//! crates must be built with cg_llvm as we are currently building a sysroot for cg_clif. -//! `RUSTFLAGS` however is only applied to target crates, so `--clif` would only be passed to the -//! target crates. - -#![feature(rustc_private)] -#![warn(rust_2018_idioms)] -#![warn(unused_lifetimes)] -#![warn(unreachable_pub)] - -extern crate rustc_driver; -extern crate rustc_interface; -extern crate rustc_session; -extern crate rustc_target; - -use std::path::PathBuf; - -use rustc_interface::interface; -use rustc_session::config::ErrorOutputType; -use rustc_session::early_error; -use rustc_target::spec::PanicStrategy; - -fn find_sysroot() -> String { - // Taken from https://github.com/Manishearth/rust-clippy/pull/911. - let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME")); - let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN")); - match (home, toolchain) { - (Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain), - _ => option_env!("RUST_SYSROOT") - .expect("need to specify RUST_SYSROOT env var or use rustup or multirust") - .to_owned(), - } -} - -pub struct CraneliftPassesCallbacks { - use_clif: bool, -} - -impl rustc_driver::Callbacks for CraneliftPassesCallbacks { - fn config(&mut self, config: &mut interface::Config) { - if !self.use_clif { - config.opts.maybe_sysroot = Some(PathBuf::from(find_sysroot())); - return; - } - - config.opts.cg.panic = Some(PanicStrategy::Abort); - config.opts.debugging_opts.panic_abort_tests = true; - config.opts.maybe_sysroot = - Some(std::env::current_exe().unwrap().parent().unwrap().parent().unwrap().to_owned()); - } -} - -fn main() { - rustc_driver::init_rustc_env_logger(); - rustc_driver::install_ice_hook(); - let exit_code = rustc_driver::catch_with_exit_code(|| { - let mut use_clif = false; - - let args = std::env::args_os() - .enumerate() - .map(|(i, arg)| { - arg.into_string().unwrap_or_else(|arg| { - early_error( - ErrorOutputType::default(), - &format!("Argument {} is not valid Unicode: {:?}", i, arg), - ) - }) - }) - .filter(|arg| { - if arg == "--clif" { - use_clif = true; - false - } else { - true - } - }) - .collect::<Vec<_>>(); - - let mut callbacks = CraneliftPassesCallbacks { use_clif }; - - let mut run_compiler = rustc_driver::RunCompiler::new(&args, &mut callbacks); - if use_clif { - run_compiler.set_make_codegen_backend(Some(Box::new(move |_| { - Box::new(rustc_codegen_cranelift::CraneliftCodegenBackend { config: None }) - }))); - } - run_compiler.run() - }); - std::process::exit(exit_code) -} diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index ffa629ca16c..f9dc1b5169e 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -340,22 +340,46 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { self.bcx.set_srcloc(SourceLoc::new(index as u32)); } - pub(crate) fn get_caller_location(&mut self, span: Span) -> CValue<'tcx> { - if let Some(loc) = self.caller_location { - // `#[track_caller]` is used; return caller location instead of current location. - return loc; + // Note: must be kept in sync with get_caller_location from cg_ssa + pub(crate) fn get_caller_location(&mut self, mut source_info: mir::SourceInfo) -> CValue<'tcx> { + let span_to_caller_location = |fx: &mut FunctionCx<'_, '_, 'tcx>, span: Span| { + let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span); + let caller = fx.tcx.sess.source_map().lookup_char_pos(topmost.lo()); + let const_loc = fx.tcx.const_caller_location(( + rustc_span::symbol::Symbol::intern( + &caller.file.name.prefer_remapped().to_string_lossy(), + ), + caller.line as u32, + caller.col_display as u32 + 1, + )); + crate::constant::codegen_const_value(fx, const_loc, fx.tcx.caller_location_ty()) + }; + + // Walk up the `SourceScope`s, in case some of them are from MIR inlining. + // If so, the starting `source_info.span` is in the innermost inlined + // function, and will be replaced with outer callsite spans as long + // as the inlined functions were `#[track_caller]`. + loop { + let scope_data = &self.mir.source_scopes[source_info.scope]; + + if let Some((callee, callsite_span)) = scope_data.inlined { + // Stop inside the most nested non-`#[track_caller]` function, + // before ever reaching its caller (which is irrelevant). + if !callee.def.requires_caller_location(self.tcx) { + return span_to_caller_location(self, source_info.span); + } + source_info.span = callsite_span; + } + + // Skip past all of the parents with `inlined: None`. + match scope_data.inlined_parent_scope { + Some(parent) => source_info.scope = parent, + None => break, + } } - let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span); - let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo()); - let const_loc = self.tcx.const_caller_location(( - rustc_span::symbol::Symbol::intern( - &caller.file.name.prefer_remapped().to_string_lossy(), - ), - caller.line as u32, - caller.col_display as u32 + 1, - )); - crate::constant::codegen_const_value(self, const_loc, self.tcx.caller_location_ty()) + // No inlined `SourceScope`s, or all of them were `#[track_caller]`. + self.caller_location.unwrap_or_else(|| span_to_caller_location(self, source_info.span)) } pub(crate) fn anonymous_str(&mut self, msg: &str) -> Value { diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs index 7f15bc75fda..1b01f4edbb3 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs @@ -74,6 +74,7 @@ fn create_jit_module<'tcx>( jit_builder.hotswap(hotswap); crate::compiler_builtins::register_functions_for_jit(&mut jit_builder); jit_builder.symbols(imported_symbols); + jit_builder.symbol("__clif_jit_fn", clif_jit_fn as *const u8); let mut jit_module = JITModule::new(jit_builder); let mut cx = crate::CodegenCx::new( @@ -210,8 +211,7 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! { } } -#[no_mangle] -extern "C" fn __clif_jit_fn( +extern "C" fn clif_jit_fn( instance_ptr: *const Instance<'static>, trampoline_ptr: *const u8, ) -> *const u8 { diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index d76dfca7960..29b3f36b2be 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -218,7 +218,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( instance: Instance<'tcx>, args: &[mir::Operand<'tcx>], destination: Option<(CPlace<'tcx>, BasicBlock)>, - span: Span, + source_info: mir::SourceInfo, ) { let intrinsic = fx.tcx.item_name(instance.def_id()); let substs = instance.substs; @@ -232,7 +232,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( fx.bcx.ins().trap(TrapCode::User(0)); } sym::transmute => { - crate::base::codegen_panic(fx, "Transmuting to uninhabited type.", span); + crate::base::codegen_panic(fx, "Transmuting to uninhabited type.", source_info); } _ => unimplemented!("unsupported instrinsic {}", intrinsic), } @@ -241,7 +241,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( }; if intrinsic.as_str().starts_with("simd_") { - self::simd::codegen_simd_intrinsic_call(fx, intrinsic, substs, args, ret, span); + self::simd::codegen_simd_intrinsic_call(fx, intrinsic, substs, args, ret, source_info.span); let ret_block = fx.get_block(destination.expect("SIMD intrinsics don't diverge").1); fx.bcx.ins().jump(ret_block, &[]); } else if codegen_float_intrinsic_call(fx, intrinsic, args, ret) { @@ -255,7 +255,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( substs, args, ret, - span, + source_info, destination, ); } @@ -339,7 +339,7 @@ fn codegen_regular_intrinsic_call<'tcx>( substs: SubstsRef<'tcx>, args: &[mir::Operand<'tcx>], ret: CPlace<'tcx>, - span: Span, + source_info: mir::SourceInfo, destination: Option<(CPlace<'tcx>, BasicBlock)>, ) { let usize_layout = fx.layout_of(fx.tcx.types.usize); @@ -347,7 +347,7 @@ fn codegen_regular_intrinsic_call<'tcx>( intrinsic_match! { fx, intrinsic, args, _ => { - fx.tcx.sess.span_fatal(span, &format!("unsupported intrinsic {}", intrinsic)); + fx.tcx.sess.span_fatal(source_info.span, &format!("unsupported intrinsic {}", intrinsic)); }; assume, (c _a) {}; @@ -658,7 +658,7 @@ fn codegen_regular_intrinsic_call<'tcx>( crate::base::codegen_panic( fx, &format!("attempted to instantiate uninhabited type `{}`", layout.ty), - span, + source_info, ) }); return; @@ -669,7 +669,7 @@ fn codegen_regular_intrinsic_call<'tcx>( crate::base::codegen_panic( fx, &format!("attempted to zero-initialize type `{}`, which is invalid", layout.ty), - span, + source_info, ); }); return; @@ -680,7 +680,7 @@ fn codegen_regular_intrinsic_call<'tcx>( crate::base::codegen_panic( fx, &format!("attempted to leave type `{}` uninitialized, which is invalid", layout.ty), - span, + source_info, ) }); return; @@ -713,14 +713,21 @@ fn codegen_regular_intrinsic_call<'tcx>( ret.write_cvalue(fx, val); }; - ptr_offset_from, (v ptr, v base) { + ptr_offset_from | ptr_offset_from_unsigned, (v ptr, v base) { let ty = substs.type_at(0); - let isize_layout = fx.layout_of(fx.tcx.types.isize); let pointee_size: u64 = fx.layout_of(ty).size.bytes(); - let diff = fx.bcx.ins().isub(ptr, base); + let diff_bytes = fx.bcx.ins().isub(ptr, base); // FIXME this can be an exact division. - let val = CValue::by_val(fx.bcx.ins().sdiv_imm(diff, pointee_size as i64), isize_layout); + let val = if intrinsic == sym::ptr_offset_from_unsigned { + let usize_layout = fx.layout_of(fx.tcx.types.usize); + // Because diff_bytes ULE isize::MAX, this would be fine as signed, + // but unsigned is slightly easier to codegen, so might as well. + CValue::by_val(fx.bcx.ins().udiv_imm(diff_bytes, pointee_size as i64), usize_layout) + } else { + let isize_layout = fx.layout_of(fx.tcx.types.isize); + CValue::by_val(fx.bcx.ins().sdiv_imm(diff_bytes, pointee_size as i64), isize_layout) + }; ret.write_cvalue(fx, val); }; @@ -735,7 +742,7 @@ fn codegen_regular_intrinsic_call<'tcx>( }; caller_location, () { - let caller_location = fx.get_caller_location(span); + let caller_location = fx.get_caller_location(source_info); ret.write_cvalue(fx, caller_location); }; @@ -758,12 +765,12 @@ fn codegen_regular_intrinsic_call<'tcx>( fx.bcx.ins().jump(ret_block, &[]); return; } else { - fx.tcx.sess.span_fatal(span, "128bit atomics not yet supported"); + fx.tcx.sess.span_fatal(source_info.span, "128bit atomics not yet supported"); } } ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} _ => { - report_atomic_type_validation_error(fx, intrinsic, span, ty); + report_atomic_type_validation_error(fx, intrinsic, source_info.span, ty); return; } } @@ -786,12 +793,12 @@ fn codegen_regular_intrinsic_call<'tcx>( fx.bcx.ins().jump(ret_block, &[]); return; } else { - fx.tcx.sess.span_fatal(span, "128bit atomics not yet supported"); + fx.tcx.sess.span_fatal(source_info.span, "128bit atomics not yet supported"); } } ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} _ => { - report_atomic_type_validation_error(fx, intrinsic, span, ty); + report_atomic_type_validation_error(fx, intrinsic, source_info.span, ty); return; } } @@ -805,7 +812,7 @@ fn codegen_regular_intrinsic_call<'tcx>( match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} _ => { - report_atomic_type_validation_error(fx, intrinsic, span, layout.ty); + report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty); return; } } @@ -823,7 +830,7 @@ fn codegen_regular_intrinsic_call<'tcx>( match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} _ => { - report_atomic_type_validation_error(fx, intrinsic, span, layout.ty); + report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty); return; } } @@ -843,7 +850,7 @@ fn codegen_regular_intrinsic_call<'tcx>( match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} _ => { - report_atomic_type_validation_error(fx, intrinsic, span, layout.ty); + report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty); return; } } @@ -861,7 +868,7 @@ fn codegen_regular_intrinsic_call<'tcx>( match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} _ => { - report_atomic_type_validation_error(fx, intrinsic, span, layout.ty); + report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty); return; } } @@ -879,7 +886,7 @@ fn codegen_regular_intrinsic_call<'tcx>( match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} _ => { - report_atomic_type_validation_error(fx, intrinsic, span, layout.ty); + report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty); return; } } @@ -897,7 +904,7 @@ fn codegen_regular_intrinsic_call<'tcx>( match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} _ => { - report_atomic_type_validation_error(fx, intrinsic, span, layout.ty); + report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty); return; } } @@ -915,7 +922,7 @@ fn codegen_regular_intrinsic_call<'tcx>( match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} _ => { - report_atomic_type_validation_error(fx, intrinsic, span, layout.ty); + report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty); return; } } @@ -933,7 +940,7 @@ fn codegen_regular_intrinsic_call<'tcx>( match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} _ => { - report_atomic_type_validation_error(fx, intrinsic, span, layout.ty); + report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty); return; } } @@ -951,7 +958,7 @@ fn codegen_regular_intrinsic_call<'tcx>( match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} _ => { - report_atomic_type_validation_error(fx, intrinsic, span, layout.ty); + report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty); return; } } @@ -969,7 +976,7 @@ fn codegen_regular_intrinsic_call<'tcx>( match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} _ => { - report_atomic_type_validation_error(fx, intrinsic, span, layout.ty); + report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty); return; } } @@ -987,7 +994,7 @@ fn codegen_regular_intrinsic_call<'tcx>( match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} _ => { - report_atomic_type_validation_error(fx, intrinsic, span, layout.ty); + report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty); return; } } @@ -1005,7 +1012,7 @@ fn codegen_regular_intrinsic_call<'tcx>( match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} _ => { - report_atomic_type_validation_error(fx, intrinsic, span, layout.ty); + report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty); return; } } diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index 8f80b02ae0d..a68225de58b 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -828,6 +828,7 @@ pub(crate) fn assert_assignable<'tcx>( } } } + (ty::Array(a, _), ty::Array(b, _)) => assert_assignable(fx, *a, *b), _ => { assert_eq!( from_ty, to_ty, diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index 2e8cd934eb2..20d91b80e8c 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -592,7 +592,7 @@ fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> ConstraintOrRegister { InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg0) => unimplemented!(), InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => unimplemented!(), InlineAsmRegClass::X86( - X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg, + X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg | X86InlineAsmRegClass::tmm_reg, ) => unreachable!("clobber-only"), InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { bug!("GCC backend does not support SPIR-V") @@ -656,6 +656,7 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg) => unimplemented!(), InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => cx.type_i16(), InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg0) => cx.type_i16(), + InlineAsmRegClass::X86(X86InlineAsmRegClass::tmm_reg) => unimplemented!(), InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => cx.type_i32(), InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { bug!("LLVM backend does not support SPIR-V") @@ -787,7 +788,7 @@ fn modifier_to_gcc(arch: InlineAsmArch, reg: InlineAsmRegClass, modifier: Option }, InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => None, InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg0) => None, - InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg) => { + InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg | X86InlineAsmRegClass::tmm_reg) => { unreachable!("clobber-only") } InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => unimplemented!(), diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index e994001f96f..a53946995ee 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -604,7 +604,8 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'_>>) -> InlineAsmRegClass::X86( X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg - | X86InlineAsmRegClass::kreg0, + | X86InlineAsmRegClass::kreg0 + | X86InlineAsmRegClass::tmm_reg, ) => unreachable!("clobber-only"), InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => "r", InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::reg) => "r", @@ -692,7 +693,8 @@ fn modifier_to_llvm( InlineAsmRegClass::X86( X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg - | X86InlineAsmRegClass::kreg0, + | X86InlineAsmRegClass::kreg0 + | X86InlineAsmRegClass::tmm_reg, ) => { unreachable!("clobber-only") } @@ -766,7 +768,8 @@ fn dummy_output_type<'ll>(cx: &CodegenCx<'ll, '_>, reg: InlineAsmRegClass) -> &' InlineAsmRegClass::X86( X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg - | X86InlineAsmRegClass::kreg0, + | X86InlineAsmRegClass::kreg0 + | X86InlineAsmRegClass::tmm_reg, ) => { unreachable!("clobber-only") } diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index c098ce36f02..9394d60134f 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -6,6 +6,7 @@ use rustc_hir::def_id::DefId; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::config::OptLevel; +use rustc_span::symbol::sym; use rustc_target::spec::abi::Abi; use rustc_target::spec::{FramePointer, SanitizerSet, StackProbeType, StackProtector}; use smallvec::SmallVec; @@ -329,9 +330,7 @@ pub fn from_fn_attrs<'ll, 'tcx>( ) { let span = cx .tcx - .get_attrs(instance.def_id()) - .iter() - .find(|a| a.has_name(rustc_span::sym::target_feature)) + .get_attr(instance.def_id(), sym::target_feature) .map_or_else(|| cx.tcx.def_span(instance.def_id()), |a| a.span); let msg = format!( "the target features {} must all be either enabled or disabled together", diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs index f814cc93043..8f6438e85ad 100644 --- a/compiler/rustc_codegen_llvm/src/back/archive.rs +++ b/compiler/rustc_codegen_llvm/src/back/archive.rs @@ -152,8 +152,10 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> { }; let target = &self.config.sess.target; - let mingw_gnu_toolchain = - target.vendor == "pc" && target.os == "windows" && target.env == "gnu"; + let mingw_gnu_toolchain = target.vendor == "pc" + && target.os == "windows" + && target.env == "gnu" + && target.abi.is_empty(); let import_name_and_ordinal_vector: Vec<(String, Option<u16>)> = dll_imports .iter() diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index b5b2a27d237..c7497bfd355 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -354,14 +354,14 @@ fn fat_lto( Ok(LtoModuleCodegen::Fat { module, _serialized_bitcode: serialized_bitcode }) } -crate struct Linker<'a>(&'a mut llvm::Linker<'a>); +pub(crate) struct Linker<'a>(&'a mut llvm::Linker<'a>); impl<'a> Linker<'a> { - crate fn new(llmod: &'a llvm::Module) -> Self { + pub(crate) fn new(llmod: &'a llvm::Module) -> Self { unsafe { Linker(llvm::LLVMRustLinkerNew(llmod)) } } - crate fn add(&mut self, bytecode: &[u8]) -> Result<(), ()> { + pub(crate) fn add(&mut self, bytecode: &[u8]) -> Result<(), ()> { unsafe { if llvm::LLVMRustLinkerAdd( self.0, @@ -394,15 +394,15 @@ impl Drop for Linker<'_> { /// /// At a high level Thin LTO looks like: /// -/// 1. Prepare a "summary" of each LLVM module in question which describes -/// the values inside, cost of the values, etc. -/// 2. Merge the summaries of all modules in question into one "index" -/// 3. Perform some global analysis on this index -/// 4. For each module, use the index and analysis calculated previously to -/// perform local transformations on the module, for example inlining -/// small functions from other modules. -/// 5. Run thin-specific optimization passes over each module, and then code -/// generate everything at the end. +/// 1. Prepare a "summary" of each LLVM module in question which describes +/// the values inside, cost of the values, etc. +/// 2. Merge the summaries of all modules in question into one "index" +/// 3. Perform some global analysis on this index +/// 4. For each module, use the index and analysis calculated previously to +/// perform local transformations on the module, for example inlining +/// small functions from other modules. +/// 5. Run thin-specific optimization passes over each module, and then code +/// generate everything at the end. /// /// The summary for each module is intended to be quite cheap, and the global /// index is relatively quite cheap to create as well. As a result, the goal of diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 88b87951ecd..839018e2a75 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1412,7 +1412,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { unsafe { llvm::LLVMBuildVAArg(self.llbuilder, list, ty, UNNAMED) } } - crate fn call_intrinsic(&mut self, intrinsic: &str, args: &[&'ll Value]) -> &'ll Value { + pub(crate) fn call_intrinsic(&mut self, intrinsic: &str, args: &[&'ll Value]) -> &'ll Value { let (ty, f) = self.cx.get_intrinsic(intrinsic); self.call(ty, f, args, None) } diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 4d3f3f318b8..5bbbfe9a4ab 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -212,11 +212,11 @@ pub fn ptrcast<'ll>(val: &'ll Value, ty: &'ll Type) -> &'ll Value { } impl<'ll> CodegenCx<'ll, '_> { - crate fn const_bitcast(&self, val: &'ll Value, ty: &'ll Type) -> &'ll Value { + pub(crate) fn const_bitcast(&self, val: &'ll Value, ty: &'ll Type) -> &'ll Value { unsafe { llvm::LLVMConstBitCast(val, ty) } } - crate fn static_addr_of_mut( + pub(crate) fn static_addr_of_mut( &self, cv: &'ll Value, align: Align, @@ -241,7 +241,7 @@ impl<'ll> CodegenCx<'ll, '_> { } } - crate fn get_static(&self, def_id: DefId) -> &'ll Value { + pub(crate) fn get_static(&self, def_id: DefId) -> &'ll Value { let instance = Instance::mono(self.tcx, def_id); if let Some(&g) = self.instances.borrow().get(&instance) { return g; diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index d296ee3b42c..5544f0d3f60 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -330,7 +330,7 @@ pub unsafe fn create_module<'ll>( } impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { - crate fn new( + pub(crate) fn new( tcx: TyCtxt<'tcx>, codegen_unit: &'tcx CodegenUnit<'tcx>, llvm_module: &'ll crate::ModuleLlvm, @@ -447,7 +447,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { } } - crate fn statics_to_rauw(&self) -> &RefCell<Vec<(&'ll Value, &'ll Value)>> { + pub(crate) fn statics_to_rauw(&self) -> &RefCell<Vec<(&'ll Value, &'ll Value)>> { &self.statics_to_rauw } @@ -599,7 +599,7 @@ impl<'ll, 'tcx> MiscMethods<'tcx> for CodegenCx<'ll, 'tcx> { } impl<'ll> CodegenCx<'ll, '_> { - crate fn get_intrinsic(&self, key: &str) -> (&'ll Type, &'ll Value) { + pub(crate) fn get_intrinsic(&self, key: &str) -> (&'ll Type, &'ll Value) { if let Some(v) = self.intrinsics.borrow().get(key).cloned() { return v; } @@ -890,7 +890,7 @@ impl<'ll> CodegenCx<'ll, '_> { None } - crate fn eh_catch_typeinfo(&self) -> &'ll Value { + pub(crate) fn eh_catch_typeinfo(&self) -> &'ll Value { if let Some(eh_catch_typeinfo) = self.eh_catch_typeinfo.get() { return eh_catch_typeinfo; } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/doc.md b/compiler/rustc_codegen_llvm/src/debuginfo/doc.md index 5a8976c6166..aaec4e68c17 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/doc.md +++ b/compiler/rustc_codegen_llvm/src/debuginfo/doc.md @@ -27,9 +27,9 @@ The module is thus driven from an outside client with functions like Internally the module will try to reuse already created metadata by utilizing a cache. The way to get a shared metadata node when needed is thus to just call the corresponding function in this module: - - let file_metadata = file_metadata(cx, file); - +```ignore (illustrative) +let file_metadata = file_metadata(cx, file); +``` The function will take care of probing the cache for an existing node for that exact file path. @@ -63,7 +63,7 @@ struct List { will generate the following callstack with a naive DFS algorithm: -``` +```ignore (illustrative) describe(t = List) describe(t = i32) describe(t = Option<Box<List>>) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index f2cf3b1ef5c..97d3acb34ce 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -36,20 +36,21 @@ use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{self, AdtKind, Instance, ParamEnv, Ty, TyCtxt, COMMON_VTABLE_ENTRIES}; use rustc_session::config::{self, DebugInfo}; use rustc_span::symbol::Symbol; +use rustc_span::FileName; use rustc_span::FileNameDisplayPreference; -use rustc_span::{self, SourceFile, SourceFileHash}; +use rustc_span::{self, SourceFile}; use rustc_target::abi::{Align, Size}; use smallvec::smallvec; use tracing::debug; use libc::{c_longlong, c_uint}; use std::borrow::Cow; -use std::collections::hash_map::Entry; use std::fmt::{self, Write}; use std::hash::{Hash, Hasher}; use std::iter; use std::path::{Path, PathBuf}; use std::ptr; +use tracing::instrument; impl PartialEq for llvm::Metadata { fn eq(&self, other: &Self) -> bool { @@ -527,78 +528,105 @@ fn hex_encode(data: &[u8]) -> String { } pub fn file_metadata<'ll>(cx: &CodegenCx<'ll, '_>, source_file: &SourceFile) -> &'ll DIFile { - debug!("file_metadata: file_name: {:?}", source_file.name); - - let hash = Some(&source_file.src_hash); - let file_name = Some(source_file.name.prefer_remapped().to_string()); - let directory = if source_file.is_real_file() && !source_file.is_imported() { - Some( - cx.sess() - .opts - .working_dir - .to_string_lossy(FileNameDisplayPreference::Remapped) - .to_string(), - ) - } else { - // If the path comes from an upstream crate we assume it has been made - // independent of the compiler's working directory one way or another. - None - }; - file_metadata_raw(cx, file_name, directory, hash) -} - -pub fn unknown_file_metadata<'ll>(cx: &CodegenCx<'ll, '_>) -> &'ll DIFile { - file_metadata_raw(cx, None, None, None) -} - -fn file_metadata_raw<'ll>( - cx: &CodegenCx<'ll, '_>, - file_name: Option<String>, - directory: Option<String>, - hash: Option<&SourceFileHash>, -) -> &'ll DIFile { - let key = (file_name, directory); - - match debug_context(cx).created_files.borrow_mut().entry(key) { - Entry::Occupied(o) => o.get(), - Entry::Vacant(v) => { - let (file_name, directory) = v.key(); - debug!("file_metadata: file_name: {:?}, directory: {:?}", file_name, directory); - - let file_name = file_name.as_deref().unwrap_or("<unknown>"); - let directory = directory.as_deref().unwrap_or(""); - - let (hash_kind, hash_value) = match hash { - Some(hash) => { - let kind = match hash.kind { - rustc_span::SourceFileHashAlgorithm::Md5 => llvm::ChecksumKind::MD5, - rustc_span::SourceFileHashAlgorithm::Sha1 => llvm::ChecksumKind::SHA1, - rustc_span::SourceFileHashAlgorithm::Sha256 => llvm::ChecksumKind::SHA256, - }; - (kind, hex_encode(hash.hash_bytes())) + let cache_key = Some((source_file.name_hash, source_file.src_hash)); + return debug_context(cx) + .created_files + .borrow_mut() + .entry(cache_key) + .or_insert_with(|| alloc_new_file_metadata(cx, source_file)); + + #[instrument(skip(cx, source_file), level = "debug")] + fn alloc_new_file_metadata<'ll>( + cx: &CodegenCx<'ll, '_>, + source_file: &SourceFile, + ) -> &'ll DIFile { + debug!(?source_file.name); + + let (directory, file_name) = match &source_file.name { + FileName::Real(filename) => { + let working_directory = &cx.sess().opts.working_dir; + debug!(?working_directory); + + let filename = cx + .sess() + .source_map() + .path_mapping() + .to_embeddable_absolute_path(filename.clone(), working_directory); + + // Construct the absolute path of the file + let abs_path = filename.remapped_path_if_available(); + debug!(?abs_path); + + if let Ok(rel_path) = + abs_path.strip_prefix(working_directory.remapped_path_if_available()) + { + // If the compiler's working directory (which also is the DW_AT_comp_dir of + // the compilation unit) is a prefix of the path we are about to emit, then + // only emit the part relative to the working directory. + // Because of path remapping we sometimes see strange things here: `abs_path` + // might actually look like a relative path + // (e.g. `<crate-name-and-version>/src/lib.rs`), so if we emit it without + // taking the working directory into account, downstream tooling will + // interpret it as `<working-directory>/<crate-name-and-version>/src/lib.rs`, + // which makes no sense. Usually in such cases the working directory will also + // be remapped to `<crate-name-and-version>` or some other prefix of the path + // we are remapping, so we end up with + // `<crate-name-and-version>/<crate-name-and-version>/src/lib.rs`. + // By moving the working directory portion into the `directory` part of the + // DIFile, we allow LLVM to emit just the relative path for DWARF, while + // still emitting the correct absolute path for CodeView. + ( + working_directory.to_string_lossy(FileNameDisplayPreference::Remapped), + rel_path.to_string_lossy().into_owned(), + ) + } else { + ("".into(), abs_path.to_string_lossy().into_owned()) } - None => (llvm::ChecksumKind::None, String::new()), - }; + } + other => ("".into(), other.prefer_remapped().to_string_lossy().into_owned()), + }; - let file_metadata = unsafe { - llvm::LLVMRustDIBuilderCreateFile( - DIB(cx), - file_name.as_ptr().cast(), - file_name.len(), - directory.as_ptr().cast(), - directory.len(), - hash_kind, - hash_value.as_ptr().cast(), - hash_value.len(), - ) - }; + let hash_kind = match source_file.src_hash.kind { + rustc_span::SourceFileHashAlgorithm::Md5 => llvm::ChecksumKind::MD5, + rustc_span::SourceFileHashAlgorithm::Sha1 => llvm::ChecksumKind::SHA1, + rustc_span::SourceFileHashAlgorithm::Sha256 => llvm::ChecksumKind::SHA256, + }; + let hash_value = hex_encode(source_file.src_hash.hash_bytes()); - v.insert(file_metadata); - file_metadata + unsafe { + llvm::LLVMRustDIBuilderCreateFile( + DIB(cx), + file_name.as_ptr().cast(), + file_name.len(), + directory.as_ptr().cast(), + directory.len(), + hash_kind, + hash_value.as_ptr().cast(), + hash_value.len(), + ) } } } +pub fn unknown_file_metadata<'ll>(cx: &CodegenCx<'ll, '_>) -> &'ll DIFile { + debug_context(cx).created_files.borrow_mut().entry(None).or_insert_with(|| unsafe { + let file_name = "<unknown>"; + let directory = ""; + let hash_value = ""; + + llvm::LLVMRustDIBuilderCreateFile( + DIB(cx), + file_name.as_ptr().cast(), + file_name.len(), + directory.as_ptr().cast(), + directory.len(), + llvm::ChecksumKind::None, + hash_value.as_ptr().cast(), + hash_value.len(), + ) + }) +} + trait MsvcBasicName { fn msvc_basic_name(self) -> &'static str; } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 6a164557a47..0910e7c94ea 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -31,7 +31,7 @@ use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TypeFoldable}; use rustc_session::config::{self, DebugInfo}; use rustc_session::Session; use rustc_span::symbol::Symbol; -use rustc_span::{self, BytePos, Pos, SourceFile, SourceFileAndLine, Span}; +use rustc_span::{self, BytePos, Pos, SourceFile, SourceFileAndLine, SourceFileHash, Span}; use rustc_target::abi::Size; use libc::c_uint; @@ -61,7 +61,7 @@ pub struct CodegenUnitDebugContext<'ll, 'tcx> { llcontext: &'ll llvm::Context, llmod: &'ll llvm::Module, builder: &'ll mut DIBuilder<'ll>, - created_files: RefCell<FxHashMap<(Option<String>, Option<String>), &'ll DIFile>>, + created_files: RefCell<FxHashMap<Option<(u128, SourceFileHash)>, &'ll DIFile>>, type_map: metadata::TypeMap<'ll, 'tcx>, namespace_map: RefCell<DefIdMap<&'ll DIScope>>, diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index cf9cf1b70aa..4407297c943 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -816,6 +816,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( span: Span, ) -> Result<&'ll Value, ()> { // macros for error handling: + #[cfg_attr(not(bootstrap), allow(unused_macro_rules))] macro_rules! emit_error { ($msg: tt) => { emit_error!($msg, ) @@ -1144,6 +1145,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( span: Span, args: &[OperandRef<'tcx, &'ll Value>], ) -> Result<&'ll Value, ()> { + #[cfg_attr(not(bootstrap), allow(unused_macro_rules))] macro_rules! emit_error { ($msg: tt) => { emit_error!($msg, ) diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 0bead4629a6..2b5154a2cf9 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -5,7 +5,6 @@ //! This API is completely unstable and subject to change. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![feature(crate_visibility_modifier)] #![feature(let_chains)] #![feature(let_else)] #![feature(extern_types)] diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 13baaddccd4..37409dbb447 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -775,7 +775,7 @@ pub mod coverageinfo { } impl CounterMappingRegion { - crate fn code_region( + pub(crate) fn code_region( counter: coverage_map::Counter, file_id: u32, start_line: u32, @@ -799,7 +799,7 @@ pub mod coverageinfo { // This function might be used in the future; the LLVM API is still evolving, as is coverage // support. #[allow(dead_code)] - crate fn branch_region( + pub(crate) fn branch_region( counter: coverage_map::Counter, false_counter: coverage_map::Counter, file_id: u32, @@ -824,7 +824,7 @@ pub mod coverageinfo { // This function might be used in the future; the LLVM API is still evolving, as is coverage // support. #[allow(dead_code)] - crate fn expansion_region( + pub(crate) fn expansion_region( file_id: u32, expanded_file_id: u32, start_line: u32, @@ -848,7 +848,7 @@ pub mod coverageinfo { // This function might be used in the future; the LLVM API is still evolving, as is coverage // support. #[allow(dead_code)] - crate fn skipped_region( + pub(crate) fn skipped_region( file_id: u32, start_line: u32, start_col: u32, @@ -871,7 +871,7 @@ pub mod coverageinfo { // This function might be used in the future; the LLVM API is still evolving, as is coverage // support. #[allow(dead_code)] - crate fn gap_region( + pub(crate) fn gap_region( counter: coverage_map::Counter, file_id: u32, start_line: u32, diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index 21b77f7dea6..cf2d3c423c3 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -39,33 +39,33 @@ impl fmt::Debug for Type { } impl<'ll> CodegenCx<'ll, '_> { - crate fn type_named_struct(&self, name: &str) -> &'ll Type { + pub(crate) fn type_named_struct(&self, name: &str) -> &'ll Type { let name = SmallCStr::new(name); unsafe { llvm::LLVMStructCreateNamed(self.llcx, name.as_ptr()) } } - crate fn set_struct_body(&self, ty: &'ll Type, els: &[&'ll Type], packed: bool) { + pub(crate) fn set_struct_body(&self, ty: &'ll Type, els: &[&'ll Type], packed: bool) { unsafe { llvm::LLVMStructSetBody(ty, els.as_ptr(), els.len() as c_uint, packed as Bool) } } - crate fn type_void(&self) -> &'ll Type { + pub(crate) fn type_void(&self) -> &'ll Type { unsafe { llvm::LLVMVoidTypeInContext(self.llcx) } } - crate fn type_metadata(&self) -> &'ll Type { + pub(crate) fn type_metadata(&self) -> &'ll Type { unsafe { llvm::LLVMRustMetadataTypeInContext(self.llcx) } } ///x Creates an integer type with the given number of bits, e.g., i24 - crate fn type_ix(&self, num_bits: u64) -> &'ll Type { + pub(crate) fn type_ix(&self, num_bits: u64) -> &'ll Type { unsafe { llvm::LLVMIntTypeInContext(self.llcx, num_bits as c_uint) } } - crate fn type_vector(&self, ty: &'ll Type, len: u64) -> &'ll Type { + pub(crate) fn type_vector(&self, ty: &'ll Type, len: u64) -> &'ll Type { unsafe { llvm::LLVMVectorType(ty, len as c_uint) } } - crate fn func_params_types(&self, ty: &'ll Type) -> Vec<&'ll Type> { + pub(crate) fn func_params_types(&self, ty: &'ll Type) -> Vec<&'ll Type> { unsafe { let n_args = llvm::LLVMCountParamTypes(ty) as usize; let mut args = Vec::with_capacity(n_args); @@ -75,11 +75,11 @@ impl<'ll> CodegenCx<'ll, '_> { } } - crate fn type_bool(&self) -> &'ll Type { + pub(crate) fn type_bool(&self) -> &'ll Type { self.type_i8() } - crate fn type_int_from_ty(&self, t: ty::IntTy) -> &'ll Type { + pub(crate) fn type_int_from_ty(&self, t: ty::IntTy) -> &'ll Type { match t { ty::IntTy::Isize => self.type_isize(), ty::IntTy::I8 => self.type_i8(), @@ -90,7 +90,7 @@ impl<'ll> CodegenCx<'ll, '_> { } } - crate fn type_uint_from_ty(&self, t: ty::UintTy) -> &'ll Type { + pub(crate) fn type_uint_from_ty(&self, t: ty::UintTy) -> &'ll Type { match t { ty::UintTy::Usize => self.type_isize(), ty::UintTy::U8 => self.type_i8(), @@ -101,14 +101,14 @@ impl<'ll> CodegenCx<'ll, '_> { } } - crate fn type_float_from_ty(&self, t: ty::FloatTy) -> &'ll Type { + pub(crate) fn type_float_from_ty(&self, t: ty::FloatTy) -> &'ll Type { match t { ty::FloatTy::F32 => self.type_f32(), ty::FloatTy::F64 => self.type_f64(), } } - crate fn type_pointee_for_align(&self, align: Align) -> &'ll Type { + pub(crate) fn type_pointee_for_align(&self, align: Align) -> &'ll Type { // FIXME(eddyb) We could find a better approximation if ity.align < align. let ity = Integer::approximate_align(self, align); self.type_from_integer(ity) @@ -116,7 +116,7 @@ impl<'ll> CodegenCx<'ll, '_> { /// Return a LLVM type that has at most the required alignment, /// and exactly the required size, as a best-effort padding array. - crate fn type_padding_filler(&self, size: Size, align: Align) -> &'ll Type { + pub(crate) fn type_padding_filler(&self, size: Size, align: Align) -> &'ll Type { let unit = Integer::approximate_align(self, align); let size = size.bytes(); let unit_size = unit.size().bytes(); @@ -124,11 +124,11 @@ impl<'ll> CodegenCx<'ll, '_> { self.type_array(self.type_from_integer(unit), size / unit_size) } - crate fn type_variadic_func(&self, args: &[&'ll Type], ret: &'ll Type) -> &'ll Type { + pub(crate) fn type_variadic_func(&self, args: &[&'ll Type], ret: &'ll Type) -> &'ll Type { unsafe { llvm::LLVMFunctionType(ret, args.as_ptr(), args.len() as c_uint, True) } } - crate fn type_array(&self, ty: &'ll Type, len: u64) -> &'ll Type { + pub(crate) fn type_array(&self, ty: &'ll Type, len: u64) -> &'ll Type { unsafe { llvm::LLVMRustArrayType(ty, len) } } } diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 87d0680bf6f..93b10a07e44 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -41,6 +41,6 @@ rustc_target = { path = "../rustc_target" } rustc_session = { path = "../rustc_session" } [dependencies.object] -version = "0.28.0" +version = "0.28.4" default-features = false features = ["read_core", "elf", "macho", "pe", "unaligned", "archive", "write"] diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 2e422728056..6aa96f9f403 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -20,7 +20,7 @@ use rustc_metadata::EncodedMetadata; use rustc_session::cstore::MetadataLoader; use rustc_session::Session; use rustc_target::abi::Endian; -use rustc_target::spec::Target; +use rustc_target::spec::{RelocModel, Target}; use crate::METADATA_FILENAME; @@ -132,15 +132,23 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static let mut file = write::Object::new(binary_format, architecture, endianness); match architecture { Architecture::Mips => { - // copied from `mipsel-linux-gnu-gcc foo.c -c` and - // inspecting the resulting `e_flags` field. - let e_flags = elf::EF_MIPS_CPIC - | elf::EF_MIPS_PIC - | if sess.target.options.cpu.contains("r6") { - elf::EF_MIPS_ARCH_32R6 | elf::EF_MIPS_NAN2008 - } else { - elf::EF_MIPS_ARCH_32R2 - }; + let arch = match sess.target.options.cpu.as_ref() { + "mips1" => elf::EF_MIPS_ARCH_1, + "mips2" => elf::EF_MIPS_ARCH_2, + "mips3" => elf::EF_MIPS_ARCH_3, + "mips4" => elf::EF_MIPS_ARCH_4, + "mips5" => elf::EF_MIPS_ARCH_5, + s if s.contains("r6") => elf::EF_MIPS_ARCH_32R6, + _ => elf::EF_MIPS_ARCH_32R2, + }; + // The only ABI LLVM supports for 32-bit MIPS CPUs is o32. + let mut e_flags = elf::EF_MIPS_CPIC | elf::EF_MIPS_ABI_O32 | arch; + if sess.target.options.relocation_model != RelocModel::Static { + e_flags |= elf::EF_MIPS_PIC; + } + if sess.target.options.cpu.contains("r6") { + e_flags |= elf::EF_MIPS_NAN2008; + } file.flags = FileFlags::Elf { e_flags }; } Architecture::Mips64 => { diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 7b7e09208a2..d11f1534153 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -15,8 +15,9 @@ use rustc_attr as attr; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry}; +use rustc_data_structures::sync::par_iter; #[cfg(parallel_compiler)] -use rustc_data_structures::sync::{par_iter, ParallelIterator}; +use rustc_data_structures::sync::ParallelIterator; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::lang_items::LangItem; @@ -607,6 +608,14 @@ pub fn codegen_crate<B: ExtraBackendMethods>( second_half.iter().rev().interleave(first_half).copied().collect() }; + // Calculate the CGU reuse + let cgu_reuse = tcx.sess.time("find_cgu_reuse", || { + codegen_units.iter().map(|cgu| determine_cgu_reuse(tcx, &cgu)).collect::<Vec<_>>() + }); + + let mut total_codegen_time = Duration::new(0, 0); + let start_rss = tcx.sess.time_passes().then(|| get_resident_set_size()); + // The non-parallel compiler can only translate codegen units to LLVM IR // on a single thread, leading to a staircase effect where the N LLVM // threads have to wait on the single codegen threads to generate work @@ -617,8 +626,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>( // This likely is a temporary measure. Once we don't have to support the // non-parallel compiler anymore, we can compile CGUs end-to-end in // parallel and get rid of the complicated scheduling logic. - #[cfg(parallel_compiler)] - let pre_compile_cgus = |cgu_reuse: &[CguReuse]| { + let mut pre_compiled_cgus = if cfg!(parallel_compiler) { tcx.sess.time("compile_first_CGU_batch", || { // Try to find one CGU to compile per thread. let cgus: Vec<_> = cgu_reuse @@ -638,48 +646,31 @@ pub fn codegen_crate<B: ExtraBackendMethods>( }) .collect(); - (pre_compiled_cgus, start_time.elapsed()) + total_codegen_time += start_time.elapsed(); + + pre_compiled_cgus }) + } else { + FxHashMap::default() }; - #[cfg(not(parallel_compiler))] - let pre_compile_cgus = |_: &[CguReuse]| (FxHashMap::default(), Duration::new(0, 0)); - - let mut cgu_reuse = Vec::new(); - let mut pre_compiled_cgus: Option<FxHashMap<usize, _>> = None; - let mut total_codegen_time = Duration::new(0, 0); - let start_rss = tcx.sess.time_passes().then(|| get_resident_set_size()); - for (i, cgu) in codegen_units.iter().enumerate() { ongoing_codegen.wait_for_signal_to_codegen_item(); ongoing_codegen.check_for_errors(tcx.sess); - // Do some setup work in the first iteration - if pre_compiled_cgus.is_none() { - // Calculate the CGU reuse - cgu_reuse = tcx.sess.time("find_cgu_reuse", || { - codegen_units.iter().map(|cgu| determine_cgu_reuse(tcx, &cgu)).collect() - }); - // Pre compile some CGUs - let (compiled_cgus, codegen_time) = pre_compile_cgus(&cgu_reuse); - pre_compiled_cgus = Some(compiled_cgus); - total_codegen_time += codegen_time; - } - let cgu_reuse = cgu_reuse[i]; tcx.sess.cgu_reuse_tracker.set_actual_reuse(cgu.name().as_str(), cgu_reuse); match cgu_reuse { CguReuse::No => { - let (module, cost) = - if let Some(cgu) = pre_compiled_cgus.as_mut().unwrap().remove(&i) { - cgu - } else { - let start_time = Instant::now(); - let module = backend.compile_codegen_unit(tcx, cgu.name()); - total_codegen_time += start_time.elapsed(); - module - }; + let (module, cost) = if let Some(cgu) = pre_compiled_cgus.remove(&i) { + cgu + } else { + let start_time = Instant::now(); + let module = backend.compile_codegen_unit(tcx, cgu.name()); + total_codegen_time += start_time.elapsed(); + module + }; // This will unwind if there are errors, which triggers our `AbortCodegenOnDrop` // guard. Unfortunately, just skipping the `submit_codegened_module_to_llvm` makes // compilation hang on post-monomorphization errors. diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index fa39e8dd247..80dab115fac 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -328,7 +328,7 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, CleanupKi bb, data, result[bb], funclet ); - for &succ in data.terminator().successors() { + for succ in data.terminator().successors() { let kind = result[succ]; debug!("cleanup_kinds: propagating {:?} to {:?}/{:?}", funclet, succ, kind); match kind { diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index f15c469ae57..6d6d3ae01f4 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -555,21 +555,28 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - sym::ptr_offset_from => { + sym::ptr_offset_from | sym::ptr_offset_from_unsigned => { let ty = substs.type_at(0); let pointee_size = bx.layout_of(ty).size; - // This is the same sequence that Clang emits for pointer subtraction. - // It can be neither `nsw` nor `nuw` because the input is treated as - // unsigned but then the output is treated as signed, so neither works. let a = args[0].immediate(); let b = args[1].immediate(); let a = bx.ptrtoint(a, bx.type_isize()); let b = bx.ptrtoint(b, bx.type_isize()); - let d = bx.sub(a, b); let pointee_size = bx.const_usize(pointee_size.bytes()); - // this is where the signed magic happens (notice the `s` in `exactsdiv`) - bx.exactsdiv(d, pointee_size) + if name == sym::ptr_offset_from { + // This is the same sequence that Clang emits for pointer subtraction. + // It can be neither `nsw` nor `nuw` because the input is treated as + // unsigned but then the output is treated as signed, so neither works. + let d = bx.sub(a, b); + // this is where the signed magic happens (notice the `s` in `exactsdiv`) + bx.exactsdiv(d, pointee_size) + } else { + // The `_unsigned` version knows the relative ordering of the pointers, + // so can use `sub nuw` and `udiv exact` instead of dealing in signed. + let d = bx.unchecked_usub(a, b); + bx.exactudiv(d, pointee_size) + } } _ => { 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 38fecf7232e..d7cbc48e032 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -13,7 +13,7 @@ use rustc_middle::mir::pretty::display_allocation; use rustc_middle::traits::Reveal; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, subst::Subst, TyCtxt}; +use rustc_middle::ty::{self, subst::Subst, EarlyBinder, TyCtxt}; use rustc_span::source_map::Span; use rustc_target::abi::{self, Abi}; use std::borrow::Cow; @@ -47,7 +47,7 @@ fn eval_body_using_ecx<'mir, 'tcx>( "Unexpected DefKind: {:?}", ecx.tcx.def_kind(cid.instance.def_id()) ); - let layout = ecx.layout_of(body.return_ty().subst(tcx, cid.instance.substs))?; + let layout = ecx.layout_of(EarlyBinder(body.return_ty()).subst(tcx, cid.instance.substs))?; assert!(!layout.is_unsized()); let ret = ecx.allocate(layout, MemoryKind::Stack)?; @@ -135,7 +135,7 @@ pub(super) fn op_to_const<'tcx>( } else { // It is guaranteed that any non-slice scalar pair is actually ByRef here. // When we come back from raw const eval, we are always by-ref. The only way our op here is - // by-val is if we are in destructure_const, i.e., if this is (a field of) something that we + // by-val is if we are in destructure_mir_constant, i.e., if this is (a field of) something that we // "tried to make immediate" before. We wouldn't do that for non-slice scalar pairs or // structs containing such. op.try_as_mplace() 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 1f291db55be..d6f62062d1f 100644 --- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs @@ -4,13 +4,12 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{DefIdTree, TyCtxt}; use rustc_span::symbol::Symbol; -use rustc_target::spec::abi::Abi; /// Whether the `def_id` is an unstable const fn and what feature gate is necessary to enable it pub fn is_unstable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Symbol> { if tcx.is_const_fn_raw(def_id) { let const_stab = tcx.lookup_const_stability(def_id)?; - if const_stab.level.is_unstable() { Some(const_stab.feature) } else { None } + if const_stab.is_const_unstable() { Some(const_stab.feature) } else { None } } else { None } @@ -34,10 +33,7 @@ fn impl_constness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::Constness { hir::Node::ForeignItem(hir::ForeignItem { kind: hir::ForeignItemKind::Fn(..), .. }) => { // Intrinsics use `rustc_const_{un,}stable` attributes to indicate constness. All other // foreign items cannot be evaluated at compile-time. - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - let is_const = if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = - tcx.hir().get_foreign_abi(hir_id) - { + let is_const = if tcx.is_intrinsic(def_id) { tcx.lookup_const_stability(def_id).is_some() } else { false diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index d57504deeab..9e5b00462f3 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -18,7 +18,7 @@ use rustc_target::spec::abi::Abi; use crate::interpret::{ self, compile_time_machine, AllocId, ConstAllocation, Frame, ImmTy, InterpCx, InterpResult, - OpTy, PlaceTy, Scalar, StackPopUnwind, + OpTy, PlaceTy, Pointer, Scalar, StackPopUnwind, }; use super::error::*; @@ -163,7 +163,7 @@ impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxHashMap<K, V> { } } -crate type CompileTimeEvalContext<'mir, 'tcx> = +pub(crate) type CompileTimeEvalContext<'mir, 'tcx> = InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>; #[derive(Debug, PartialEq, Eq, Copy, Clone)] @@ -444,6 +444,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, } #[inline(always)] + fn expose_ptr( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _ptr: Pointer<AllocId>, + ) -> InterpResult<'tcx> { + Err(ConstEvalErrKind::NeedsRfc("exposing pointers".to_string()).into()) + } + + #[inline(always)] fn init_frame_extra( ecx: &mut InterpCx<'mir, 'tcx, Self>, frame: Frame<'mir, 'tcx>, diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index 96c18d488ee..db43f7a425c 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -4,6 +4,7 @@ use std::convert::TryFrom; use rustc_hir::Mutability; use rustc_middle::mir; +use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId}; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::{source_map::DUMMY_SP, symbol::Symbol}; @@ -22,7 +23,7 @@ pub use error::*; pub use eval_queries::*; pub use fn_queries::*; pub use machine::*; -pub(crate) use valtrees::{const_to_valtree, valtree_to_const_value}; +pub(crate) use valtrees::{const_to_valtree_inner, valtree_to_const_value}; pub(crate) fn const_caller_location( tcx: TyCtxt<'_>, @@ -38,6 +39,57 @@ pub(crate) fn const_caller_location( ConstValue::Scalar(Scalar::from_maybe_pointer(loc_place.ptr, &tcx)) } +// We forbid type-level constants that contain more than `VALTREE_MAX_NODES` nodes. +const VALTREE_MAX_NODES: usize = 1000; + +pub(crate) enum ValTreeCreationError { + NodesOverflow, + NonSupportedType, + Other, +} +pub(crate) type ValTreeCreationResult<'tcx> = Result<ty::ValTree<'tcx>, ValTreeCreationError>; + +/// Evaluates a constant and turns it into a type-level constant value. +pub(crate) fn eval_to_valtree<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + cid: GlobalId<'tcx>, +) -> EvalToValTreeResult<'tcx> { + let const_alloc = tcx.eval_to_allocation_raw(param_env.and(cid))?; + let ecx = mk_eval_cx( + tcx, DUMMY_SP, param_env, + // It is absolutely crucial for soundness that + // we do not read from static items or other mutable memory. + false, + ); + let place = ecx.raw_const_to_mplace(const_alloc).unwrap(); + debug!(?place); + + let mut num_nodes = 0; + let valtree_result = const_to_valtree_inner(&ecx, &place, &mut num_nodes); + + match valtree_result { + Ok(valtree) => Ok(Some(valtree)), + Err(err) => { + let did = cid.instance.def_id(); + let s = cid.display(tcx); + match err { + ValTreeCreationError::NodesOverflow => { + let msg = format!("maximum number of nodes exceeded in constant {}", &s); + let mut diag = match tcx.hir().span_if_local(did) { + Some(span) => tcx.sess.struct_span_err(span, &msg), + None => tcx.sess.struct_err(&msg), + }; + diag.emit(); + + Ok(None) + } + ValTreeCreationError::NonSupportedType | ValTreeCreationError::Other => Ok(None), + } + } + } +} + /// This function should never fail for validated constants. However, it is also invoked from the /// pretty printer which might attempt to format invalid constants and in that case it might fail. pub(crate) fn try_destructure_const<'tcx>( @@ -48,7 +100,6 @@ pub(crate) fn try_destructure_const<'tcx>( trace!("destructure_const: {:?}", val); let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false); let op = ecx.const_to_op(val, None)?; - // We go to `usize` as we cannot allocate anything bigger anyway. let (field_count, variant, down) = match val.ty().kind() { ty::Array(_, len) => (usize::try_from(len.eval_usize(tcx, param_env)).unwrap(), None, op), @@ -64,7 +115,6 @@ pub(crate) fn try_destructure_const<'tcx>( ty::Tuple(substs) => (substs.len(), None, op), _ => bug!("cannot destructure constant {:?}", val), }; - let fields = (0..field_count) .map(|i| { let field_op = ecx.operand_field(&down, i)?; @@ -73,11 +123,47 @@ pub(crate) fn try_destructure_const<'tcx>( }) .collect::<InterpResult<'tcx, Vec<_>>>()?; let fields = tcx.arena.alloc_from_iter(fields); - Ok(mir::DestructuredConst { variant, fields }) } #[instrument(skip(tcx), level = "debug")] +pub(crate) fn try_destructure_mir_constant<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + val: mir::ConstantKind<'tcx>, +) -> InterpResult<'tcx, mir::DestructuredMirConstant<'tcx>> { + trace!("destructure_mir_constant: {:?}", val); + let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false); + let op = ecx.mir_const_to_op(&val, None)?; + + // We go to `usize` as we cannot allocate anything bigger anyway. + let (field_count, variant, down) = match val.ty().kind() { + ty::Array(_, len) => (len.eval_usize(tcx, param_env) as usize, None, op), + ty::Adt(def, _) if def.variants().is_empty() => { + throw_ub!(Unreachable) + } + ty::Adt(def, _) => { + let variant = ecx.read_discriminant(&op).unwrap().1; + let down = ecx.operand_downcast(&op, variant).unwrap(); + (def.variants()[variant].fields.len(), Some(variant), down) + } + ty::Tuple(substs) => (substs.len(), None, op), + _ => bug!("cannot destructure mir constant {:?}", val), + }; + + let fields_iter = (0..field_count) + .map(|i| { + let field_op = ecx.operand_field(&down, i)?; + let val = op_to_const(&ecx, &field_op); + Ok(mir::ConstantKind::Val(val, field_op.layout.ty)) + }) + .collect::<InterpResult<'tcx, Vec<_>>>()?; + let fields = tcx.arena.alloc_from_iter(fields_iter); + + Ok(mir::DestructuredMirConstant { variant, fields }) +} + +#[instrument(skip(tcx), level = "debug")] pub(crate) fn deref_const<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -113,3 +199,39 @@ pub(crate) fn deref_const<'tcx>( tcx.mk_const(ty::ConstS { val: ty::ConstKind::Value(op_to_const(&ecx, &mplace.into())), ty }) } + +#[instrument(skip(tcx), level = "debug")] +pub(crate) fn deref_mir_constant<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + val: mir::ConstantKind<'tcx>, +) -> mir::ConstantKind<'tcx> { + let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false); + let op = ecx.mir_const_to_op(&val, None).unwrap(); + let mplace = ecx.deref_operand(&op).unwrap(); + if let Some(alloc_id) = mplace.ptr.provenance { + assert_eq!( + tcx.get_global_alloc(alloc_id).unwrap().unwrap_memory().0.0.mutability, + Mutability::Not, + "deref_const cannot be used with mutable allocations as \ + that could allow pattern matching to observe mutable statics", + ); + } + + let ty = match mplace.meta { + MemPlaceMeta::None => mplace.layout.ty, + MemPlaceMeta::Poison => bug!("poison metadata in `deref_const`: {:#?}", mplace), + // In case of unsized types, figure out the real type behind. + MemPlaceMeta::Meta(scalar) => match mplace.layout.ty.kind() { + ty::Str => bug!("there's no sized equivalent of a `str`"), + ty::Slice(elem_ty) => tcx.mk_array(*elem_ty, scalar.to_machine_usize(&tcx).unwrap()), + _ => bug!( + "type {} should not have metadata, but had {:?}", + mplace.layout.ty, + mplace.meta + ), + }, + }; + + mir::ConstantKind::Val(op_to_const(&ecx, &mplace.into()), ty) +} diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 374179d0cc2..7346d69bb5d 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -1,33 +1,16 @@ use super::eval_queries::{mk_eval_cx, op_to_const}; use super::machine::CompileTimeEvalContext; +use super::{ValTreeCreationError, ValTreeCreationResult, VALTREE_MAX_NODES}; use crate::interpret::{ intern_const_alloc_recursive, ConstValue, ImmTy, Immediate, InternKind, MemPlaceMeta, MemoryKind, PlaceTy, Scalar, ScalarMaybeUninit, }; -use rustc_middle::mir::interpret::ConstAlloc; -use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt}; use rustc_span::source_map::DUMMY_SP; use rustc_target::abi::{Align, VariantIdx}; use crate::interpret::MPlaceTy; use crate::interpret::Value; - -/// Convert an evaluated constant to a type level constant -#[instrument(skip(tcx), level = "debug")] -pub(crate) fn const_to_valtree<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - raw: ConstAlloc<'tcx>, -) -> Option<ty::ValTree<'tcx>> { - let ecx = mk_eval_cx( - tcx, DUMMY_SP, param_env, - // It is absolutely crucial for soundness that - // we do not read from static items or other mutable memory. - false, - ); - let place = ecx.raw_const_to_mplace(raw).unwrap(); - const_to_valtree_inner(&ecx, &place) -} +use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt}; #[instrument(skip(ecx), level = "debug")] fn branches<'tcx>( @@ -35,7 +18,8 @@ fn branches<'tcx>( place: &MPlaceTy<'tcx>, n: usize, variant: Option<VariantIdx>, -) -> Option<ty::ValTree<'tcx>> { + num_nodes: &mut usize, +) -> ValTreeCreationResult<'tcx> { let place = match variant { Some(variant) => ecx.mplace_downcast(&place, variant).unwrap(), None => *place, @@ -43,80 +27,116 @@ fn branches<'tcx>( let variant = variant.map(|variant| Some(ty::ValTree::Leaf(ScalarInt::from(variant.as_u32())))); debug!(?place, ?variant); - let fields = (0..n).map(|i| { + let mut fields = Vec::with_capacity(n); + for i in 0..n { let field = ecx.mplace_field(&place, i).unwrap(); - const_to_valtree_inner(ecx, &field) - }); - // For enums, we preped their variant index before the variant's fields so we can figure out + let valtree = const_to_valtree_inner(ecx, &field, num_nodes)?; + fields.push(Some(valtree)); + } + + // For enums, we prepend their variant index before the variant's fields so we can figure out // the variant again when just seeing a valtree. - let branches = variant.into_iter().chain(fields); - Some(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(branches.collect::<Option<Vec<_>>>()?))) + let branches = variant + .into_iter() + .chain(fields.into_iter()) + .collect::<Option<Vec<_>>>() + .expect("should have already checked for errors in ValTree creation"); + + // Have to account for ZSTs here + if branches.len() == 0 { + *num_nodes += 1; + } + + Ok(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(branches))) } #[instrument(skip(ecx), level = "debug")] fn slice_branches<'tcx>( ecx: &CompileTimeEvalContext<'tcx, 'tcx>, place: &MPlaceTy<'tcx>, -) -> Option<ty::ValTree<'tcx>> { - let n = place.len(&ecx.tcx.tcx).expect(&format!("expected to use len of place {:?}", place)); - let branches = (0..n).map(|i| { + num_nodes: &mut usize, +) -> ValTreeCreationResult<'tcx> { + let n = place + .len(&ecx.tcx.tcx) + .unwrap_or_else(|_| panic!("expected to use len of place {:?}", place)); + + let mut elems = Vec::with_capacity(n as usize); + for i in 0..n { let place_elem = ecx.mplace_index(place, i).unwrap(); - const_to_valtree_inner(ecx, &place_elem) - }); + let valtree = const_to_valtree_inner(ecx, &place_elem, num_nodes)?; + elems.push(valtree); + } - Some(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(branches.collect::<Option<Vec<_>>>()?))) + Ok(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(elems))) } #[instrument(skip(ecx), level = "debug")] -fn const_to_valtree_inner<'tcx>( +pub(crate) fn const_to_valtree_inner<'tcx>( ecx: &CompileTimeEvalContext<'tcx, 'tcx>, place: &MPlaceTy<'tcx>, -) -> Option<ty::ValTree<'tcx>> { - match place.layout.ty.kind() { - ty::FnDef(..) => Some(ty::ValTree::zst()), + num_nodes: &mut usize, +) -> ValTreeCreationResult<'tcx> { + if *num_nodes >= VALTREE_MAX_NODES { + return Err(ValTreeCreationError::NodesOverflow); + } + + let ty = place.layout.ty; + debug!("ty kind: {:?}", ty.kind()); + + match ty.kind() { + ty::FnDef(..) => { + *num_nodes += 1; + Ok(ty::ValTree::zst()) + } ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => { - let val = ecx.read_immediate(&place.into()).unwrap(); + let Ok(val) = ecx.read_immediate(&place.into()) else { + return Err(ValTreeCreationError::Other); + }; let val = val.to_scalar().unwrap(); - Some(ty::ValTree::Leaf(val.assert_int())) + *num_nodes += 1; + + Ok(ty::ValTree::Leaf(val.assert_int())) } // Raw pointers are not allowed in type level constants, as we cannot properly test them for // equality at compile-time (see `ptr_guaranteed_eq`/`_ne`). // Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to // agree with runtime equality tests. - ty::FnPtr(_) | ty::RawPtr(_) => None, + ty::FnPtr(_) | ty::RawPtr(_) => Err(ValTreeCreationError::NonSupportedType), ty::Ref(_, _, _) => { - let derefd_place = ecx.deref_operand(&place.into()).unwrap_or_else(|e| bug!("couldn't deref {:?}, error: {:?}", place, e)); + let Ok(derefd_place)= ecx.deref_operand(&place.into()) else { + return Err(ValTreeCreationError::Other); + }; debug!(?derefd_place); - const_to_valtree_inner(ecx, &derefd_place) + const_to_valtree_inner(ecx, &derefd_place, num_nodes) } ty::Str | ty::Slice(_) | ty::Array(_, _) => { - let valtree = slice_branches(ecx, place); - debug!(?valtree); - - valtree + slice_branches(ecx, place, num_nodes) } // Trait objects are not allowed in type level constants, as we have no concept for // resolving their backing type, even if we can do that at const eval time. We may // hypothetically be able to allow `dyn StructuralEq` trait objects in the future, // but it is unclear if this is useful. - ty::Dynamic(..) => None, + ty::Dynamic(..) => Err(ValTreeCreationError::NonSupportedType), - ty::Tuple(substs) => branches(ecx, place, substs.len(), None), + ty::Tuple(elem_tys) => { + branches(ecx, place, elem_tys.len(), None, num_nodes) + } ty::Adt(def, _) => { if def.is_union() { - return None + return Err(ValTreeCreationError::NonSupportedType); } else if def.variants().is_empty() { bug!("uninhabited types should have errored and never gotten converted to valtree") } - let variant = ecx.read_discriminant(&place.into()).unwrap().1; - - branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant)) + let Ok((_, variant)) = ecx.read_discriminant(&place.into()) else { + return Err(ValTreeCreationError::Other); + }; + branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant), num_nodes) } ty::Never @@ -134,7 +154,7 @@ fn const_to_valtree_inner<'tcx>( // FIXME(oli-obk): we can probably encode closures just like structs | ty::Closure(..) | ty::Generator(..) - | ty::GeneratorWitness(..) => None, + | ty::GeneratorWitness(..) => Err(ValTreeCreationError::NonSupportedType), } } @@ -223,8 +243,11 @@ fn create_pointee_place<'tcx>( .unwrap(); debug!(?ptr); - let mut place = MPlaceTy::from_aligned_ptr(ptr.into(), layout); - place.meta = MemPlaceMeta::Meta(Scalar::from_u64(num_elems as u64)); + let place = MPlaceTy::from_aligned_ptr_with_meta( + ptr.into(), + layout, + MemPlaceMeta::Meta(Scalar::from_u64(num_elems as u64)), + ); debug!(?place); place @@ -235,11 +258,11 @@ fn create_pointee_place<'tcx>( /// Converts a `ValTree` to a `ConstValue`, which is needed after mir /// construction has finished. -// FIXME Merge `valtree_to_const_value` and `fill_place_recursively` into one function +// FIXME Merge `valtree_to_const_value` and `valtree_into_mplace` into one function #[instrument(skip(tcx), level = "debug")] pub fn valtree_to_const_value<'tcx>( tcx: TyCtxt<'tcx>, - param_env_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, + ty: Ty<'tcx>, valtree: ty::ValTree<'tcx>, ) -> ConstValue<'tcx> { // Basic idea: We directly construct `Scalar` values from trivial `ValTree`s @@ -249,8 +272,8 @@ pub fn valtree_to_const_value<'tcx>( // create inner `MPlace`s which are filled recursively. // FIXME Does this need an example? - let (param_env, ty) = param_env_ty.into_parts(); - let mut ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false); + let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::empty(), false); + let param_env_ty = ty::ParamEnv::empty().and(ty); match ty.kind() { ty::FnDef(..) => { @@ -273,7 +296,7 @@ pub fn valtree_to_const_value<'tcx>( }; debug!(?place); - fill_place_recursively(&mut ecx, &mut place, valtree); + valtree_into_mplace(&mut ecx, &mut place, valtree); dump_place(&ecx, place.into()); intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &place).unwrap(); @@ -315,7 +338,7 @@ pub fn valtree_to_const_value<'tcx>( // FIXME Needs a better/correct name #[instrument(skip(ecx), level = "debug")] -fn fill_place_recursively<'tcx>( +fn valtree_into_mplace<'tcx>( ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>, place: &mut MPlaceTy<'tcx>, valtree: ty::ValTree<'tcx>, @@ -347,7 +370,7 @@ fn fill_place_recursively<'tcx>( let mut pointee_place = create_pointee_place(ecx, *inner_ty, valtree); debug!(?pointee_place); - fill_place_recursively(ecx, &mut pointee_place, valtree); + valtree_into_mplace(ecx, &mut pointee_place, valtree); dump_place(ecx, pointee_place.into()); intern_const_alloc_recursive(ecx, InternKind::Constant, &pointee_place).unwrap(); @@ -435,7 +458,7 @@ fn fill_place_recursively<'tcx>( }; debug!(?place_inner); - fill_place_recursively(ecx, &mut place_inner, *inner_valtree); + valtree_into_mplace(ecx, &mut place_inner, *inner_valtree); dump_place(&ecx, place_inner.into()); } diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 3ea3729dbcd..7cd2ba34b04 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -98,7 +98,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } pub fn misc_cast( - &self, + &mut self, src: &ImmTy<'tcx, M::PointerTag>, cast_ty: Ty<'tcx>, ) -> InterpResult<'tcx, Immediate<M::PointerTag>> { @@ -139,7 +139,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if let Some(discr) = src.layout.ty.discriminant_for_variant(*self.tcx, index) { assert!(src.layout.is_zst()); let discr_layout = self.layout_of(discr.ty)?; - return Ok(self.cast_from_int_like(discr.val, discr_layout, cast_ty).into()); + + let scalar = Scalar::from_uint(discr.val, discr_layout.layout.size()); + return Ok(self.cast_from_int_like(scalar, discr_layout, cast_ty)?.into()); } } Variants::Multiple { .. } => {} @@ -170,38 +172,62 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } // # The remaining source values are scalar and "int-like". + let scalar = src.to_scalar()?; + + // If we are casting from a pointer to something + // that is not a pointer, mark the pointer as exposed + if src.layout.ty.is_any_ptr() && !cast_ty.is_any_ptr() { + let ptr = self.scalar_to_ptr(scalar)?; + + match ptr.into_pointer_or_addr() { + Ok(ptr) => { + M::expose_ptr(self, ptr)?; + } + Err(_) => { + // do nothing, exposing an invalid pointer + // has no meaning + } + }; + } - // For all remaining casts, we either - // (a) cast a raw ptr to usize, or - // (b) cast from an integer-like (including bool, char, enums). - // In both cases we want the bits. - let bits = src.to_scalar()?.to_bits(src.layout.size)?; - Ok(self.cast_from_int_like(bits, src.layout, cast_ty).into()) + Ok(self.cast_from_int_like(scalar, src.layout, cast_ty)?.into()) } - fn cast_from_int_like( + pub fn cast_from_int_like( &self, - v: u128, // raw bits (there is no ScalarTy so we separate data+layout) + scalar: Scalar<M::PointerTag>, // input value (there is no ScalarTy so we separate data+layout) src_layout: TyAndLayout<'tcx>, cast_ty: Ty<'tcx>, - ) -> Scalar<M::PointerTag> { + ) -> InterpResult<'tcx, Scalar<M::PointerTag>> { // Let's make sure v is sign-extended *if* it has a signed type. let signed = src_layout.abi.is_signed(); // Also asserts that abi is `Scalar`. + + let v = scalar.to_bits(src_layout.size)?; let v = if signed { self.sign_extend(v, src_layout) } else { v }; trace!("cast_from_scalar: {}, {} -> {}", v, src_layout.ty, cast_ty); use rustc_middle::ty::TyKind::*; - match *cast_ty.kind() { - Int(_) | Uint(_) | RawPtr(_) => { + + Ok(match *cast_ty.kind() { + Int(_) | Uint(_) => { let size = match *cast_ty.kind() { Int(t) => Integer::from_int_ty(self, t).size(), Uint(t) => Integer::from_uint_ty(self, t).size(), - RawPtr(_) => self.pointer_size(), _ => bug!(), }; let v = size.truncate(v); Scalar::from_uint(v, size) } + RawPtr(_) => { + assert!(src_layout.ty.is_integral()); + + let size = self.pointer_size(); + let addr = u64::try_from(size.truncate(v)).unwrap(); + + let ptr = M::ptr_from_addr_cast(&self, addr); + Scalar::from_maybe_pointer(ptr, self) + } + Float(FloatTy::F32) if signed => Scalar::from_f32(Single::from_i128(v as i128).value), Float(FloatTy::F64) if signed => Scalar::from_f64(Double::from_i128(v as i128).value), Float(FloatTy::F32) => Scalar::from_f32(Single::from_u128(v).value), @@ -214,7 +240,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Casts to bool are not permitted by rustc, no need to handle them here. _ => span_bug!(self.cur_span(), "invalid int to {:?} cast", cast_ty), - } + }) } fn cast_from_float<F>(&self, f: F, dest_ty: Ty<'tcx>) -> Scalar<M::PointerTag> diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 827959113b9..dfb81a2afc4 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -905,7 +905,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { trace!( "deallocating local {:?}: {:?}", local, - self.dump_alloc(ptr.provenance.unwrap().get_alloc_id()) + // Locals always have a `alloc_id` (they are never the result of a int2ptr). + self.dump_alloc(ptr.provenance.unwrap().get_alloc_id().unwrap()) ); self.deallocate_ptr(ptr, None, MemoryKind::Stack)?; }; @@ -1013,9 +1014,13 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug } } - write!(fmt, ": {:?}", self.ecx.dump_allocs(allocs)) + write!( + fmt, + ": {:?}", + self.ecx.dump_allocs(allocs.into_iter().filter_map(|x| x).collect()) + ) } - Place::Ptr(mplace) => match mplace.ptr.provenance.map(Provenance::get_alloc_id) { + Place::Ptr(mplace) => match mplace.ptr.provenance.and_then(Provenance::get_alloc_id) { Some(alloc_id) => write!( fmt, "by align({}) ref {:?}: {:?}", diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 3cc237faf69..5e0d1abd6c1 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -44,7 +44,7 @@ fn numeric_intrinsic<Tag>(name: Symbol, bits: u128, kind: Primitive) -> Scalar<T /// The logic for all nullary intrinsics is implemented here. These intrinsics don't get evaluated /// inside an `InterpCx` and instead have their value computed directly from rustc internal info. -crate fn eval_nullary_intrinsic<'tcx>( +pub(crate) fn eval_nullary_intrinsic<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, def_id: DefId, @@ -308,7 +308,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let offset_ptr = ptr.wrapping_signed_offset(offset_bytes, self); self.write_pointer(offset_ptr, dest)?; } - sym::ptr_offset_from => { + sym::ptr_offset_from | sym::ptr_offset_from_unsigned => { let a = self.read_pointer(&args[0])?; let b = self.read_pointer(&args[1])?; @@ -330,8 +330,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Both are pointers. They must be into the same allocation. if a_alloc_id != b_alloc_id { throw_ub_format!( - "ptr_offset_from cannot compute offset of pointers into different \ - allocations.", + "{} cannot compute offset of pointers into different allocations.", + intrinsic_name, ); } // And they must both be valid for zero-sized accesses ("in-bounds or one past the end"). @@ -348,16 +348,39 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { CheckInAllocMsg::OffsetFromTest, )?; + if intrinsic_name == sym::ptr_offset_from_unsigned && a_offset < b_offset { + throw_ub_format!( + "{} cannot compute a negative offset, but {} < {}", + intrinsic_name, + a_offset.bytes(), + b_offset.bytes(), + ); + } + // Compute offset. let usize_layout = self.layout_of(self.tcx.types.usize)?; let isize_layout = self.layout_of(self.tcx.types.isize)?; - let a_offset = ImmTy::from_uint(a_offset.bytes(), usize_layout); - let b_offset = ImmTy::from_uint(b_offset.bytes(), usize_layout); - let (val, _overflowed, _ty) = + let ret_layout = if intrinsic_name == sym::ptr_offset_from { + isize_layout + } else { + usize_layout + }; + + // The subtraction is always done in `isize` to enforce + // the "no more than `isize::MAX` apart" requirement. + let a_offset = ImmTy::from_uint(a_offset.bytes(), isize_layout); + let b_offset = ImmTy::from_uint(b_offset.bytes(), isize_layout); + let (val, overflowed, _ty) = self.overflowing_binary_op(BinOp::Sub, &a_offset, &b_offset)?; + if overflowed { + throw_ub_format!("Pointers were too far apart for {}", intrinsic_name); + } + let pointee_layout = self.layout_of(substs.type_at(0))?; - let val = ImmTy::from_scalar(val, isize_layout); - let size = ImmTy::from_int(pointee_layout.size.bytes(), isize_layout); + // This re-interprets an isize at ret_layout, but we already checked + // that if ret_layout is usize, then the result must be non-negative. + let val = ImmTy::from_scalar(val, ret_layout); + let size = ImmTy::from_int(pointee_layout.size.bytes(), ret_layout); self.exact_div(&val, &size, dest)?; } } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs index 058903dcdee..e66cb9837c9 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs @@ -15,7 +15,7 @@ use crate::interpret::{ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Walks up the callstack from the intrinsic's callsite, searching for the first callsite in a /// frame which is not `#[track_caller]`. - crate fn find_closest_untracked_caller_location(&self) -> Span { + pub(crate) fn find_closest_untracked_caller_location(&self) -> Span { for frame in self.stack().iter().rev() { debug!("find_closest_untracked_caller_location: checking frame {:?}", frame.instance); @@ -74,7 +74,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } /// Allocate a `const core::panic::Location` with the provided filename and line/column numbers. - crate fn alloc_caller_location( + pub(crate) fn alloc_caller_location( &mut self, filename: Symbol, line: u32, @@ -95,7 +95,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Allocate memory for `CallerLocation` struct. let loc_ty = self .tcx - .type_of(self.tcx.require_lang_item(LangItem::PanicLocation, None)) + .bound_type_of(self.tcx.require_lang_item(LangItem::PanicLocation, None)) .subst(*self.tcx, self.tcx.mk_substs([self.tcx.lifetimes.re_erased.into()].iter())); let loc_layout = self.layout_of(loc_ty).unwrap(); // This can fail if rustc runs out of memory right here. Trying to emit an error would be @@ -113,7 +113,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { location } - crate fn location_triple_for_span(&self, span: Span) -> (Symbol, u32, u32) { + pub(crate) fn location_triple_for_span(&self, span: Span) -> (Symbol, u32, u32) { let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span); let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo()); ( diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/type_name.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/type_name.rs index 447797f915c..f9847742f08 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics/type_name.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics/type_name.rs @@ -189,7 +189,7 @@ impl Write for AbsolutePathPrinter<'_> { } /// Directly returns an `Allocation` containing an absolute path representation of the given type. -crate fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ConstAllocation<'tcx> { +pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ConstAllocation<'tcx> { let path = AbsolutePathPrinter { tcx, path: String::new() }.print_type(ty).unwrap().path; let alloc = Allocation::from_bytes_byte_aligned_immutable(path.into_bytes()); tcx.intern_const_alloc(alloc) diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 7721485771b..1dcd50a5b70 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -133,9 +133,11 @@ pub trait Machine<'mir, 'tcx>: Sized { /// Whether to enforce the validity invariant fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool; - /// Whether to enforce validity (e.g., initialization and not having ptr provenance) - /// of integers and floats. - fn enforce_number_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool; + /// Whether to enforce integers and floats being initialized. + fn enforce_number_init(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool; + + /// Whether to enforce integers and floats not having provenance. + fn enforce_number_no_provenance(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool; /// Whether function calls should be [ABI](Abi)-checked. fn enforce_abi(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { @@ -286,19 +288,36 @@ pub trait Machine<'mir, 'tcx>: Sized { ) -> Pointer<Self::PointerTag>; /// "Int-to-pointer cast" - fn ptr_from_addr( + fn ptr_from_addr_cast( + ecx: &InterpCx<'mir, 'tcx, Self>, + addr: u64, + ) -> Pointer<Option<Self::PointerTag>>; + + // FIXME: Transmuting an integer to a pointer should just always return a `None` + // provenance, but that causes problems with function pointers in Miri. + /// Hook for returning a pointer from a transmute-like operation on an addr. + fn ptr_from_addr_transmute( ecx: &InterpCx<'mir, 'tcx, Self>, addr: u64, ) -> Pointer<Option<Self::PointerTag>>; + /// Marks a pointer as exposed, allowing it's provenance + /// to be recovered. "Pointer-to-int cast" + fn expose_ptr( + ecx: &mut InterpCx<'mir, 'tcx, Self>, + ptr: Pointer<Self::PointerTag>, + ) -> InterpResult<'tcx>; + /// Convert a pointer with provenance into an allocation-offset pair /// and extra provenance info. /// /// The returned `AllocId` must be the same as `ptr.provenance.get_alloc_id()`. + /// + /// When this fails, that means the pointer does not point to a live allocation. fn ptr_get_alloc( ecx: &InterpCx<'mir, 'tcx, Self>, ptr: Pointer<Self::PointerTag>, - ) -> (AllocId, Size, Self::TagExtra); + ) -> Option<(AllocId, Size, Self::TagExtra)>; /// Called to initialize the "extra" state of an allocation and make the pointers /// it contains (in relocations) tagged. The way we construct allocations is @@ -436,7 +455,12 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { } #[inline(always)] - fn enforce_number_validity(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool { + fn enforce_number_init(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool { + true + } + + #[inline(always)] + fn enforce_number_no_provenance(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool { true } @@ -480,7 +504,18 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { } #[inline(always)] - fn ptr_from_addr(_ecx: &InterpCx<$mir, $tcx, Self>, addr: u64) -> Pointer<Option<AllocId>> { + fn ptr_from_addr_transmute( + _ecx: &InterpCx<$mir, $tcx, Self>, + addr: u64, + ) -> Pointer<Option<AllocId>> { + Pointer::new(None, Size::from_bytes(addr)) + } + + #[inline(always)] + fn ptr_from_addr_cast( + _ecx: &InterpCx<$mir, $tcx, Self>, + addr: u64, + ) -> Pointer<Option<AllocId>> { Pointer::new(None, Size::from_bytes(addr)) } @@ -488,9 +523,9 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { fn ptr_get_alloc( _ecx: &InterpCx<$mir, $tcx, Self>, ptr: Pointer<AllocId>, - ) -> (AllocId, Size, Self::TagExtra) { + ) -> Option<(AllocId, Size, Self::TagExtra)> { // We know `offset` is relative to the allocation, so we can use `into_parts`. let (alloc_id, offset) = ptr.into_parts(); - (alloc_id, offset, ()) + Some((alloc_id, offset, ())) } } diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index b1d7ab6a098..721abff689a 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -770,7 +770,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if reachable.insert(id) { // This is a new allocation, add its relocations to `todo`. if let Some((_, alloc)) = self.memory.alloc_map.get(id) { - todo.extend(alloc.relocations().values().map(|tag| tag.get_alloc_id())); + todo.extend( + alloc.relocations().values().filter_map(|tag| tag.get_alloc_id()), + ); } } } @@ -805,7 +807,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a, allocs_to_print: &mut VecDeque<AllocId>, alloc: &Allocation<Tag, Extra>, ) -> std::fmt::Result { - for alloc_id in alloc.relocations().values().map(|tag| tag.get_alloc_id()) { + for alloc_id in alloc.relocations().values().filter_map(|tag| tag.get_alloc_id()) { allocs_to_print.push_back(alloc_id); } write!(fmt, "{}", display_allocation(tcx, alloc)) @@ -922,10 +924,15 @@ impl<'tcx, 'a, Tag: Provenance, Extra> AllocRef<'a, 'tcx, Tag, Extra> { self.read_scalar(alloc_range(offset, self.tcx.data_layout().pointer_size)) } - pub fn check_bytes(&self, range: AllocRange, allow_uninit_and_ptr: bool) -> InterpResult<'tcx> { + pub fn check_bytes( + &self, + range: AllocRange, + allow_uninit: bool, + allow_ptr: bool, + ) -> InterpResult<'tcx> { Ok(self .alloc - .check_bytes(&self.tcx, self.range.subrange(range), allow_uninit_and_ptr) + .check_bytes(&self.tcx, self.range.subrange(range), allow_uninit, allow_ptr) .map_err(|e| e.to_interp_error(self.alloc_id))?) } } @@ -1142,11 +1149,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Err(ptr) => ptr.into(), Ok(bits) => { let addr = u64::try_from(bits).unwrap(); - let ptr = M::ptr_from_addr(&self, addr); - if addr == 0 { - assert!(ptr.provenance.is_none(), "null pointer can never have an AllocId"); - } - ptr + M::ptr_from_addr_transmute(&self, addr) } }, ) @@ -1182,10 +1185,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ptr: Pointer<Option<M::PointerTag>>, ) -> Result<(AllocId, Size, M::TagExtra), u64> { match ptr.into_pointer_or_addr() { - Ok(ptr) => { - let (alloc_id, offset, extra) = M::ptr_get_alloc(self, ptr); - Ok((alloc_id, offset, extra)) - } + Ok(ptr) => match M::ptr_get_alloc(self, ptr) { + Some((alloc_id, offset, extra)) => Ok((alloc_id, offset, extra)), + None => { + assert!(M::PointerTag::OFFSET_IS_ADDR); + let (_, addr) = ptr.into_parts(); + Err(addr.bytes()) + } + }, Err(addr) => Err(addr.bytes()), } } diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs index 69d6c8470a2..2b73ad568e0 100644 --- a/compiler/rustc_const_eval/src/interpret/mod.rs +++ b/compiler/rustc_const_eval/src/interpret/mod.rs @@ -29,5 +29,5 @@ pub use self::place::{MPlaceTy, MemPlace, MemPlaceMeta, Place, PlaceTy}; pub use self::validity::{CtfeValidationMode, RefTracking}; pub use self::visitor::{MutValueVisitor, Value, ValueVisitor}; -crate use self::intrinsics::eval_nullary_intrinsic; +pub(crate) use self::intrinsics::eval_nullary_intrinsic; use eval_context::{from_known_layout, mir_assign_valid_types}; diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index a8a5ac2f9d9..f5e1ee4e233 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -284,8 +284,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Abi::Scalar(s) if force => Some(s.primitive()), _ => None, }; - if let Some(_) = scalar_layout { - let scalar = alloc.read_scalar(alloc_range(Size::ZERO, mplace.layout.size))?; + if let Some(_s) = scalar_layout { + //FIXME(#96185): let size = s.size(self); + //FIXME(#96185): assert_eq!(size, mplace.layout.size, "abi::Scalar size does not match layout size"); + let size = mplace.layout.size; //FIXME(#96185): remove this line + let scalar = alloc.read_scalar(alloc_range(Size::ZERO, size))?; return Ok(Some(ImmTy { imm: scalar.into(), layout: mplace.layout })); } let scalar_pair_layout = match mplace.layout.abi { @@ -302,7 +305,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // which `ptr.offset(b_offset)` cannot possibly fail to satisfy. let (a_size, b_size) = (a.size(self), b.size(self)); let b_offset = a_size.align_to(b.align(self).abi); - assert!(b_offset.bytes() > 0); // we later use the offset to tell apart the fields + assert!(b_offset.bytes() > 0); // in `operand_field` we use the offset to tell apart the fields let a_val = alloc.read_scalar(alloc_range(Size::ZERO, a_size))?; let b_val = alloc.read_scalar(alloc_range(b_offset, b_size))?; return Ok(Some(ImmTy { @@ -394,28 +397,44 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Err(value) => value, }; - let field_layout = op.layout.field(self, field); - if field_layout.is_zst() { - let immediate = Scalar::ZST.into(); - return Ok(OpTy { op: Operand::Immediate(immediate), layout: field_layout }); - } - let offset = op.layout.fields.offset(field); - let immediate = match *base { + let field_layout = base.layout.field(self, field); + let offset = base.layout.fields.offset(field); + // This makes several assumptions about what layouts we will encounter; we match what + // codegen does as good as we can (see `extract_field` in `rustc_codegen_ssa/src/mir/operand.rs`). + let field_val: Immediate<_> = match (*base, base.layout.abi) { + // the field contains no information + _ if field_layout.is_zst() => Scalar::ZST.into(), // the field covers the entire type - _ if offset.bytes() == 0 && field_layout.size == op.layout.size => *base, + _ if field_layout.size == base.layout.size => { + assert!(match (base.layout.abi, field_layout.abi) { + (Abi::Scalar(..), Abi::Scalar(..)) => true, + (Abi::ScalarPair(..), Abi::ScalarPair(..)) => true, + _ => false, + }); + assert!(offset.bytes() == 0); + *base + } // extract fields from types with `ScalarPair` ABI - Immediate::ScalarPair(a, b) => { - let val = if offset.bytes() == 0 { a } else { b }; - Immediate::from(val) + (Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => { + assert!(matches!(field_layout.abi, Abi::Scalar(..))); + Immediate::from(if offset.bytes() == 0 { + debug_assert_eq!(field_layout.size, a.size(self)); + a_val + } else { + debug_assert_eq!(offset, a.size(self).align_to(b.align(self).abi)); + debug_assert_eq!(field_layout.size, b.size(self)); + b_val + }) } - Immediate::Scalar(val) => span_bug!( + _ => span_bug!( self.cur_span(), - "field access on non aggregate {:#?}, {:#?}", - val, - op.layout + "invalid field access on immediate {}, layout {:#?}", + base, + base.layout ), }; - Ok(OpTy { op: Operand::Immediate(immediate), layout: field_layout }) + + Ok(OpTy { op: Operand::Immediate(field_val), layout: field_layout }) } pub fn operand_index( @@ -520,8 +539,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(OpTy { op, layout: place.layout }) } - // Evaluate a place with the goal of reading from it. This lets us sometimes - // avoid allocations. + /// Evaluate a place with the goal of reading from it. This lets us sometimes + /// avoid allocations. pub fn eval_place_to_op( &self, place: mir::Place<'tcx>, @@ -624,7 +643,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } - crate fn const_val_to_op( + pub(crate) fn const_val_to_op( &self, val_val: ConstValue<'tcx>, ty: Ty<'tcx>, @@ -722,17 +741,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Figure out which discriminant and variant this corresponds to. Ok(match *tag_encoding { TagEncoding::Direct => { + let scalar = tag_val.to_scalar()?; // Generate a specific error if `tag_val` is not an integer. // (`tag_bits` itself is only used for error messages below.) - let tag_bits = tag_val - .to_scalar()? + let tag_bits = scalar .try_to_int() .map_err(|dbg_val| err_ub!(InvalidTag(dbg_val)))? .assert_bits(tag_layout.size); // Cast bits from tag layout to discriminant layout. - // After the checks we did above, this cannot fail. + // After the checks we did above, this cannot fail, as + // discriminants are int-like. let discr_val = - self.misc_cast(&tag_val, discr_layout.ty).unwrap().to_scalar().unwrap(); + self.cast_from_int_like(scalar, tag_val.layout, discr_layout.ty).unwrap(); let discr_bits = discr_val.assert_bits(discr_layout.size); // Convert discriminant to variant index, and catch invalid discriminants. let index = match *op.layout.ty.kind() { diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index df6e05bb13c..62f9c8f990d 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -16,7 +16,7 @@ use rustc_target::abi::{HasDataLayout, Size, VariantIdx, Variants}; use super::{ alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, CheckInAllocMsg, ConstAlloc, ImmTy, Immediate, InterpCx, InterpResult, LocalValue, Machine, MemoryKind, OpTy, - Operand, Pointer, PointerArithmetic, Provenance, Scalar, ScalarMaybeUninit, + Operand, Pointer, Provenance, Scalar, ScalarMaybeUninit, }; #[derive(Copy, Clone, Hash, PartialEq, Eq, HashStable, Debug)] @@ -115,12 +115,6 @@ impl<'tcx, Tag: Provenance> std::ops::Deref for MPlaceTy<'tcx, Tag> { } } -impl<'tcx, Tag: Provenance> std::ops::DerefMut for MPlaceTy<'tcx, Tag> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.mplace - } -} - impl<'tcx, Tag: Provenance> From<MPlaceTy<'tcx, Tag>> for PlaceTy<'tcx, Tag> { #[inline(always)] fn from(mplace: MPlaceTy<'tcx, Tag>) -> Self { @@ -197,6 +191,18 @@ impl<'tcx, Tag: Provenance> MPlaceTy<'tcx, Tag> { } #[inline] + pub fn from_aligned_ptr_with_meta( + ptr: Pointer<Option<Tag>>, + layout: TyAndLayout<'tcx>, + meta: MemPlaceMeta<Tag>, + ) -> Self { + let mut mplace = MemPlace::from_ptr(ptr, layout.align.abi); + mplace.meta = meta; + + MPlaceTy { mplace, layout } + } + + #[inline] pub(crate) fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> { if self.layout.is_unsized() { // We need to consult `meta` metadata @@ -495,7 +501,7 @@ where /// Project into an mplace #[instrument(skip(self), level = "debug")] - pub(crate) fn mplace_projection( + pub(super) fn mplace_projection( &self, base: &MPlaceTy<'tcx, M::PointerTag>, proj_elem: mir::PlaceElem<'tcx>, @@ -625,7 +631,7 @@ where } /// Computes a place. You should only use this if you intend to write into this - /// place; for reading, a more efficient alternative is `eval_place_for_read`. + /// place; for reading, a more efficient alternative is `eval_place_to_op`. #[instrument(skip(self), level = "debug")] pub fn eval_place( &mut self, @@ -700,24 +706,7 @@ where src: Immediate<M::PointerTag>, dest: &PlaceTy<'tcx, M::PointerTag>, ) -> InterpResult<'tcx> { - if cfg!(debug_assertions) { - // This is a very common path, avoid some checks in release mode - assert!(!dest.layout.is_unsized(), "Cannot write unsized data"); - match src { - Immediate::Scalar(ScalarMaybeUninit::Scalar(Scalar::Ptr(..))) => assert_eq!( - self.pointer_size(), - dest.layout.size, - "Size mismatch when writing pointer" - ), - Immediate::Scalar(ScalarMaybeUninit::Scalar(Scalar::Int(int))) => { - assert_eq!(int.size(), dest.layout.size, "Size mismatch when writing bits") - } - Immediate::Scalar(ScalarMaybeUninit::Uninit) => {} // uninit can have any size - Immediate::ScalarPair(_, _) => { - // FIXME: Can we check anything here? - } - } - } + assert!(!dest.layout.is_unsized(), "Cannot write unsized data"); trace!("write_immediate: {:?} <- {:?}: {}", *dest, src, dest.layout.ty); // See if we can avoid an allocation. This is the counterpart to `read_immediate_raw`, @@ -753,31 +742,27 @@ where dest: &MPlaceTy<'tcx, M::PointerTag>, ) -> InterpResult<'tcx> { // Note that it is really important that the type here is the right one, and matches the - // type things are read at. In case `src_val` is a `ScalarPair`, we don't do any magic here + // type things are read at. In case `value` is a `ScalarPair`, we don't do any magic here // to handle padding properly, which is only correct if we never look at this data with the // wrong type. - // Invalid places are a thing: the return place of a diverging function let tcx = *self.tcx; let Some(mut alloc) = self.get_place_alloc_mut(dest)? else { // zero-sized access return Ok(()); }; - // FIXME: We should check that there are dest.layout.size many bytes available in - // memory. The code below is not sufficient, with enough padding it might not - // cover all the bytes! match value { Immediate::Scalar(scalar) => { - match dest.layout.abi { - Abi::Scalar(_) => {} // fine - _ => span_bug!( + let Abi::Scalar(s) = dest.layout.abi else { span_bug!( self.cur_span(), "write_immediate_to_mplace: invalid Scalar layout: {:#?}", dest.layout - ), - } - alloc.write_scalar(alloc_range(Size::ZERO, dest.layout.size), scalar) + ) + }; + let size = s.size(&tcx); + //FIXME(#96185): assert_eq!(dest.layout.size, size, "abi::Scalar size does not match layout size"); + alloc.write_scalar(alloc_range(Size::ZERO, size), scalar) } Immediate::ScalarPair(a_val, b_val) => { // We checked `ptr_align` above, so all fields will have the alignment they need. @@ -791,6 +776,7 @@ where }; let (a_size, b_size) = (a.size(&tcx), b.size(&tcx)); let b_offset = a_size.align_to(b.align(&tcx).abi); + assert!(b_offset.bytes() > 0); // in `operand_field` we use the offset to tell apart the fields // It is tempting to verify `b_offset` against `layout.fields.offset(1)`, // but that does not work: We could be a newtype around a pair, then the diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index c2664565f15..25f9d4baca3 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -312,8 +312,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; match instance.def { - ty::InstanceDef::Intrinsic(..) => { - assert!(caller_abi == Abi::RustIntrinsic || caller_abi == Abi::PlatformIntrinsic); + ty::InstanceDef::Intrinsic(def_id) => { + assert!(self.tcx.is_intrinsic(def_id)); // caller_fn_abi is not relevant here, we interpret the arguments directly for each intrinsic. M::call_intrinsic(self, instance, args, ret, unwind) } diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index e17bd9a8c08..1940b573db0 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -8,7 +8,7 @@ use std::ops::ControlFlow; /// 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. -crate fn ensure_monomorphic_enough<'tcx, T>(tcx: TyCtxt<'tcx>, ty: T) -> InterpResult<'tcx> +pub(crate) fn ensure_monomorphic_enough<'tcx, T>(tcx: TyCtxt<'tcx>, ty: T) -> InterpResult<'tcx> where T: TypeFoldable<'tcx>, { diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 92e3ac04dc4..b39a33aff09 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -412,22 +412,27 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' self.path, err_ub!(AlignmentCheckFailed { required, has }) => { - "an unaligned {} (required {} byte alignment but found {})", - kind, + "an unaligned {kind} (required {} byte alignment but found {})", required.bytes(), has.bytes() }, err_ub!(DanglingIntPointer(0, _)) => - { "a null {}", kind }, + { "a null {kind}" }, err_ub!(DanglingIntPointer(i, _)) => - { "a dangling {} (address 0x{:x} is unallocated)", kind, i }, + { "a dangling {kind} (address 0x{i:x} is unallocated)" }, err_ub!(PointerOutOfBounds { .. }) => - { "a dangling {} (going beyond the bounds of its allocation)", kind }, + { "a dangling {kind} (going beyond the bounds of its allocation)" }, // This cannot happen during const-eval (because interning already detects // dangling pointers), but it can happen in Miri. err_ub!(PointerUseAfterFree(..)) => - { "a dangling {} (use-after-free)", kind }, + { "a dangling {kind} (use-after-free)" }, ); + // Do not allow pointers to uninhabited types. + if place.layout.abi.is_uninhabited() { + throw_validation_failure!(self.path, + { "a {kind} pointing to uninhabited type {}", place.layout.ty } + ) + } // Recursive checking if let Some(ref mut ref_tracking) = self.ref_tracking { // Proceed recursively even for ZST, no reason to skip them! @@ -531,15 +536,21 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' let value = self.read_scalar(value)?; // NOTE: Keep this in sync with the array optimization for int/float // types below! - if M::enforce_number_validity(self.ecx) { - // Integers/floats with number validity: Must be scalar bits, pointers are dangerous. + if M::enforce_number_init(self.ecx) { + try_validation!( + value.check_init(), + self.path, + err_ub!(InvalidUninitBytes(..)) => + { "{:x}", value } expected { "initialized bytes" } + ); + } + if M::enforce_number_no_provenance(self.ecx) { // As a special exception we *do* match on a `Scalar` here, since we truly want // to know its underlying representation (and *not* cast it to an integer). - let is_bits = - value.check_init().map_or(false, |v| matches!(v, Scalar::Int(..))); - if !is_bits { + let is_ptr = value.check_init().map_or(false, |v| matches!(v, Scalar::Ptr(..))); + if is_ptr { throw_validation_failure!(self.path, - { "{:x}", value } expected { "initialized plain (non-pointer) bytes" } + { "{:x}", value } expected { "plain (non-pointer) bytes" } ) } } @@ -645,17 +656,18 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' // i.e. that we go over the `check_init` below. let size = scalar_layout.size(self.ecx); let is_full_range = match scalar_layout { - ScalarAbi::Initialized { valid_range, .. } => { - if M::enforce_number_validity(self.ecx) { + ScalarAbi::Initialized { .. } => { + if M::enforce_number_init(self.ecx) { false // not "full" since uninit is not accepted } else { - valid_range.is_full_for(size) + scalar_layout.is_always_valid(self.ecx) } } ScalarAbi::Union { .. } => true, }; if is_full_range { - // Nothing to check + // Nothing to check. Cruciall we don't even `read_scalar` until here, since that would + // fail for `Union` scalars! return Ok(()); } // We have something to check: it must at least be initialized. @@ -688,7 +700,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' } else { return Ok(()); } - } else if scalar_layout.valid_range(self.ecx).is_full_for(size) { + } else if scalar_layout.is_always_valid(self.ecx) { // Easy. (This is reachable if `enforce_number_validity` is set.) return Ok(()); } else { @@ -904,10 +916,10 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> return Ok(()); }; - let allow_uninit_and_ptr = !M::enforce_number_validity(self.ecx); match alloc.check_bytes( alloc_range(Size::ZERO, size), - allow_uninit_and_ptr, + /*allow_uninit*/ !M::enforce_number_init(self.ecx), + /*allow_ptr*/ !M::enforce_number_no_provenance(self.ecx), ) { // In the happy case, we needn't check anything else. Ok(()) => {} diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 1ab461a9421..eacb5978d99 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -7,7 +7,6 @@ Rust MIR: a lowered representation of Rust. #![feature(assert_matches)] #![feature(box_patterns)] #![feature(control_flow_enum)] -#![feature(crate_visibility_modifier)] #![feature(decl_macro)] #![feature(exact_size_is_empty)] #![feature(let_else)] @@ -34,26 +33,32 @@ pub mod transform; pub mod util; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::ParamEnv; pub fn provide(providers: &mut Providers) { const_eval::provide(providers); providers.eval_to_const_value_raw = const_eval::eval_to_const_value_raw_provider; providers.eval_to_allocation_raw = const_eval::eval_to_allocation_raw_provider; providers.const_caller_location = const_eval::const_caller_location; - providers.try_destructure_const = |tcx, param_env_and_value| { - let (param_env, value) = param_env_and_value.into_parts(); - const_eval::try_destructure_const(tcx, param_env, value).ok() + providers.try_destructure_const = |tcx, param_env_and_val| { + let (param_env, c) = param_env_and_val.into_parts(); + const_eval::try_destructure_const(tcx, param_env, c).ok() }; - providers.const_to_valtree = |tcx, param_env_and_value| { + providers.eval_to_valtree = |tcx, param_env_and_value| { let (param_env, raw) = param_env_and_value.into_parts(); - const_eval::const_to_valtree(tcx, param_env, raw) + const_eval::eval_to_valtree(tcx, param_env, raw) }; - providers.valtree_to_const_val = |tcx, (ty, valtree)| { - const_eval::valtree_to_const_value(tcx, ParamEnv::empty().and(ty), valtree) + providers.try_destructure_mir_constant = |tcx, param_env_and_value| { + let (param_env, value) = param_env_and_value.into_parts(); + const_eval::try_destructure_mir_constant(tcx, param_env, value).ok() }; + providers.valtree_to_const_val = + |tcx, (ty, valtree)| const_eval::valtree_to_const_value(tcx, ty, valtree); providers.deref_const = |tcx, param_env_and_value| { let (param_env, value) = param_env_and_value.into_parts(); const_eval::deref_const(tcx, param_env, value) }; + providers.deref_mir_constant = |tcx, param_env_and_value| { + let (param_env, value) = param_env_and_value.into_parts(); + const_eval::deref_mir_constant(tcx, param_env, value) + }; } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index 7e2a50444db..2c669dd6d9a 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -229,18 +229,6 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { // The local type and predicate checks are not free and only relevant for `const fn`s. if self.const_kind() == hir::ConstContext::ConstFn { - // Prevent const trait methods from being annotated as `stable`. - // FIXME: Do this as part of stability checking. - if self.is_const_stable_const_fn() { - if crate::const_eval::is_parent_const_impl_raw(tcx, def_id) { - self.ccx - .tcx - .sess - .struct_span_err(self.span, "trait methods cannot be stable const fn") - .emit(); - } - } - for (idx, local) in body.local_decls.iter_enumerated() { // Handle the return place below. if idx == RETURN_PLACE || local.internal { @@ -312,11 +300,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { Status::Unstable(gate) if self.tcx.features().enabled(gate) => { let unstable_in_stable = self.ccx.is_const_stable_const_fn() - && !super::rustc_allow_const_fn_unstable( - self.tcx, - self.def_id().to_def_id(), - gate, - ); + && !super::rustc_allow_const_fn_unstable(self.tcx, self.def_id(), gate); if unstable_in_stable { emit_unstable_in_stable_error(self.ccx, span, gate); } @@ -706,14 +690,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { #[instrument(level = "debug", skip(self))] fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { - use rustc_target::spec::abi::Abi::RustIntrinsic; - self.super_terminator(terminator, location); match &terminator.kind { TerminatorKind::Call { func, args, fn_span, from_hir_call, .. } => { let ConstCx { tcx, body, param_env, .. } = *self.ccx; - let caller = self.def_id().to_def_id(); + let caller = self.def_id(); let fn_ty = func.ty(body, tcx); @@ -797,7 +779,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // trait. let callee_trait = tcx.trait_of_item(callee); if callee_trait.is_some() - && tcx.has_attr(caller, sym::default_method_body_is_const) + && tcx.has_attr(caller.to_def_id(), sym::default_method_body_is_const) && callee_trait == tcx.trait_of_item(caller) // Can only call methods when it's `<Self as TheTrait>::f`. && tcx.types.self_param == substs.type_at(0) @@ -889,7 +871,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { return; } - let is_intrinsic = tcx.fn_sig(callee).abi() == RustIntrinsic; + let is_intrinsic = tcx.is_intrinsic(callee); if !tcx.is_const_fn_raw(callee) { if tcx.trait_of_item(callee).is_some() { @@ -950,7 +932,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // have no `rustc_const_stable` attributes to be const-unstable as well. This // should be fixed later. let callee_is_unstable_unmarked = tcx.lookup_const_stability(callee).is_none() - && tcx.lookup_stability(callee).map_or(false, |s| s.level.is_unstable()); + && tcx.lookup_stability(callee).map_or(false, |s| s.is_unstable()); if callee_is_unstable_unmarked { trace!("callee_is_unstable_unmarked"); // We do not use `const` modifiers for intrinsic "functions", as intrinsics are diff --git a/compiler/rustc_const_eval/src/transform/check_consts/mod.rs b/compiler/rustc_const_eval/src/transform/check_consts/mod.rs index 25ba97ee605..0f79fe5513d 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/mod.rs @@ -66,8 +66,12 @@ impl<'mir, 'tcx> ConstCx<'mir, 'tcx> { } } -pub fn rustc_allow_const_fn_unstable(tcx: TyCtxt<'_>, def_id: DefId, feature_gate: Symbol) -> bool { - let attrs = tcx.get_attrs(def_id); +pub fn rustc_allow_const_fn_unstable( + tcx: TyCtxt<'_>, + def_id: LocalDefId, + feature_gate: Symbol, +) -> bool { + let attrs = tcx.hir().attrs(tcx.hir().local_def_id_to_hir_id(def_id)); attr::rustc_allow_const_fn_unstable(&tcx.sess, attrs).any(|name| name == feature_gate) } @@ -80,8 +84,6 @@ pub fn rustc_allow_const_fn_unstable(tcx: TyCtxt<'_>, def_id: DefId, feature_gat // functions are subject to more stringent restrictions than "const-unstable" functions: They // cannot use unstable features and can only call other "const-stable" functions. pub fn is_const_stable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - use attr::{ConstStability, Stability, StabilityLevel}; - // A default body marked const is not const-stable because const // trait fns currently cannot be const-stable. We shouldn't // restrict default bodies to only call const-stable functions. @@ -92,22 +94,39 @@ pub fn is_const_stable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool { // Const-stability is only relevant for `const fn`. assert!(tcx.is_const_fn_raw(def_id)); - // Functions with `#[rustc_const_unstable]` are const-unstable. + // A function is only const-stable if it has `#[rustc_const_stable]` or it the trait it belongs + // to is const-stable. match tcx.lookup_const_stability(def_id) { - Some(ConstStability { level: StabilityLevel::Unstable { .. }, .. }) => return false, - Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => return true, - None => {} + Some(stab) => stab.is_const_stable(), + None if is_parent_const_stable_trait(tcx, def_id) => { + // Remove this when `#![feature(const_trait_impl)]` is stabilized, + // returning `true` unconditionally. + tcx.sess.delay_span_bug( + tcx.def_span(def_id), + "trait implementations cannot be const stable yet", + ); + true + } + None => false, // By default, items are not const stable. } +} + +fn is_parent_const_stable_trait(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + let local_def_id = def_id.expect_local(); + let hir_id = tcx.local_def_id_to_hir_id(local_def_id); + + let Some(parent) = tcx.hir().find_parent_node(hir_id) else { return false }; + let parent_def = tcx.hir().get(parent); - // Functions with `#[unstable]` are const-unstable. - // - // FIXME(ecstaticmorse): We should keep const-stability attributes wholly separate from normal stability - // attributes. `#[unstable]` should be irrelevant. - if let Some(Stability { level: StabilityLevel::Unstable { .. }, .. }) = - tcx.lookup_stability(def_id) - { + if !matches!( + parent_def, + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { constness: hir::Constness::Const, .. }), + .. + }) + ) { return false; } - true + tcx.lookup_const_stability(parent.owner).map_or(false, |stab| stab.is_const_stable()) } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs index ba248a3b6cb..4e71baa77b0 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs @@ -1,5 +1,6 @@ //! Concrete error types for all operations which may be invalid in a certain const context. +use hir::def_id::LocalDefId; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -88,14 +89,17 @@ impl<'tcx> NonConstOp<'tcx> for FnCallIndirect { ccx: &ConstCx<'_, 'tcx>, span: Span, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - ccx.tcx.sess.struct_span_err(span, "function pointers are not allowed in const fn") + ccx.tcx.sess.struct_span_err( + span, + &format!("function pointer calls are not allowed in {}s", ccx.const_kind()), + ) } } /// A function call where the callee is not marked as `const`. #[derive(Debug, Clone, Copy)] pub struct FnCallNonConst<'tcx> { - pub caller: DefId, + pub caller: LocalDefId, pub callee: DefId, pub substs: SubstsRef<'tcx>, pub span: Span, @@ -117,13 +121,8 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { match self_ty.kind() { Param(param_ty) => { debug!(?param_ty); - if let Some(generics) = caller - .as_local() - .map(|id| tcx.hir().local_def_id_to_hir_id(id)) - .map(|id| tcx.hir().get(id)) - .as_ref() - .and_then(|node| node.generics()) - { + let caller_hir_id = tcx.hir().local_def_id_to_hir_id(caller); + if let Some(generics) = tcx.hir().get(caller_hir_id).generics() { let constraint = with_no_trimmed_paths!(format!( "~const {}", trait_ref.print_only_trait_path() diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs index 1052d588fad..f88538f61ec 100644 --- a/compiler/rustc_const_eval/src/transform/promote_consts.rs +++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs @@ -60,9 +60,9 @@ impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> { let mut rpo = traversal::reverse_postorder(body); let ccx = ConstCx::new(tcx, body); - let (temps, all_candidates) = collect_temps_and_candidates(&ccx, &mut rpo); + let (mut temps, all_candidates) = collect_temps_and_candidates(&ccx, &mut rpo); - let promotable_candidates = validate_candidates(&ccx, &temps, &all_candidates); + let promotable_candidates = validate_candidates(&ccx, &mut temps, &all_candidates); let promoted = promote_candidates(body, tcx, temps, promotable_candidates); self.promoted_fragments.set(promoted); @@ -77,7 +77,7 @@ pub enum TempState { /// One direct assignment and any number of direct uses. /// A borrow of this temp is promotable if the assigned /// value is qualified as constant. - Defined { location: Location, uses: usize }, + Defined { location: Location, uses: usize, valid: Result<(), ()> }, /// Any other combination of assignments/uses. Unpromotable, /// This temp was part of an rvalue which got extracted @@ -133,7 +133,7 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> { match context { PlaceContext::MutatingUse(MutatingUseContext::Store) | PlaceContext::MutatingUse(MutatingUseContext::Call) => { - *temp = TempState::Defined { location, uses: 0 }; + *temp = TempState::Defined { location, uses: 0, valid: Err(()) }; return; } _ => { /* mark as unpromotable below */ } @@ -188,7 +188,7 @@ pub fn collect_temps_and_candidates<'tcx>( /// This wraps an `Item`, and has access to all fields of that `Item` via `Deref` coercion. struct Validator<'a, 'tcx> { ccx: &'a ConstCx<'a, 'tcx>, - temps: &'a IndexVec<Local, TempState>, + temps: &'a mut IndexVec<Local, TempState>, } impl<'a, 'tcx> std::ops::Deref for Validator<'a, 'tcx> { @@ -202,7 +202,7 @@ impl<'a, 'tcx> std::ops::Deref for Validator<'a, 'tcx> { struct Unpromotable; impl<'tcx> Validator<'_, 'tcx> { - fn validate_candidate(&self, candidate: Candidate) -> Result<(), Unpromotable> { + fn validate_candidate(&mut self, candidate: Candidate) -> Result<(), Unpromotable> { let loc = candidate.location; let statement = &self.body[loc.block].statements[loc.statement_index]; match &statement.kind { @@ -234,7 +234,7 @@ impl<'tcx> Validator<'_, 'tcx> { } // FIXME(eddyb) maybe cache this? - fn qualif_local<Q: qualifs::Qualif>(&self, local: Local) -> bool { + fn qualif_local<Q: qualifs::Qualif>(&mut self, local: Local) -> bool { if let TempState::Defined { location: loc, .. } = self.temps[local] { let num_stmts = self.body[loc.block].statements.len(); @@ -272,40 +272,50 @@ impl<'tcx> Validator<'_, 'tcx> { } } - // FIXME(eddyb) maybe cache this? - fn validate_local(&self, local: Local) -> Result<(), Unpromotable> { - if let TempState::Defined { location: loc, .. } = self.temps[local] { - let block = &self.body[loc.block]; - let num_stmts = block.statements.len(); - - if loc.statement_index < num_stmts { - let statement = &block.statements[loc.statement_index]; - match &statement.kind { - StatementKind::Assign(box (_, rhs)) => self.validate_rvalue(rhs), - _ => { - span_bug!( - statement.source_info.span, - "{:?} is not an assignment", - statement - ); - } - } - } else { - let terminator = block.terminator(); - match &terminator.kind { - TerminatorKind::Call { func, args, .. } => self.validate_call(func, args), - TerminatorKind::Yield { .. } => Err(Unpromotable), - kind => { - span_bug!(terminator.source_info.span, "{:?} not promotable", kind); + fn validate_local(&mut self, local: Local) -> Result<(), Unpromotable> { + if let TempState::Defined { location: loc, uses, valid } = self.temps[local] { + valid.or_else(|_| { + let ok = { + let block = &self.body[loc.block]; + let num_stmts = block.statements.len(); + + if loc.statement_index < num_stmts { + let statement = &block.statements[loc.statement_index]; + match &statement.kind { + StatementKind::Assign(box (_, rhs)) => self.validate_rvalue(rhs), + _ => { + span_bug!( + statement.source_info.span, + "{:?} is not an assignment", + statement + ); + } + } + } else { + let terminator = block.terminator(); + match &terminator.kind { + TerminatorKind::Call { func, args, .. } => { + self.validate_call(func, args) + } + TerminatorKind::Yield { .. } => Err(Unpromotable), + kind => { + span_bug!(terminator.source_info.span, "{:?} not promotable", kind); + } + } } - } - } + }; + self.temps[local] = match ok { + Ok(()) => TempState::Defined { location: loc, uses, valid: Ok(()) }, + Err(_) => TempState::Unpromotable, + }; + ok + }) } else { Err(Unpromotable) } } - fn validate_place(&self, place: PlaceRef<'tcx>) -> Result<(), Unpromotable> { + fn validate_place(&mut self, place: PlaceRef<'tcx>) -> Result<(), Unpromotable> { match place.last_projection() { None => self.validate_local(place.local), Some((place_base, elem)) => { @@ -417,7 +427,7 @@ impl<'tcx> Validator<'_, 'tcx> { } } - fn validate_operand(&self, operand: &Operand<'tcx>) -> Result<(), Unpromotable> { + fn validate_operand(&mut self, operand: &Operand<'tcx>) -> Result<(), Unpromotable> { match operand { Operand::Copy(place) | Operand::Move(place) => self.validate_place(place.as_ref()), @@ -447,7 +457,7 @@ impl<'tcx> Validator<'_, 'tcx> { } } - fn validate_ref(&self, kind: BorrowKind, place: &Place<'tcx>) -> Result<(), Unpromotable> { + fn validate_ref(&mut self, kind: BorrowKind, place: &Place<'tcx>) -> Result<(), Unpromotable> { match kind { // Reject these borrow types just to be safe. // FIXME(RalfJung): could we allow them? Should we? No point in it until we have a usecase. @@ -480,7 +490,7 @@ impl<'tcx> Validator<'_, 'tcx> { Ok(()) } - fn validate_rvalue(&self, rvalue: &Rvalue<'tcx>) -> Result<(), Unpromotable> { + fn validate_rvalue(&mut self, rvalue: &Rvalue<'tcx>) -> Result<(), Unpromotable> { match rvalue { Rvalue::Use(operand) | Rvalue::Repeat(operand, _) => { self.validate_operand(operand)?; @@ -623,7 +633,7 @@ impl<'tcx> Validator<'_, 'tcx> { } fn validate_call( - &self, + &mut self, callee: &Operand<'tcx>, args: &[Operand<'tcx>], ) -> Result<(), Unpromotable> { @@ -665,10 +675,10 @@ impl<'tcx> Validator<'_, 'tcx> { // FIXME(eddyb) remove the differences for promotability in `static`, `const`, `const fn`. pub fn validate_candidates( ccx: &ConstCx<'_, '_>, - temps: &IndexVec<Local, TempState>, + temps: &mut IndexVec<Local, TempState>, candidates: &[Candidate], ) -> Vec<Candidate> { - let validator = Validator { ccx, temps }; + let mut validator = Validator { ccx, temps }; candidates .iter() @@ -720,7 +730,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { fn promote_temp(&mut self, temp: Local) -> Local { let old_keep_original = self.keep_original; let loc = match self.temps[temp] { - TempState::Defined { location, uses } if uses > 0 => { + TempState::Defined { location, uses, .. } if uses > 0 => { if uses > 1 { self.keep_original = true; } diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index f71bc586b48..3ce33d547c5 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -1,5 +1,6 @@ //! Validates the MIR to ensure that invariants are upheld. +use rustc_data_structures::fx::FxHashSet; use rustc_index::bit_set::BitSet; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::interpret::Scalar; @@ -14,7 +15,7 @@ use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeFoldable}; use rustc_mir_dataflow::impls::MaybeStorageLive; use rustc_mir_dataflow::storage::AlwaysLiveLocals; use rustc_mir_dataflow::{Analysis, ResultsCursor}; -use rustc_target::abi::Size; +use rustc_target::abi::{Size, VariantIdx}; #[derive(Copy, Clone, Debug)] enum EdgeKind { @@ -244,6 +245,60 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.fail(location, format!("bad index ({:?} != usize)", index_ty)) } } + if let ProjectionElem::Field(f, ty) = elem { + let parent = Place { local, projection: self.tcx.intern_place_elems(proj_base) }; + let parent_ty = parent.ty(&self.body.local_decls, self.tcx); + let fail_out_of_bounds = |this: &Self, location| { + this.fail(location, format!("Out of bounds field {:?} for {:?}", f, parent_ty)); + }; + let check_equal = |this: &Self, location, f_ty| { + if !this.mir_assign_valid_types(ty, f_ty) { + this.fail( + location, + format!( + "Field projection `{:?}.{:?}` specified type `{:?}`, but actual type is {:?}", + parent, f, ty, f_ty + ) + ) + } + }; + match parent_ty.ty.kind() { + ty::Tuple(fields) => { + let Some(f_ty) = fields.get(f.as_usize()) else { + fail_out_of_bounds(self, location); + return; + }; + check_equal(self, location, *f_ty); + } + ty::Adt(adt_def, substs) => { + let var = parent_ty.variant_index.unwrap_or(VariantIdx::from_u32(0)); + let Some(field) = adt_def.variant(var).fields.get(f.as_usize()) else { + fail_out_of_bounds(self, location); + return; + }; + check_equal(self, location, field.ty(self.tcx, substs)); + } + ty::Closure(_, substs) => { + let substs = substs.as_closure(); + let Some(f_ty) = substs.upvar_tys().nth(f.as_usize()) else { + fail_out_of_bounds(self, location); + return; + }; + check_equal(self, location, f_ty); + } + ty::Generator(_, substs, _) => { + let substs = substs.as_generator(); + let Some(f_ty) = substs.upvar_tys().nth(f.as_usize()) else { + fail_out_of_bounds(self, location); + return; + }; + check_equal(self, location, f_ty); + } + _ => { + self.fail(location, format!("{:?} does not have fields", parent_ty.ty)); + } + } + } self.super_projection_elem(local, proj_base, elem, context, location); } @@ -291,7 +346,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ty::Array(..) | ty::Slice(..) ); } - Rvalue::BinaryOp(op, vals) | Rvalue::CheckedBinaryOp(op, vals) => { + Rvalue::BinaryOp(op, vals) => { use BinOp::*; let a = vals.0.ty(&self.body.local_decls, self.tcx); let b = vals.1.ty(&self.body.local_decls, self.tcx); @@ -355,17 +410,55 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { for x in [a, b] { check_kinds!( x, - "Cannot perform op on type {:?}", + "Cannot perform arithmetic on type {:?}", ty::Uint(..) | ty::Int(..) | ty::Float(..) ) } if a != b { self.fail( location, - format!("Cannot perform op on unequal types {:?} and {:?}", a, b), + format!( + "Cannot perform arithmetic on unequal types {:?} and {:?}", + a, b + ), + ); + } + } + } + } + Rvalue::CheckedBinaryOp(op, vals) => { + use BinOp::*; + let a = vals.0.ty(&self.body.local_decls, self.tcx); + let b = vals.1.ty(&self.body.local_decls, self.tcx); + match op { + Add | Sub | Mul => { + for x in [a, b] { + check_kinds!( + x, + "Cannot perform checked arithmetic on type {:?}", + ty::Uint(..) | ty::Int(..) + ) + } + if a != b { + self.fail( + location, + format!( + "Cannot perform checked arithmetic on unequal types {:?} and {:?}", + a, b + ), ); } } + Shl | Shr => { + for x in [a, b] { + check_kinds!( + x, + "Cannot perform checked shift on non-integer type {:?}", + ty::Uint(..) | ty::Int(..) + ) + } + } + _ => self.fail(location, format!("There is no checked version of {:?}", op)), } } Rvalue::UnaryOp(op, operand) => { @@ -609,8 +702,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } let all_len = self.place_cache.len(); - self.place_cache.sort_unstable(); - self.place_cache.dedup(); + let mut dedup = FxHashSet::default(); + self.place_cache.retain(|p| dedup.insert(*p)); let has_duplicates = all_len != self.place_cache.len(); if has_duplicates { self.fail( diff --git a/compiler/rustc_data_structures/src/frozen.rs b/compiler/rustc_data_structures/src/frozen.rs index 2daf5b04141..c81e1b124f0 100644 --- a/compiler/rustc_data_structures/src/frozen.rs +++ b/compiler/rustc_data_structures/src/frozen.rs @@ -23,7 +23,8 @@ //! `computed` does not change accidentally (e.g. somebody might accidentally call //! `foo.computed.mutate()`). This is what `Frozen` is for. We can do the following: //! -//! ```rust +//! ``` +//! # struct Bar {} //! use rustc_data_structures::frozen::Frozen; //! //! struct Foo { diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 5d42f8c9306..76ae17f28c6 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -17,6 +17,7 @@ #![feature(generators)] #![feature(let_else)] #![feature(hash_raw_entry)] +#![feature(hasher_prefixfree_extras)] #![feature(maybe_uninit_uninit_array)] #![feature(min_specialization)] #![feature(never_type)] diff --git a/compiler/rustc_data_structures/src/obligation_forest/mod.rs b/compiler/rustc_data_structures/src/obligation_forest/mod.rs index 5fe2a1fb84b..74f432a7967 100644 --- a/compiler/rustc_data_structures/src/obligation_forest/mod.rs +++ b/compiler/rustc_data_structures/src/obligation_forest/mod.rs @@ -202,7 +202,7 @@ impl<O> Node<O> { /// with this node. /// /// The non-`Error` state transitions are as follows. -/// ``` +/// ```text /// (Pre-creation) /// | /// | register_obligation_at() (called by process_obligations() and diff --git a/compiler/rustc_data_structures/src/owning_ref/mod.rs b/compiler/rustc_data_structures/src/owning_ref/mod.rs index e7397bf13ba..ed5e566184f 100644 --- a/compiler/rustc_data_structures/src/owning_ref/mod.rs +++ b/compiler/rustc_data_structures/src/owning_ref/mod.rs @@ -25,9 +25,8 @@ of the reference because the backing allocation of the vector does not change. This library enables this safe usage by keeping the owner and the reference bundled together in a wrapper type that ensure that lifetime constraint: -```rust -# extern crate owning_ref; -# use owning_ref::OwningRef; +``` +# use rustc_data_structures::owning_ref::OwningRef; # fn main() { fn return_owned_and_referenced() -> OwningRef<Vec<u8>, [u8]> { let v = vec![1, 2, 3, 4]; @@ -56,8 +55,7 @@ See the documentation around `OwningHandle` for more details. ## Basics ``` -extern crate owning_ref; -use owning_ref::BoxRef; +use rustc_data_structures::owning_ref::BoxRef; fn main() { // Create an array owned by a Box. @@ -78,8 +76,7 @@ fn main() { ## Caching a reference to a struct field ``` -extern crate owning_ref; -use owning_ref::BoxRef; +use rustc_data_structures::owning_ref::BoxRef; fn main() { struct Foo { @@ -106,8 +103,7 @@ fn main() { ## Caching a reference to an entry in a vector ``` -extern crate owning_ref; -use owning_ref::VecRef; +use rustc_data_structures::owning_ref::VecRef; fn main() { let v = VecRef::new(vec![1, 2, 3, 4, 5]).map(|v| &v[3]); @@ -118,8 +114,7 @@ fn main() { ## Caching a subslice of a String ``` -extern crate owning_ref; -use owning_ref::StringRef; +use rustc_data_structures::owning_ref::StringRef; fn main() { let s = StringRef::new("hello world".to_owned()) @@ -132,8 +127,7 @@ fn main() { ## Reference counted slices that share ownership of the backing storage ``` -extern crate owning_ref; -use owning_ref::RcRef; +use rustc_data_structures::owning_ref::RcRef; use std::rc::Rc; fn main() { @@ -155,8 +149,7 @@ fn main() { ## Atomic reference counted slices that share ownership of the backing storage ``` -extern crate owning_ref; -use owning_ref::ArcRef; +use rustc_data_structures::owning_ref::ArcRef; use std::sync::Arc; fn main() { @@ -188,8 +181,7 @@ fn main() { ## References into RAII locks ``` -extern crate owning_ref; -use owning_ref::RefRef; +use rustc_data_structures::owning_ref::RefRef; use std::cell::{RefCell, Ref}; fn main() { @@ -219,8 +211,7 @@ When the owned container implements `DerefMut`, it is also possible to make a _mutable owning reference_. (e.g., with `Box`, `RefMut`, `MutexGuard`) ``` -extern crate owning_ref; -use owning_ref::RefMutRefMut; +use rustc_data_structures::owning_ref::RefMutRefMut; use std::cell::{RefCell, RefMut}; fn main() { @@ -326,8 +317,7 @@ impl<O, T: ?Sized> OwningRef<O, T> { /// /// # Example /// ``` - /// extern crate owning_ref; - /// use owning_ref::OwningRef; + /// use rustc_data_structures::owning_ref::OwningRef; /// /// fn main() { /// let owning_ref = OwningRef::new(Box::new(42)); @@ -362,8 +352,7 @@ impl<O, T: ?Sized> OwningRef<O, T> { /// /// # Example /// ``` - /// extern crate owning_ref; - /// use owning_ref::OwningRef; + /// use rustc_data_structures::owning_ref::OwningRef; /// /// fn main() { /// let owning_ref = OwningRef::new(Box::new([1, 2, 3, 4])); @@ -390,8 +379,7 @@ impl<O, T: ?Sized> OwningRef<O, T> { /// /// # Example /// ``` - /// extern crate owning_ref; - /// use owning_ref::OwningRef; + /// use rustc_data_structures::owning_ref::OwningRef; /// /// fn main() { /// let owning_ref = OwningRef::new(Box::new([1, 2, 3, 4])); @@ -441,8 +429,7 @@ impl<O, T: ?Sized> OwningRef<O, T> { /// /// # Example /// ``` - /// extern crate owning_ref; - /// use owning_ref::{OwningRef, Erased}; + /// use rustc_data_structures::owning_ref::{OwningRef, Erased}; /// /// fn main() { /// // N.B., using the concrete types here for explicitness. @@ -460,7 +447,7 @@ impl<O, T: ?Sized> OwningRef<O, T> { /// let owning_ref_b: OwningRef<Box<Vec<(i32, bool)>>, i32> /// = owning_ref_b.map(|a| &a[1].0); /// - /// let owning_refs: [OwningRef<Box<Erased>, i32>; 2] + /// let owning_refs: [OwningRef<Box<dyn Erased>, i32>; 2] /// = [owning_ref_a.erase_owner(), owning_ref_b.erase_owner()]; /// /// assert_eq!(*owning_refs[0], 1); @@ -516,8 +503,7 @@ impl<O, T: ?Sized> OwningRefMut<O, T> { /// /// # Example /// ``` - /// extern crate owning_ref; - /// use owning_ref::OwningRefMut; + /// use rustc_data_structures::owning_ref::OwningRefMut; /// /// fn main() { /// let owning_ref_mut = OwningRefMut::new(Box::new(42)); @@ -552,8 +538,7 @@ impl<O, T: ?Sized> OwningRefMut<O, T> { /// /// # Example /// ``` - /// extern crate owning_ref; - /// use owning_ref::OwningRefMut; + /// use rustc_data_structures::owning_ref::OwningRefMut; /// /// fn main() { /// let owning_ref_mut = OwningRefMut::new(Box::new([1, 2, 3, 4])); @@ -580,8 +565,7 @@ impl<O, T: ?Sized> OwningRefMut<O, T> { /// /// # Example /// ``` - /// extern crate owning_ref; - /// use owning_ref::OwningRefMut; + /// use rustc_data_structures::owning_ref::OwningRefMut; /// /// fn main() { /// let owning_ref_mut = OwningRefMut::new(Box::new([1, 2, 3, 4])); @@ -608,8 +592,7 @@ impl<O, T: ?Sized> OwningRefMut<O, T> { /// /// # Example /// ``` - /// extern crate owning_ref; - /// use owning_ref::OwningRefMut; + /// use rustc_data_structures::owning_ref::OwningRefMut; /// /// fn main() { /// let owning_ref_mut = OwningRefMut::new(Box::new([1, 2, 3, 4])); @@ -638,8 +621,7 @@ impl<O, T: ?Sized> OwningRefMut<O, T> { /// /// # Example /// ``` - /// extern crate owning_ref; - /// use owning_ref::OwningRefMut; + /// use rustc_data_structures::owning_ref::OwningRefMut; /// /// fn main() { /// let owning_ref_mut = OwningRefMut::new(Box::new([1, 2, 3, 4])); @@ -689,8 +671,7 @@ impl<O, T: ?Sized> OwningRefMut<O, T> { /// /// # Example /// ``` - /// extern crate owning_ref; - /// use owning_ref::{OwningRefMut, Erased}; + /// use rustc_data_structures::owning_ref::{OwningRefMut, Erased}; /// /// fn main() { /// // N.B., using the concrete types here for explicitness. @@ -708,7 +689,7 @@ impl<O, T: ?Sized> OwningRefMut<O, T> { /// let owning_ref_mut_b: OwningRefMut<Box<Vec<(i32, bool)>>, i32> /// = owning_ref_mut_b.map_mut(|a| &mut a[1].0); /// - /// let owning_refs_mut: [OwningRefMut<Box<Erased>, i32>; 2] + /// let owning_refs_mut: [OwningRefMut<Box<dyn Erased>, i32>; 2] /// = [owning_ref_mut_a.erase_owner(), owning_ref_mut_b.erase_owner()]; /// /// assert_eq!(*owning_refs_mut[0], 1); diff --git a/compiler/rustc_data_structures/src/profiling.rs b/compiler/rustc_data_structures/src/profiling.rs index 3f7a90a8467..bf7924a81ff 100644 --- a/compiler/rustc_data_structures/src/profiling.rs +++ b/compiler/rustc_data_structures/src/profiling.rs @@ -737,11 +737,30 @@ impl Drop for VerboseTimingGuard<'_> { fn drop(&mut self) { if let Some((start_time, start_rss, ref message)) = self.start_and_message { let end_rss = get_resident_set_size(); - print_time_passes_entry(&message, start_time.elapsed(), start_rss, end_rss); + let dur = start_time.elapsed(); + + if should_print_passes(dur, start_rss, end_rss) { + print_time_passes_entry(&message, dur, start_rss, end_rss); + } } } } +fn should_print_passes(dur: Duration, start_rss: Option<usize>, end_rss: Option<usize>) -> bool { + if dur.as_millis() > 5 { + return true; + } + + if let (Some(start_rss), Some(end_rss)) = (start_rss, end_rss) { + let change_rss = end_rss.abs_diff(start_rss); + if change_rss > 0 { + return true; + } + } + + false +} + pub fn print_time_passes_entry( what: &str, dur: Duration, diff --git a/compiler/rustc_data_structures/src/sip128.rs b/compiler/rustc_data_structures/src/sip128.rs index 6e5c0617bf3..abd25f46ad5 100644 --- a/compiler/rustc_data_structures/src/sip128.rs +++ b/compiler/rustc_data_structures/src/sip128.rs @@ -462,6 +462,14 @@ impl Hasher for SipHasher128 { self.slice_write(msg); } + #[inline] + fn write_str(&mut self, s: &str) { + // This hasher works byte-wise, and `0xFF` cannot show up in a `str`, + // so just hashing the one extra byte is enough to be prefix-free. + self.write(s.as_bytes()); + self.write_u8(0xFF); + } + fn finish(&self) -> u64 { panic!("SipHasher128 cannot provide valid 64 bit hashes") } diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index 25353290fd5..c8bb4fc5e6a 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -74,6 +74,17 @@ impl Hasher for StableHasher { } #[inline] + fn write_str(&mut self, s: &str) { + self.state.write_str(s); + } + + #[inline] + fn write_length_prefix(&mut self, len: usize) { + // Our impl for `usize` will extend it if needed. + self.write_usize(len); + } + + #[inline] fn write_u8(&mut self, i: u8) { self.state.write_u8(i); } diff --git a/compiler/rustc_data_structures/src/tagged_ptr.rs b/compiler/rustc_data_structures/src/tagged_ptr.rs index 324a8624dd0..651bc556c98 100644 --- a/compiler/rustc_data_structures/src/tagged_ptr.rs +++ b/compiler/rustc_data_structures/src/tagged_ptr.rs @@ -45,7 +45,8 @@ pub unsafe trait Pointer: Deref { /// case you'll need to manually figure out what the right type to pass to /// align_of is. /// - /// ```rust + /// ```ignore UNSOLVED (what to do about the Self) + /// # use std::ops::Deref; /// std::mem::align_of::<<Self as Deref>::Target>().trailing_zeros() as usize; /// ``` const BITS: usize; diff --git a/compiler/rustc_data_structures/src/transitive_relation.rs b/compiler/rustc_data_structures/src/transitive_relation.rs index 780753ed200..0ff64969b07 100644 --- a/compiler/rustc_data_structures/src/transitive_relation.rs +++ b/compiler/rustc_data_structures/src/transitive_relation.rs @@ -282,7 +282,7 @@ impl<T: Eq + Hash + Copy> TransitiveRelation<T> { /// (where the relation is encoding the `<=` relation for the lattice). /// So e.g., if the relation is `->` and we have /// - /// ``` + /// ```text /// a -> b -> d -> f /// | ^ /// +--> c -> e ---+ diff --git a/compiler/rustc_error_codes/src/error_codes/E0455.md b/compiler/rustc_error_codes/src/error_codes/E0455.md index 84689b3ece6..437dacaff22 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0455.md +++ b/compiler/rustc_error_codes/src/error_codes/E0455.md @@ -1,6 +1,11 @@ +Some linking kinds are target-specific and not supported on all platforms. + Linking with `kind=framework` is only supported when targeting macOS, as frameworks are specific to that operating system. +Similarly, `kind=raw-dylib` is only supported when targeting Windows-like +platforms. + Erroneous code example: ```ignore (should-compile_fail-but-cannot-doctest-conditionally-without-macos) diff --git a/compiler/rustc_error_codes/src/error_codes/E0458.md b/compiler/rustc_error_codes/src/error_codes/E0458.md index 359aeb6fd9a..1b280cba44f 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0458.md +++ b/compiler/rustc_error_codes/src/error_codes/E0458.md @@ -12,3 +12,4 @@ Please specify a valid "kind" value, from one of the following: * static * dylib * framework +* raw-dylib diff --git a/compiler/rustc_error_codes/src/error_codes/E0539.md b/compiler/rustc_error_codes/src/error_codes/E0539.md index df2d7d910bb..c53d60a5f47 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0539.md +++ b/compiler/rustc_error_codes/src/error_codes/E0539.md @@ -6,7 +6,7 @@ Erroneous code example: #![feature(staged_api)] #![stable(since = "1.0.0", feature = "test")] -#[rustc_deprecated(reason)] // error! +#[deprecated(note)] // error! #[unstable(feature = "deprecated_fn", issue = "123")] fn deprecated() {} @@ -30,7 +30,7 @@ To fix these issues you need to give required key-value pairs. #![feature(staged_api)] #![stable(since = "1.0.0", feature = "test")] -#[rustc_deprecated(since = "1.39.0", reason = "reason")] // ok! +#[deprecated(since = "1.39.0", note = "reason")] // ok! #[unstable(feature = "deprecated_fn", issue = "123")] fn deprecated() {} diff --git a/compiler/rustc_error_codes/src/error_codes/E0542.md b/compiler/rustc_error_codes/src/error_codes/E0542.md index 7fecfeaa57c..c69e574179b 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0542.md +++ b/compiler/rustc_error_codes/src/error_codes/E0542.md @@ -13,8 +13,8 @@ fn _stable_fn() {} const fn _stable_const_fn() {} #[stable(feature = "_deprecated_fn", since = "0.1.0")] -#[rustc_deprecated( - reason = "explanation for deprecation" +#[deprecated( + note = "explanation for deprecation" )] // invalid fn _deprecated_fn() {} ``` @@ -32,9 +32,9 @@ fn _stable_fn() {} const fn _stable_const_fn() {} #[stable(feature = "_deprecated_fn", since = "0.1.0")] -#[rustc_deprecated( +#[deprecated( since = "1.0.0", - reason = "explanation for deprecation" + note = "explanation for deprecation" )] // ok! fn _deprecated_fn() {} ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0543.md b/compiler/rustc_error_codes/src/error_codes/E0543.md index ba26f92e89f..d0b2e2f7a7d 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0543.md +++ b/compiler/rustc_error_codes/src/error_codes/E0543.md @@ -1,4 +1,4 @@ -The `reason` value is missing in a stability attribute. +The `note` value is missing in a stability attribute. Erroneous code example: @@ -7,22 +7,22 @@ Erroneous code example: #![stable(since = "1.0.0", feature = "test")] #[stable(since = "0.1.0", feature = "_deprecated_fn")] -#[rustc_deprecated( +#[deprecated( since = "1.0.0" )] // invalid fn _deprecated_fn() {} ``` -To fix this issue, you need to provide the `reason` field. Example: +To fix this issue, you need to provide the `note` field. Example: ``` #![feature(staged_api)] #![stable(since = "1.0.0", feature = "test")] #[stable(since = "0.1.0", feature = "_deprecated_fn")] -#[rustc_deprecated( +#[deprecated( since = "1.0.0", - reason = "explanation for deprecation" + note = "explanation for deprecation" )] // ok! fn _deprecated_fn() {} ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0549.md b/compiler/rustc_error_codes/src/error_codes/E0549.md index d4b78e7e0d6..70e458a9867 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0549.md +++ b/compiler/rustc_error_codes/src/error_codes/E0549.md @@ -1,5 +1,5 @@ -A `rustc_deprecated` attribute wasn't paired with a `stable`/`unstable` -attribute. +A `deprecated` attribute wasn't paired with a `stable`/`unstable` attribute with +`#![feature(staged_api)]` enabled. Erroneous code example: @@ -7,9 +7,9 @@ Erroneous code example: #![feature(staged_api)] #![stable(since = "1.0.0", feature = "test")] -#[rustc_deprecated( +#[deprecated( since = "1.0.1", - reason = "explanation for deprecation" + note = "explanation for deprecation" )] // invalid fn _deprecated_fn() {} ``` @@ -22,9 +22,9 @@ Example: #![stable(since = "1.0.0", feature = "test")] #[stable(since = "1.0.0", feature = "test")] -#[rustc_deprecated( +#[deprecated( since = "1.0.1", - reason = "explanation for deprecation" + note = "explanation for deprecation" )] // ok! fn _deprecated_fn() {} ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0550.md b/compiler/rustc_error_codes/src/error_codes/E0550.md index 1487d701847..6aac5c969d2 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0550.md +++ b/compiler/rustc_error_codes/src/error_codes/E0550.md @@ -1,8 +1,10 @@ +#### Note: this error code is no longer emitted by the compiler + More than one `deprecated` attribute has been put on an item. Erroneous code example: -```compile_fail,E0550 +```compile_fail #[deprecated(note = "because why not?")] #[deprecated(note = "right?")] // error! fn the_banished() {} diff --git a/compiler/rustc_error_codes/src/error_codes/E0734.md b/compiler/rustc_error_codes/src/error_codes/E0734.md index 4b8e89a7060..b912061ec42 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0734.md +++ b/compiler/rustc_error_codes/src/error_codes/E0734.md @@ -3,7 +3,6 @@ A stability attribute has been used outside of the standard library. Erroneous code example: ```compile_fail,E0734 -#[rustc_deprecated(since = "b", reason = "text")] // invalid #[stable(feature = "a", since = "b")] // invalid #[unstable(feature = "b", issue = "none")] // invalid fn foo(){} diff --git a/compiler/rustc_error_messages/locales/en-US/parser.ftl b/compiler/rustc_error_messages/locales/en-US/parser.ftl index 3143b81b609..c98989b23c1 100644 --- a/compiler/rustc_error_messages/locales/en-US/parser.ftl +++ b/compiler/rustc_error_messages/locales/en-US/parser.ftl @@ -5,3 +5,12 @@ parser-struct-literal-body-without-path = parser-maybe-report-ambiguous-plus = ambiguous `+` in a type .suggestion = use parentheses to disambiguate + +parser-maybe-recover-from-bad-type-plus = + expected a path on the left-hand side of `+`, not `{$ty}` + +parser-add-paren = try adding parentheses + +parser-forgot-paren = perhaps you forgot parentheses? + +parser-expect-path = expected a path diff --git a/compiler/rustc_error_messages/locales/en-US/typeck.ftl b/compiler/rustc_error_messages/locales/en-US/typeck.ftl index 6a3235fc772..95b348ec613 100644 --- a/compiler/rustc_error_messages/locales/en-US/typeck.ftl +++ b/compiler/rustc_error_messages/locales/en-US/typeck.ftl @@ -45,6 +45,7 @@ typeck-copy-impl-on-non-adt = typeck-trait-object-declared-with-no-traits = at least one trait is required for an object type + .alias-span = this alias does not contain a trait typeck-ambiguous-lifetime-bound = ambiguous lifetime bound, explicit lifetime bound required @@ -90,3 +91,41 @@ typeck-add-return-type-missing-here = a return type might be missing here typeck-expected-default-return-type = expected `()` because of default return type typeck-expected-return-type = expected `{$expected}` because of return type + +typeck-unconstrained-opaque-type = unconstrained opaque type + .note = `{$name}` must be used in combination with a concrete type within the same module + +typeck-explicit-generic-args-with-impl-trait = + cannot provide explicit generic arguments when `impl Trait` is used in argument position + .label = explicit generic argument not allowed + .note = see issue #83701 <https://github.com/rust-lang/rust/issues/83701> for more information + .help = add `#![feature(explicit_generic_args_with_impl_trait)]` to the crate attributes to enable + +typeck-missing-type-params = + the type {$parameterCount -> + [one] parameter + *[other] parameters + } {$parameters} must be explicitly specified + .label = type {$parameterCount -> + [one] parameter + *[other] parameters + } {$parameters} must be specified for this + .suggestion = set the type {$parameterCount -> + [one] parameter + *[other] parameters + } to the desired {$parameterCount -> + [one] type + *[other] types + } + .no-suggestion-label = missing {$parameterCount -> + [one] reference + *[other] references + } to {$parameters} + .note = because of the default `Self` reference, type parameters must be specified on object types + +typeck-manual-implementation = + manual implementations of `{$trait_name}` are experimental + .label = manual implementations of `{$trait_name}` are experimental + .help = add `#![feature(unboxed_closures)]` to the crate attributes to enable + +typeck-substs-on-overridden-impl = could not resolve substs on overridden impl diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 83e6a751394..f130b5aa9a6 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -90,7 +90,7 @@ pub trait AddSubdiagnostic { pub struct Diagnostic { // NOTE(eddyb) this is private to disallow arbitrary after-the-fact changes, // outside of what methods in this crate themselves allow. - crate level: Level, + pub(crate) level: Level, pub message: Vec<(DiagnosticMessage, Style)>, pub code: Option<DiagnosticId>, @@ -821,9 +821,9 @@ impl Diagnostic { pub fn set_arg( &mut self, name: impl Into<Cow<'static, str>>, - arg: DiagnosticArgValue<'static>, + arg: impl IntoDiagnosticArg, ) -> &mut Self { - self.args.push((name.into(), arg)); + self.args.push((name.into(), arg.into_diagnostic_arg())); self } diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index 96b730c2baa..6ef2c832c65 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -1,4 +1,4 @@ -use crate::diagnostic::DiagnosticArgValue; +use crate::diagnostic::IntoDiagnosticArg; use crate::{Diagnostic, DiagnosticId, DiagnosticMessage, DiagnosticStyledString, ErrorGuaranteed}; use crate::{Handler, Level, MultiSpan, StashKey}; use rustc_lint_defs::Applicability; @@ -88,7 +88,7 @@ mod sealed_level_is_error { use crate::Level; /// Sealed helper trait for statically checking that a `Level` is an error. - crate trait IsError<const L: Level> {} + pub(crate) trait IsError<const L: Level> {} impl IsError<{ Level::Bug }> for () {} impl IsError<{ Level::DelayedBug }> for () {} @@ -101,7 +101,7 @@ mod sealed_level_is_error { impl<'a> DiagnosticBuilder<'a, ErrorGuaranteed> { /// Convenience function for internal use, clients should use one of the /// `struct_*` methods on [`Handler`]. - crate fn new_guaranteeing_error<M: Into<DiagnosticMessage>, const L: Level>( + pub(crate) fn new_guaranteeing_error<M: Into<DiagnosticMessage>, const L: Level>( handler: &'a Handler, message: M, ) -> Self @@ -168,7 +168,7 @@ impl EmissionGuarantee for ErrorGuaranteed { impl<'a> DiagnosticBuilder<'a, ()> { /// Convenience function for internal use, clients should use one of the /// `struct_*` methods on [`Handler`]. - crate fn new<M: Into<DiagnosticMessage>>( + pub(crate) fn new<M: Into<DiagnosticMessage>>( handler: &'a Handler, level: Level, message: M, @@ -179,7 +179,7 @@ impl<'a> DiagnosticBuilder<'a, ()> { /// Creates a new `DiagnosticBuilder` with an already constructed /// diagnostic. - crate fn new_diagnostic(handler: &'a Handler, diagnostic: Diagnostic) -> Self { + pub(crate) fn new_diagnostic(handler: &'a Handler, diagnostic: Diagnostic) -> Self { debug!("Created new diagnostic"); Self { inner: DiagnosticBuilderInner { @@ -210,14 +210,14 @@ impl EmissionGuarantee for () { impl<'a> DiagnosticBuilder<'a, !> { /// Convenience function for internal use, clients should use one of the /// `struct_*` methods on [`Handler`]. - crate fn new_fatal(handler: &'a Handler, message: impl Into<DiagnosticMessage>) -> Self { + pub(crate) fn new_fatal(handler: &'a Handler, message: impl Into<DiagnosticMessage>) -> Self { let diagnostic = Diagnostic::new_with_code(Level::Fatal, None, message); Self::new_diagnostic_fatal(handler, diagnostic) } /// Creates a new `DiagnosticBuilder` with an already constructed /// diagnostic. - crate fn new_diagnostic_fatal(handler: &'a Handler, diagnostic: Diagnostic) -> Self { + pub(crate) fn new_diagnostic_fatal(handler: &'a Handler, diagnostic: Diagnostic) -> Self { debug!("Created new diagnostic"); Self { inner: DiagnosticBuilderInner { @@ -528,7 +528,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { forward!(pub fn set_arg( &mut self, name: impl Into<Cow<'static, str>>, - arg: DiagnosticArgValue<'static>, + arg: impl IntoDiagnosticArg, ) -> &mut Self); forward!(pub fn subdiagnostic( diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index df41fc00714..d2f50d5df54 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -3,7 +3,6 @@ //! This module contains the code for creating and emitting diagnostics. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![feature(crate_visibility_modifier)] #![feature(drain_filter)] #![feature(backtrace)] #![feature(if_let_guard)] @@ -100,7 +99,7 @@ pub struct CodeSuggestion { /// `foo.bar` might be replaced with `a.b` or `x.y` by replacing /// `foo` and `bar` on their own: /// - /// ``` + /// ```ignore (illustrative) /// vec![ /// Substitution { parts: vec![(0..3, "a"), (4..7, "b")] }, /// Substitution { parts: vec![(0..3, "x"), (4..7, "y")] }, @@ -109,7 +108,7 @@ pub struct CodeSuggestion { /// /// or by replacing the entire span: /// - /// ``` + /// ```ignore (illustrative) /// vec![ /// Substitution { parts: vec![(0..7, "a.b")] }, /// Substitution { parts: vec![(0..7, "x.y")] }, @@ -426,6 +425,13 @@ struct HandlerInner { future_breakage_diagnostics: Vec<Diagnostic>, + /// The [`Self::unstable_expect_diagnostics`] should be empty when this struct is + /// dropped. However, it can have values if the compilation is stopped early + /// or is only partially executed. To avoid ICEs, like in rust#94953 we only + /// check if [`Self::unstable_expect_diagnostics`] is empty, if the expectation ids + /// have been converted. + check_unstable_expect_diagnostics: bool, + /// Expected [`Diagnostic`]s store a [`LintExpectationId`] as part of /// the lint level. [`LintExpectationId`]s created early during the compilation /// (before `HirId`s have been defined) are not stable and can therefore not be @@ -497,10 +503,12 @@ impl Drop for HandlerInner { ); } - assert!( - self.unstable_expect_diagnostics.is_empty(), - "all diagnostics with unstable expectations should have been converted", - ); + if self.check_unstable_expect_diagnostics { + assert!( + self.unstable_expect_diagnostics.is_empty(), + "all diagnostics with unstable expectations should have been converted", + ); + } } } @@ -574,6 +582,7 @@ impl Handler { emitted_diagnostics: Default::default(), stashed_diagnostics: Default::default(), future_breakage_diagnostics: Vec::new(), + check_unstable_expect_diagnostics: false, unstable_expect_diagnostics: Vec::new(), fulfilled_expectations: Default::default(), }), @@ -988,12 +997,13 @@ impl Handler { &self, unstable_to_stable: &FxHashMap<LintExpectationId, LintExpectationId>, ) { - let diags = std::mem::take(&mut self.inner.borrow_mut().unstable_expect_diagnostics); + let mut inner = self.inner.borrow_mut(); + let diags = std::mem::take(&mut inner.unstable_expect_diagnostics); + inner.check_unstable_expect_diagnostics = true; if diags.is_empty() { return; } - let mut inner = self.inner.borrow_mut(); for mut diag in diags.into_iter() { diag.update_unstable_expectation_id(unstable_to_stable); diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 2b30ec601a0..e48ce602185 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -4,16 +4,16 @@ use crate::module::DirOwnership; use rustc_ast::attr::MarkedAttrs; use rustc_ast::ptr::P; use rustc_ast::token::{self, Nonterminal}; -use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, TokenStream}; +use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{AssocCtxt, Visitor}; -use rustc_ast::{self as ast, AstLike, Attribute, Item, NodeId, PatKind}; +use rustc_ast::{self as ast, Attribute, HasAttrs, Item, NodeId, PatKind}; use rustc_attr::{self as attr, Deprecation, Stability}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::{self, Lrc}; use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, PResult}; use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT; use rustc_lint_defs::BuiltinLintDiagnostics; -use rustc_parse::{self, nt_to_tokenstream, parser, MACRO_ARGUMENTS}; +use rustc_parse::{self, parser, MACRO_ARGUMENTS}; use rustc_session::{parse::ParseSess, Limit, Session}; use rustc_span::def_id::{CrateNum, DefId, LocalDefId}; use rustc_span::edition::Edition; @@ -28,7 +28,7 @@ use std::iter; use std::path::PathBuf; use std::rc::Rc; -crate use rustc_span::hygiene::MacroKind; +pub(crate) use rustc_span::hygiene::MacroKind; // When adding new variants, make sure to // adjust the `visit_*` / `flat_map_*` calls in `InvocationCollector` @@ -109,17 +109,18 @@ impl Annotatable { } } - pub fn into_nonterminal(self) -> Nonterminal { + pub fn to_tokens(&self) -> TokenStream { match self { - Annotatable::Item(item) => token::NtItem(item), - Annotatable::TraitItem(item) | Annotatable::ImplItem(item) => { - token::NtItem(P(item.and_then(ast::AssocItem::into_item))) + Annotatable::Item(node) => TokenStream::from_ast(node), + Annotatable::TraitItem(node) | Annotatable::ImplItem(node) => { + TokenStream::from_ast(node) } - Annotatable::ForeignItem(item) => { - token::NtItem(P(item.and_then(ast::ForeignItem::into_item))) + Annotatable::ForeignItem(node) => TokenStream::from_ast(node), + Annotatable::Stmt(node) => { + assert!(!matches!(node.kind, ast::StmtKind::Empty)); + TokenStream::from_ast(node) } - Annotatable::Stmt(stmt) => token::NtStmt(stmt), - Annotatable::Expr(expr) => token::NtExpr(expr), + Annotatable::Expr(node) => TokenStream::from_ast(node), Annotatable::Arm(..) | Annotatable::ExprField(..) | Annotatable::PatField(..) @@ -131,10 +132,6 @@ impl Annotatable { } } - crate fn into_tokens(self, sess: &ParseSess) -> TokenStream { - nt_to_tokenstream(&self.into_nonterminal(), sess, CanSynthesizeMissingTokens::No) - } - pub fn expect_item(self) -> P<ast::Item> { match self { Annotatable::Item(i) => i, @@ -887,6 +884,8 @@ pub trait ResolverExpand { force: bool, ) -> Result<Lrc<SyntaxExtension>, Indeterminate>; + fn record_macro_rule_usage(&mut self, mac_id: NodeId, rule_index: usize); + fn check_unused_macros(&mut self); // Resolver interfaces for specific built-in macros. @@ -1380,16 +1379,7 @@ pub fn parse_macro_name_and_helper_attrs( /// asserts in old versions of those crates and their wide use in the ecosystem. /// See issue #73345 for more details. /// FIXME(#73933): Remove this eventually. -pub(crate) fn pretty_printing_compatibility_hack(nt: &Nonterminal, sess: &ParseSess) -> bool { - let item = match nt { - Nonterminal::NtItem(item) => item, - Nonterminal::NtStmt(stmt) => match &stmt.kind { - ast::StmtKind::Item(item) => item, - _ => return false, - }, - _ => return false, - }; - +fn pretty_printing_compatibility_hack(item: &Item, sess: &ParseSess) -> bool { let name = item.ident.name; if name == sym::ProceduralMasqueradeDummyType { if let ast::ItemKind::Enum(enum_def, _) = &item.kind { @@ -1411,3 +1401,27 @@ pub(crate) fn pretty_printing_compatibility_hack(nt: &Nonterminal, sess: &ParseS } false } + +pub(crate) fn ann_pretty_printing_compatibility_hack(ann: &Annotatable, sess: &ParseSess) -> bool { + let item = match ann { + Annotatable::Item(item) => item, + Annotatable::Stmt(stmt) => match &stmt.kind { + ast::StmtKind::Item(item) => item, + _ => return false, + }, + _ => return false, + }; + pretty_printing_compatibility_hack(item, sess) +} + +pub(crate) fn nt_pretty_printing_compatibility_hack(nt: &Nonterminal, sess: &ParseSess) -> bool { + let item = match nt { + Nonterminal::NtItem(item) => item, + Nonterminal::NtStmt(stmt) => match &stmt.kind { + ast::StmtKind::Item(item) => item, + _ => return false, + }, + _ => return false, + }; + pretty_printing_compatibility_hack(item, sess) +} diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index c91125105d7..3cada372570 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -6,7 +6,7 @@ use rustc_ast::tokenstream::{AttrAnnotatedTokenStream, AttrAnnotatedTokenTree}; use rustc_ast::tokenstream::{DelimSpan, Spacing}; use rustc_ast::tokenstream::{LazyTokenStream, TokenTree}; use rustc_ast::NodeId; -use rustc_ast::{self as ast, AstLike, AttrStyle, Attribute, MetaItem}; +use rustc_ast::{self as ast, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem}; use rustc_attr as attr; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::map_in_place::MapInPlace; @@ -246,7 +246,7 @@ macro_rules! configure { } impl<'a> StripUnconfigured<'a> { - pub fn configure<T: AstLike>(&self, mut node: T) -> Option<T> { + pub fn configure<T: HasAttrs + HasTokens>(&self, mut node: T) -> Option<T> { self.process_cfg_attrs(&mut node); if self.in_cfg(node.attrs()) { self.try_configure_tokens(&mut node); @@ -256,7 +256,7 @@ impl<'a> StripUnconfigured<'a> { } } - fn try_configure_tokens<T: AstLike>(&self, node: &mut T) { + fn try_configure_tokens<T: HasTokens>(&self, node: &mut T) { if self.config_tokens { if let Some(Some(tokens)) = node.tokens_mut() { let attr_annotated_tokens = tokens.create_token_stream(); @@ -330,7 +330,7 @@ impl<'a> StripUnconfigured<'a> { /// Gives compiler warnings if any `cfg_attr` does not contain any /// attributes and is in the original source code. Gives compiler errors if /// the syntax of any `cfg_attr` is incorrect. - fn process_cfg_attrs<T: AstLike>(&self, node: &mut T) { + fn process_cfg_attrs<T: HasAttrs>(&self, node: &mut T) { node.visit_attrs(|attrs| { attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr)); }); @@ -347,7 +347,7 @@ impl<'a> StripUnconfigured<'a> { /// Gives a compiler warning when the `cfg_attr` contains no attributes and /// is in the original source file. Gives a compiler error if the syntax of /// the attribute is incorrect. - crate fn expand_cfg_attr(&self, attr: Attribute, recursive: bool) -> Vec<Attribute> { + pub(crate) fn expand_cfg_attr(&self, attr: Attribute, recursive: bool) -> Vec<Attribute> { let Some((cfg_predicate, expanded_attrs)) = rustc_parse::parse_cfg_attr(&attr, &self.sess.parse_sess) else { return vec![]; @@ -400,7 +400,7 @@ impl<'a> StripUnconfigured<'a> { // Use the `#` in `#[cfg_attr(pred, attr)]` as the `#` token // for `attr` when we expand it to `#[attr]` - let mut orig_trees = orig_tokens.trees(); + let mut orig_trees = orig_tokens.into_trees(); let TokenTree::Token(pound_token @ Token { kind: TokenKind::Pound, .. }) = orig_trees.next().unwrap() else { panic!("Bad tokens for attribute {:?}", attr); }; @@ -451,7 +451,7 @@ impl<'a> StripUnconfigured<'a> { attrs.iter().all(|attr| !is_cfg(attr) || self.cfg_true(attr)) } - crate fn cfg_true(&self, attr: &Attribute) -> bool { + pub(crate) fn cfg_true(&self, attr: &Attribute) -> bool { let meta_item = match validate_attr::parse_meta(&self.sess.parse_sess, attr) { Ok(meta_item) => meta_item, Err(mut err) => { @@ -465,7 +465,7 @@ impl<'a> StripUnconfigured<'a> { } /// If attributes are not allowed on expressions, emit an error for `attr` - crate fn maybe_emit_expr_attr_err(&self, attr: &Attribute) { + pub(crate) fn maybe_emit_expr_attr_err(&self, attr: &Attribute) { if !self.features.map_or(true, |features| features.stmt_expr_attributes) { let mut err = feature_err( &self.sess.parse_sess, diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 5bd89f3f42f..5af6b777abe 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -11,7 +11,8 @@ use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter}; use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{self, AssocCtxt, Visitor}; -use rustc_ast::{AssocItemKind, AstLike, AstLikeWrapper, AttrStyle, ExprKind, ForeignItemKind}; +use rustc_ast::{AssocItemKind, AstNodeWrapper, AttrStyle, ExprKind, ForeignItemKind}; +use rustc_ast::{HasAttrs, HasNodeId}; use rustc_ast::{Inline, ItemKind, MacArgs, MacStmtStyle, MetaItemKind, ModKind}; use rustc_ast::{NestedMetaItem, NodeId, PatKind, StmtKind, TyKind}; use rustc_ast_pretty::pprust; @@ -213,7 +214,7 @@ pub enum SupportsMacroExpansion { } impl AstFragmentKind { - crate fn dummy(self, span: Span) -> AstFragment { + pub(crate) fn dummy(self, span: Span) -> AstFragment { self.make_from(DummyResult::any(span)).expect("couldn't create a dummy AST fragment") } @@ -678,12 +679,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> { ) ) => { - rustc_parse::fake_token_stream( + rustc_parse::fake_token_stream_for_item( &self.cx.sess.parse_sess, - &item.into_nonterminal(), + item_inner, ) } - _ => item.into_tokens(&self.cx.sess.parse_sess), + _ => item.to_tokens(), }; let attr_item = attr.unwrap_normal_item(); if let MacArgs::Eq(..) = attr_item.args { @@ -998,13 +999,12 @@ enum AddSemicolon { /// A trait implemented for all `AstFragment` nodes and providing all pieces /// of functionality used by `InvocationCollector`. -trait InvocationCollectorNode: AstLike { +trait InvocationCollectorNode: HasAttrs + HasNodeId + Sized { type OutputTy = SmallVec<[Self; 1]>; type AttrsTy: Deref<Target = [ast::Attribute]> = Vec<ast::Attribute>; const KIND: AstFragmentKind; fn to_annotatable(self) -> Annotatable; fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy; - fn id(&mut self) -> &mut NodeId; fn descr() -> &'static str { unreachable!() } @@ -1040,9 +1040,6 @@ impl InvocationCollectorNode for P<ast::Item> { fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { fragment.make_items() } - fn id(&mut self) -> &mut NodeId { - &mut self.id - } fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { noop_flat_map_item(self, visitor) } @@ -1142,7 +1139,7 @@ impl InvocationCollectorNode for P<ast::Item> { } struct TraitItemTag; -impl InvocationCollectorNode for AstLikeWrapper<P<ast::AssocItem>, TraitItemTag> { +impl InvocationCollectorNode for AstNodeWrapper<P<ast::AssocItem>, TraitItemTag> { type OutputTy = SmallVec<[P<ast::AssocItem>; 1]>; const KIND: AstFragmentKind = AstFragmentKind::TraitItems; fn to_annotatable(self) -> Annotatable { @@ -1151,9 +1148,6 @@ impl InvocationCollectorNode for AstLikeWrapper<P<ast::AssocItem>, TraitItemTag> fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { fragment.make_trait_items() } - fn id(&mut self) -> &mut NodeId { - &mut self.wrapped.id - } fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { noop_flat_map_assoc_item(self.wrapped, visitor) } @@ -1170,7 +1164,7 @@ impl InvocationCollectorNode for AstLikeWrapper<P<ast::AssocItem>, TraitItemTag> } struct ImplItemTag; -impl InvocationCollectorNode for AstLikeWrapper<P<ast::AssocItem>, ImplItemTag> { +impl InvocationCollectorNode for AstNodeWrapper<P<ast::AssocItem>, ImplItemTag> { type OutputTy = SmallVec<[P<ast::AssocItem>; 1]>; const KIND: AstFragmentKind = AstFragmentKind::ImplItems; fn to_annotatable(self) -> Annotatable { @@ -1179,9 +1173,6 @@ impl InvocationCollectorNode for AstLikeWrapper<P<ast::AssocItem>, ImplItemTag> fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { fragment.make_impl_items() } - fn id(&mut self) -> &mut NodeId { - &mut self.wrapped.id - } fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { noop_flat_map_assoc_item(self.wrapped, visitor) } @@ -1205,9 +1196,6 @@ impl InvocationCollectorNode for P<ast::ForeignItem> { fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { fragment.make_foreign_items() } - fn id(&mut self) -> &mut NodeId { - &mut self.id - } fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { noop_flat_map_foreign_item(self, visitor) } @@ -1231,9 +1219,6 @@ impl InvocationCollectorNode for ast::Variant { fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { fragment.make_variants() } - fn id(&mut self) -> &mut NodeId { - &mut self.id - } fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { noop_flat_map_variant(self, visitor) } @@ -1247,9 +1232,6 @@ impl InvocationCollectorNode for ast::FieldDef { fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { fragment.make_field_defs() } - fn id(&mut self) -> &mut NodeId { - &mut self.id - } fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { noop_flat_map_field_def(self, visitor) } @@ -1263,9 +1245,6 @@ impl InvocationCollectorNode for ast::PatField { fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { fragment.make_pat_fields() } - fn id(&mut self) -> &mut NodeId { - &mut self.id - } fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { noop_flat_map_pat_field(self, visitor) } @@ -1279,9 +1258,6 @@ impl InvocationCollectorNode for ast::ExprField { fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { fragment.make_expr_fields() } - fn id(&mut self) -> &mut NodeId { - &mut self.id - } fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { noop_flat_map_expr_field(self, visitor) } @@ -1295,9 +1271,6 @@ impl InvocationCollectorNode for ast::Param { fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { fragment.make_params() } - fn id(&mut self) -> &mut NodeId { - &mut self.id - } fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { noop_flat_map_param(self, visitor) } @@ -1311,9 +1284,6 @@ impl InvocationCollectorNode for ast::GenericParam { fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { fragment.make_generic_params() } - fn id(&mut self) -> &mut NodeId { - &mut self.id - } fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { noop_flat_map_generic_param(self, visitor) } @@ -1327,9 +1297,6 @@ impl InvocationCollectorNode for ast::Arm { fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { fragment.make_arms() } - fn id(&mut self) -> &mut NodeId { - &mut self.id - } fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { noop_flat_map_arm(self, visitor) } @@ -1344,9 +1311,6 @@ impl InvocationCollectorNode for ast::Stmt { fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { fragment.make_stmts() } - fn id(&mut self) -> &mut NodeId { - &mut self.id - } fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { noop_flat_map_stmt(self, visitor) } @@ -1403,9 +1367,6 @@ impl InvocationCollectorNode for ast::Crate { fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { fragment.make_crate() } - fn id(&mut self) -> &mut NodeId { - &mut self.id - } fn noop_visit<V: MutVisitor>(&mut self, visitor: &mut V) { noop_visit_crate(self, visitor) } @@ -1420,9 +1381,6 @@ impl InvocationCollectorNode for P<ast::Ty> { fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { fragment.make_ty() } - fn id(&mut self) -> &mut NodeId { - &mut self.id - } fn noop_visit<V: MutVisitor>(&mut self, visitor: &mut V) { noop_visit_ty(self, visitor) } @@ -1447,9 +1405,6 @@ impl InvocationCollectorNode for P<ast::Pat> { fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { fragment.make_pat() } - fn id(&mut self) -> &mut NodeId { - &mut self.id - } fn noop_visit<V: MutVisitor>(&mut self, visitor: &mut V) { noop_visit_pat(self, visitor) } @@ -1475,9 +1430,6 @@ impl InvocationCollectorNode for P<ast::Expr> { fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { fragment.make_expr() } - fn id(&mut self) -> &mut NodeId { - &mut self.id - } fn descr() -> &'static str { "an expression" } @@ -1497,7 +1449,7 @@ impl InvocationCollectorNode for P<ast::Expr> { } struct OptExprTag; -impl InvocationCollectorNode for AstLikeWrapper<P<ast::Expr>, OptExprTag> { +impl InvocationCollectorNode for AstNodeWrapper<P<ast::Expr>, OptExprTag> { type OutputTy = Option<P<ast::Expr>>; type AttrsTy = ast::AttrVec; const KIND: AstFragmentKind = AstFragmentKind::OptExpr; @@ -1507,9 +1459,6 @@ impl InvocationCollectorNode for AstLikeWrapper<P<ast::Expr>, OptExprTag> { fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { fragment.make_opt_expr() } - fn id(&mut self) -> &mut NodeId { - &mut self.wrapped.id - } fn noop_flat_map<V: MutVisitor>(mut self, visitor: &mut V) -> Self::OutputTy { noop_visit_expr(&mut self.wrapped, visitor); Some(self.wrapped) @@ -1584,7 +1533,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { /// legacy derive helpers (helpers written before derives that introduce them). fn take_first_attr( &self, - item: &mut impl AstLike, + item: &mut impl HasAttrs, ) -> Option<(ast::Attribute, usize, Vec<ast::Path>)> { let mut attr = None; @@ -1680,7 +1629,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { fn expand_cfg_true( &mut self, - node: &mut impl AstLike, + node: &mut impl HasAttrs, attr: ast::Attribute, pos: usize, ) -> bool { @@ -1695,7 +1644,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { res } - fn expand_cfg_attr(&self, node: &mut impl AstLike, attr: ast::Attribute, pos: usize) { + fn expand_cfg_attr(&self, node: &mut impl HasAttrs, attr: ast::Attribute, pos: usize) { node.visit_attrs(|attrs| { attrs.splice(pos..pos, self.cfg().expand_cfg_attr(attr, false)); }); @@ -1733,7 +1682,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { } None => { match Node::wrap_flat_map_node_noop_flat_map(node, self, |mut node, this| { - assign_id!(this, node.id(), || node.noop_flat_map(this)) + assign_id!(this, node.node_id_mut(), || node.noop_flat_map(this)) }) { Ok(output) => output, Err(returned_node) => { @@ -1781,7 +1730,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { }) } None => { - assign_id!(self, node.id(), || node.noop_visit(self)) + assign_id!(self, node.node_id_mut(), || node.noop_visit(self)) } }; } @@ -1794,11 +1743,11 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { } fn flat_map_trait_item(&mut self, node: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> { - self.flat_map_node(AstLikeWrapper::new(node, TraitItemTag)) + self.flat_map_node(AstNodeWrapper::new(node, TraitItemTag)) } fn flat_map_impl_item(&mut self, node: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> { - self.flat_map_node(AstLikeWrapper::new(node, ImplItemTag)) + self.flat_map_node(AstNodeWrapper::new(node, ImplItemTag)) } fn flat_map_foreign_item( @@ -1889,7 +1838,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { } fn filter_map_expr(&mut self, node: P<ast::Expr>) -> Option<P<ast::Expr>> { - self.flat_map_node(AstLikeWrapper::new(node, OptExprTag)) + self.flat_map_node(AstNodeWrapper::new(node, OptExprTag)) } fn visit_block(&mut self, node: &mut P<ast::Block>) { diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index dc181ecda5b..7043ad54645 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -1,7 +1,6 @@ #![allow(rustc::potential_query_instability)] #![feature(associated_type_bounds)] #![feature(associated_type_defaults)] -#![feature(crate_visibility_modifier)] #![feature(if_let_guard)] #![feature(let_chains)] #![feature(let_else)] @@ -21,7 +20,7 @@ mod placeholders; mod proc_macro_server; pub use mbe::macro_rules::compile_declarative_macro; -crate use rustc_span::hygiene; +pub(crate) use rustc_span::hygiene; pub mod base; pub mod build; #[macro_use] @@ -30,7 +29,7 @@ pub mod expand; pub mod module; pub mod proc_macro; -crate mod mbe; +pub(crate) mod mbe; // HACK(Centril, #64197): These shouldn't really be here. // Rather, they should be with their respective modules which are defined in other crates. diff --git a/compiler/rustc_expand/src/mbe.rs b/compiler/rustc_expand/src/mbe.rs index 36295da74ad..f42576b16f5 100644 --- a/compiler/rustc_expand/src/mbe.rs +++ b/compiler/rustc_expand/src/mbe.rs @@ -3,12 +3,12 @@ //! why we call this module `mbe`. For external documentation, prefer the //! official terminology: "declarative macros". -crate mod macro_check; -crate mod macro_parser; -crate mod macro_rules; -crate mod metavar_expr; -crate mod quoted; -crate mod transcribe; +pub(crate) mod macro_check; +pub(crate) mod macro_parser; +pub(crate) mod macro_rules; +pub(crate) mod metavar_expr; +pub(crate) mod quoted; +pub(crate) mod transcribe; use metavar_expr::MetaVarExpr; use rustc_ast::token::{Delimiter, NonterminalKind, Token, TokenKind}; diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs index 35b5e0d0f2f..8994a2f7891 100644 --- a/compiler/rustc_expand/src/mbe/macro_check.rs +++ b/compiler/rustc_expand/src/mbe/macro_check.rs @@ -4,7 +4,7 @@ //! //! ## Meta-variables must not be bound twice //! -//! ``` +//! ```compile_fail //! macro_rules! foo { ($x:tt $x:tt) => { $x }; } //! ``` //! @@ -604,9 +604,9 @@ fn check_ops_is_prefix( /// Kleene operators of its binder as a prefix. /// /// Consider $i in the following example: -/// -/// ( $( $i:ident = $($j:ident),+ );* ) => { $($( $i += $j; )+)* } -/// +/// ```ignore (illustrative) +/// ( $( $i:ident = $($j:ident),+ );* ) => { $($( $i += $j; )+)* } +/// ``` /// It occurs under the Kleene stack ["*", "+"] and is bound under ["*"] only. /// /// Arguments: diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index 8f260e1cdb5..0631a5e42c2 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -70,12 +70,13 @@ //! eof: [a $( a )* a b ·] //! ``` -crate use NamedMatch::*; -crate use ParseResult::*; +pub(crate) use NamedMatch::*; +pub(crate) use ParseResult::*; use crate::mbe::{KleeneOp, TokenTree}; use rustc_ast::token::{self, DocComment, Nonterminal, NonterminalKind, Token}; +use rustc_lint_defs::pluralize; use rustc_parse::parser::{NtOrTt, Parser}; use rustc_span::symbol::MacroRulesNormalizedIdent; use rustc_span::Span; @@ -261,7 +262,7 @@ enum EofMatcherPositions { } /// Represents the possible results of an attempted parse. -crate enum ParseResult<T> { +pub(crate) enum ParseResult<T> { /// Parsed successfully. Success(T), /// Arm failed to match. If the second parameter is `token::Eof`, it indicates an unexpected @@ -275,7 +276,7 @@ crate enum ParseResult<T> { /// A `ParseResult` where the `Success` variant contains a mapping of /// `MacroRulesNormalizedIdent`s to `NamedMatch`es. This represents the mapping /// of metavars to the token trees they bind to. -crate type NamedParseResult = ParseResult<FxHashMap<MacroRulesNormalizedIdent, NamedMatch>>; +pub(crate) type NamedParseResult = ParseResult<FxHashMap<MacroRulesNormalizedIdent, NamedMatch>>; /// Count how many metavars declarations are in `matcher`. pub(super) fn count_metavar_decls(matcher: &[TokenTree]) -> usize { @@ -320,7 +321,8 @@ pub(super) fn count_metavar_decls(matcher: &[TokenTree]) -> usize { /// /// Then, the tree will have the following shape: /// -/// ```rust +/// ```ignore (private-internal) +/// # use NamedMatch::*; /// MatchedSeq([ /// MatchedSeq([ /// MatchedNonterminal(a), @@ -338,7 +340,7 @@ pub(super) fn count_metavar_decls(matcher: &[TokenTree]) -> usize { /// ]) /// ``` #[derive(Debug, Clone)] -crate enum NamedMatch { +pub(crate) enum NamedMatch { MatchedSeq(Vec<NamedMatch>), // A metavar match of type `tt`. @@ -667,8 +669,7 @@ impl TtParser { self.macro_name, match self.next_mps.len() { 0 => format!("built-in NTs {}.", nts), - 1 => format!("built-in NTs {} or 1 other option.", nts), - n => format!("built-in NTs {} or {} other options.", nts, n), + n => format!("built-in NTs {} or {n} other option{s}.", nts, s = pluralize!(n)), } ), ) diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 050710097c3..b16fa7111c5 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -33,7 +33,7 @@ use std::collections::hash_map::Entry; use std::{mem, slice}; use tracing::debug; -crate struct ParserAnyMacro<'a> { +pub(crate) struct ParserAnyMacro<'a> { parser: Parser<'a>, /// Span of the expansion site of the macro this parser is for @@ -47,7 +47,7 @@ crate struct ParserAnyMacro<'a> { is_local: bool, } -crate fn annotate_err_with_kind(err: &mut Diagnostic, kind: AstFragmentKind, span: Span) { +pub(crate) fn annotate_err_with_kind(err: &mut Diagnostic, kind: AstFragmentKind, span: Span) { match kind { AstFragmentKind::Ty => { err.span_label(span, "this macro call doesn't expand to a type"); @@ -113,7 +113,7 @@ fn emit_frag_parse_err( } impl<'a> ParserAnyMacro<'a> { - crate fn make(mut self: Box<ParserAnyMacro<'a>>, kind: AstFragmentKind) -> AstFragment { + pub(crate) fn make(mut self: Box<ParserAnyMacro<'a>>, kind: AstFragmentKind) -> AstFragment { let ParserAnyMacro { site_span, macro_ident, @@ -156,13 +156,13 @@ impl<'a> ParserAnyMacro<'a> { } struct MacroRulesMacroExpander { + node_id: NodeId, name: Ident, span: Span, transparency: Transparency, lhses: Vec<Vec<MatcherLoc>>, rhses: Vec<mbe::TokenTree>, valid: bool, - is_local: bool, } impl TTMacroExpander for MacroRulesMacroExpander { @@ -175,16 +175,16 @@ impl TTMacroExpander for MacroRulesMacroExpander { if !self.valid { return DummyResult::any(sp); } - generic_extension( + expand_macro( cx, sp, self.span, + self.node_id, self.name, self.transparency, input, &self.lhses, &self.rhses, - self.is_local, ) } } @@ -202,19 +202,23 @@ fn trace_macros_note(cx_expansions: &mut FxHashMap<Span, Vec<String>>, sp: Span, cx_expansions.entry(sp).or_default().push(message); } -/// Given `lhses` and `rhses`, this is the new macro we create -fn generic_extension<'cx, 'tt>( +/// Expands the rules based macro defined by `lhses` and `rhses` for a given +/// input `arg`. +fn expand_macro<'cx, 'tt>( cx: &'cx mut ExtCtxt<'_>, sp: Span, def_span: Span, + node_id: NodeId, name: Ident, transparency: Transparency, arg: TokenStream, lhses: &'tt [Vec<MatcherLoc>], rhses: &'tt [mbe::TokenTree], - is_local: bool, ) -> Box<dyn MacResult + 'cx> { let sess = &cx.sess.parse_sess; + // Macros defined in the current crate have a real node id, + // whereas macros from an external crate have a dummy id. + let is_local = node_id != DUMMY_NODE_ID; if cx.trace_macros() { let msg = format!("expanding `{}! {{ {} }}`", name, pprust::tts_to_string(&arg)); @@ -296,6 +300,10 @@ fn generic_extension<'cx, 'tt>( let mut p = Parser::new(sess, tts, false, None); p.last_type_ascription = cx.current_expansion.prior_type_ascription; + if is_local { + cx.resolver.record_macro_rule_usage(node_id, i); + } + // Let the context choose how to interpret the result. // Weird, but useful for X-macros. return Box::new(ParserAnyMacro { @@ -372,7 +380,7 @@ pub fn compile_declarative_macro( features: &Features, def: &ast::Item, edition: Edition, -) -> SyntaxExtension { +) -> (SyntaxExtension, Vec<Span>) { debug!("compile_declarative_macro: {:?}", def); let mk_syn_ext = |expander| { SyntaxExtension::new( @@ -385,6 +393,7 @@ pub fn compile_declarative_macro( &def.attrs, ) }; + let dummy_syn_ext = || (mk_syn_ext(Box::new(macro_rules_dummy_expander)), Vec::new()); let diag = &sess.parse_sess.span_diagnostic; let lhs_nm = Ident::new(sym::lhs, def.span); @@ -445,17 +454,17 @@ pub fn compile_declarative_macro( let s = parse_failure_msg(&token); let sp = token.span.substitute_dummy(def.span); sess.parse_sess.span_diagnostic.struct_span_err(sp, &s).span_label(sp, msg).emit(); - return mk_syn_ext(Box::new(macro_rules_dummy_expander)); + return dummy_syn_ext(); } Error(sp, msg) => { sess.parse_sess .span_diagnostic .struct_span_err(sp.substitute_dummy(def.span), &msg) .emit(); - return mk_syn_ext(Box::new(macro_rules_dummy_expander)); + return dummy_syn_ext(); } ErrorReported => { - return mk_syn_ext(Box::new(macro_rules_dummy_expander)); + return dummy_syn_ext(); } }; @@ -530,6 +539,15 @@ pub fn compile_declarative_macro( None => {} } + // Compute the spans of the macro rules + // We only take the span of the lhs here, + // so that the spans of created warnings are smaller. + let rule_spans = if def.id != DUMMY_NODE_ID { + lhses.iter().map(|lhs| lhs.span()).collect::<Vec<_>>() + } else { + Vec::new() + }; + // Convert the lhses into `MatcherLoc` form, which is better for doing the // actual matching. Unless the matcher is invalid. let lhses = if valid { @@ -549,17 +567,16 @@ pub fn compile_declarative_macro( vec![] }; - mk_syn_ext(Box::new(MacroRulesMacroExpander { + let expander = Box::new(MacroRulesMacroExpander { name: def.ident, span: def.span, + node_id: def.id, transparency, lhses, rhses, valid, - // Macros defined in the current crate have a real node id, - // whereas macros from an external crate have a dummy id. - is_local: def.id != DUMMY_NODE_ID, - })) + }); + (mk_syn_ext(expander), rule_spans) } fn check_lhs_nt_follows(sess: &ParseSess, def: &ast::Item, lhs: &mbe::TokenTree) -> bool { diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs index cdc5e204236..ccc1c2b2ca0 100644 --- a/compiler/rustc_expand/src/mbe/metavar_expr.rs +++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs @@ -1,5 +1,5 @@ use rustc_ast::token::{self, Delimiter}; -use rustc_ast::tokenstream::{Cursor, TokenStream, TokenTree}; +use rustc_ast::tokenstream::{CursorRef, TokenStream, TokenTree}; use rustc_ast::{LitIntType, LitKind}; use rustc_ast_pretty::pprust; use rustc_errors::{Applicability, PResult}; @@ -9,7 +9,7 @@ use rustc_span::Span; /// A meta-variable expression, for expansions based on properties of meta-variables. #[derive(Debug, Clone, PartialEq, Encodable, Decodable)] -crate enum MetaVarExpr { +pub(crate) enum MetaVarExpr { /// The number of repetitions of an identifier, optionally limited to a number /// of outer-most repetition depths. If the depth limit is `None` then the depth is unlimited. Count(Ident, Option<usize>), @@ -28,7 +28,7 @@ crate enum MetaVarExpr { impl MetaVarExpr { /// Attempt to parse a meta-variable expression from a token stream. - crate fn parse<'sess>( + pub(crate) fn parse<'sess>( input: &TokenStream, outer_span: Span, sess: &'sess ParseSess, @@ -62,7 +62,7 @@ impl MetaVarExpr { Ok(rslt) } - crate fn ident(&self) -> Option<Ident> { + pub(crate) fn ident(&self) -> Option<Ident> { match *self { MetaVarExpr::Count(ident, _) | MetaVarExpr::Ignore(ident) => Some(ident), MetaVarExpr::Index(..) | MetaVarExpr::Length(..) => None, @@ -71,12 +71,14 @@ impl MetaVarExpr { } // Checks if there are any remaining tokens. For example, `${ignore(ident ... a b c ...)}` -fn check_trailing_token<'sess>(iter: &mut Cursor, sess: &'sess ParseSess) -> PResult<'sess, ()> { +fn check_trailing_token<'sess>( + iter: &mut CursorRef<'_>, + sess: &'sess ParseSess, +) -> PResult<'sess, ()> { if let Some(tt) = iter.next() { - let mut diag = sess.span_diagnostic.struct_span_err( - tt.span(), - &format!("unexpected token: {}", pprust::tt_to_string(&tt)), - ); + let mut diag = sess + .span_diagnostic + .struct_span_err(tt.span(), &format!("unexpected token: {}", pprust::tt_to_string(tt))); diag.span_note(tt.span(), "meta-variable expression must not have trailing tokens"); Err(diag) } else { @@ -86,7 +88,7 @@ fn check_trailing_token<'sess>(iter: &mut Cursor, sess: &'sess ParseSess) -> PRe /// Parse a meta-variable `count` expression: `count(ident[, depth])` fn parse_count<'sess>( - iter: &mut Cursor, + iter: &mut CursorRef<'_>, sess: &'sess ParseSess, span: Span, ) -> PResult<'sess, MetaVarExpr> { @@ -97,7 +99,7 @@ fn parse_count<'sess>( /// Parses the depth used by index(depth) and length(depth). fn parse_depth<'sess>( - iter: &mut Cursor, + iter: &mut CursorRef<'_>, sess: &'sess ParseSess, span: Span, ) -> PResult<'sess, usize> { @@ -110,7 +112,7 @@ fn parse_depth<'sess>( "meta-variable expression depth must be a literal" )); }; - if let Ok(lit_kind) = LitKind::from_lit_token(lit) + if let Ok(lit_kind) = LitKind::from_lit_token(*lit) && let LitKind::Int(n_u128, LitIntType::Unsuffixed) = lit_kind && let Ok(n_usize) = usize::try_from(n_u128) { @@ -124,7 +126,7 @@ fn parse_depth<'sess>( /// Parses an generic ident fn parse_ident<'sess>( - iter: &mut Cursor, + iter: &mut CursorRef<'_>, sess: &'sess ParseSess, span: Span, ) -> PResult<'sess, Ident> { @@ -132,7 +134,7 @@ fn parse_ident<'sess>( if let Some((elem, false)) = token.ident() { return Ok(elem); } - let token_str = pprust::token_to_string(&token); + let token_str = pprust::token_to_string(token); let mut err = sess.span_diagnostic.struct_span_err( span, &format!("expected identifier, found `{}`", &token_str) @@ -150,7 +152,7 @@ fn parse_ident<'sess>( /// Tries to move the iterator forward returning `true` if there is a comma. If not, then the /// iterator is not modified and the result is `false`. -fn try_eat_comma(iter: &mut Cursor) -> bool { +fn try_eat_comma(iter: &mut CursorRef<'_>) -> bool { if let Some(TokenTree::Token(token::Token { kind: token::Comma, .. })) = iter.look_ahead(0) { let _ = iter.next(); return true; diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index d52de24c393..707cb73f097 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -48,7 +48,7 @@ pub(super) fn parse( // For each token tree in `input`, parse the token into a `self::TokenTree`, consuming // additional trees if need be. - let mut trees = input.trees(); + let mut trees = input.into_trees(); while let Some(tree) = trees.next() { // Given the parsed tree, if there is a metavar and we are expecting matchers, actually // parse out the matcher (i.e., in `$id:ident` this would parse the `:` and `ident`). diff --git a/compiler/rustc_expand/src/module.rs b/compiler/rustc_expand/src/module.rs index 2a059f3519d..876faad33b6 100644 --- a/compiler/rustc_expand/src/module.rs +++ b/compiler/rustc_expand/src/module.rs @@ -26,7 +26,7 @@ pub struct ModulePathSuccess { pub dir_ownership: DirOwnership, } -crate struct ParsedExternalMod { +pub(crate) struct ParsedExternalMod { pub items: Vec<P<Item>>, pub spans: ModSpans, pub file_path: PathBuf, @@ -42,7 +42,7 @@ pub enum ModError<'a> { ParserError(DiagnosticBuilder<'a, ErrorGuaranteed>), } -crate fn parse_external_mod( +pub(crate) fn parse_external_mod( sess: &Session, ident: Ident, span: Span, // The span to blame on errors. @@ -78,7 +78,7 @@ crate fn parse_external_mod( ParsedExternalMod { items, spans, file_path, dir_path, dir_ownership } } -crate fn mod_dir_path( +pub(crate) fn mod_dir_path( sess: &Session, ident: Ident, attrs: &[Attribute], diff --git a/compiler/rustc_expand/src/parse/tests.rs b/compiler/rustc_expand/src/parse/tests.rs index 5d447d911e7..8da78792758 100644 --- a/compiler/rustc_expand/src/parse/tests.rs +++ b/compiler/rustc_expand/src/parse/tests.rs @@ -61,7 +61,7 @@ fn bad_path_expr_1() { fn string_to_tts_macro() { create_default_session_globals_then(|| { let tts: Vec<_> = - string_to_stream("macro_rules! zip (($a)=>($a))".to_string()).trees().collect(); + string_to_stream("macro_rules! zip (($a)=>($a))".to_string()).into_trees().collect(); let tts: &[TokenTree] = &tts[..]; match tts { @@ -293,7 +293,7 @@ fn ttdelim_span() { .unwrap(); let tts: Vec<_> = match expr.kind { - ast::ExprKind::MacCall(ref mac) => mac.args.inner_tokens().trees().collect(), + ast::ExprKind::MacCall(ref mac) => mac.args.inner_tokens().into_trees().collect(), _ => panic!("not a macro"), }; diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index 8e1966a0711..b3679b31c6c 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -4,10 +4,9 @@ use crate::proc_macro_server; use rustc_ast as ast; use rustc_ast::ptr::P; use rustc_ast::token; -use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, TokenStream, TokenTree}; +use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_data_structures::sync::Lrc; use rustc_errors::ErrorGuaranteed; -use rustc_parse::nt_to_tokenstream; use rustc_parse::parser::ForceCollect; use rustc_span::profiling::SpannedEventArgRecorder; use rustc_span::{Span, DUMMY_SP}; @@ -87,25 +86,17 @@ impl MultiItemModifier for ProcMacroDerive { ) -> ExpandResult<Vec<Annotatable>, Annotatable> { // We need special handling for statement items // (e.g. `fn foo() { #[derive(Debug)] struct Bar; }`) - let mut is_stmt = false; - let item = match item { - Annotatable::Item(item) => token::NtItem(item), - Annotatable::Stmt(stmt) => { - is_stmt = true; - assert!(stmt.is_item()); - - // A proc macro can't observe the fact that we're passing - // them an `NtStmt` - it can only see the underlying tokens - // of the wrapped item - token::NtStmt(stmt) - } - _ => unreachable!(), - }; - let input = if crate::base::pretty_printing_compatibility_hack(&item, &ecx.sess.parse_sess) - { - TokenTree::token(token::Interpolated(Lrc::new(item)), DUMMY_SP).into() + let is_stmt = matches!(item, Annotatable::Stmt(..)); + let hack = crate::base::ann_pretty_printing_compatibility_hack(&item, &ecx.sess.parse_sess); + let input = if hack { + let nt = match item { + Annotatable::Item(item) => token::NtItem(item), + Annotatable::Stmt(stmt) => token::NtStmt(stmt), + _ => unreachable!(), + }; + TokenTree::token(token::Interpolated(Lrc::new(nt)), DUMMY_SP).into() } else { - nt_to_tokenstream(&item, &ecx.sess.parse_sess, CanSynthesizeMissingTokens::No) + item.to_tokens() }; let stream = { diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index b7230cec3e4..d4407c03d03 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -2,14 +2,13 @@ use crate::base::ExtCtxt; use rustc_ast as ast; use rustc_ast::token; -use rustc_ast::tokenstream::{self, CanSynthesizeMissingTokens}; -use rustc_ast::tokenstream::{DelimSpan, Spacing::*, TokenStream, TreeAndSpacing}; +use rustc_ast::tokenstream::{self, DelimSpan, Spacing::*, TokenStream, TreeAndSpacing}; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; use rustc_errors::{Diagnostic, MultiSpan, PResult}; use rustc_parse::lexer::nfc_normalize; -use rustc_parse::{nt_to_tokenstream, parse_stream_from_source_str}; +use rustc_parse::parse_stream_from_source_str; use rustc_session::parse::ParseSess; use rustc_span::def_id::CrateNum; use rustc_span::symbol::{self, kw, sym, Symbol}; @@ -179,12 +178,11 @@ impl FromInternal<(TreeAndSpacing, &'_ mut Vec<Self>, &mut Rustc<'_, '_>)> TokenTree::Ident(Ident::new(rustc.sess(), ident.name, is_raw, ident.span)) } Interpolated(nt) => { - let stream = nt_to_tokenstream(&nt, rustc.sess(), CanSynthesizeMissingTokens::No); TokenTree::Group(Group { delimiter: pm::Delimiter::None, - stream, + stream: TokenStream::from_nonterminal_ast(&nt), span: DelimSpan::from_single(span), - flatten: crate::base::pretty_printing_compatibility_hack(&nt, rustc.sess()), + flatten: crate::base::nt_pretty_printing_compatibility_hack(&nt, rustc.sess()), }) } @@ -454,7 +452,7 @@ impl server::TokenStream for Rustc<'_, '_> { // NOTE: For now, limit `expand_expr` to exclusively expand to literals. // This may be relaxed in the future. - // We don't use `nt_to_tokenstream` as the tokenstream currently cannot + // We don't use `TokenStream::from_ast` as the tokenstream currently cannot // be recovered in the general case. match &expr.kind { ast::ExprKind::Lit(l) => { @@ -484,7 +482,7 @@ impl server::TokenStream for Rustc<'_, '_> { tree.to_internal() } fn into_iter(&mut self, stream: Self::TokenStream) -> Self::TokenStreamIter { - TokenStreamIter { cursor: stream.trees(), stack: vec![] } + TokenStreamIter { cursor: stream.into_trees(), stack: vec![] } } } diff --git a/compiler/rustc_expand/src/tests.rs b/compiler/rustc_expand/src/tests.rs index 693159f9aec..8b7153776e4 100644 --- a/compiler/rustc_expand/src/tests.rs +++ b/compiler/rustc_expand/src/tests.rs @@ -22,7 +22,7 @@ fn string_to_parser(ps: &ParseSess, source_str: String) -> Parser<'_> { new_parser_from_source_str(ps, PathBuf::from("bogofile").into(), source_str) } -crate fn with_error_checking_parse<'a, T, F>(s: String, ps: &'a ParseSess, f: F) -> T +pub(crate) fn with_error_checking_parse<'a, T, F>(s: String, ps: &'a ParseSess, f: F) -> T where F: FnOnce(&mut Parser<'a>) -> PResult<'a, T>, { @@ -33,7 +33,7 @@ where } /// Maps a string to tts, using a made-up filename. -crate fn string_to_stream(source_str: String) -> TokenStream { +pub(crate) fn string_to_stream(source_str: String) -> TokenStream { let ps = ParseSess::new(FilePathMapping::empty()); source_file_to_stream( &ps, @@ -44,7 +44,7 @@ crate fn string_to_stream(source_str: String) -> TokenStream { } /// Parses a string, returns a crate. -crate fn string_to_crate(source_str: String) -> ast::Crate { +pub(crate) fn string_to_crate(source_str: String) -> ast::Crate { let ps = ParseSess::new(FilePathMapping::empty()); with_error_checking_parse(source_str, &ps, |p| p.parse_crate_mod()) } @@ -53,7 +53,7 @@ crate fn string_to_crate(source_str: String) -> ast::Crate { /// may be deleted or replaced with other whitespace to match the pattern. /// This function is relatively Unicode-ignorant; fortunately, the careful design /// of UTF-8 mitigates this ignorance. It doesn't do NKF-normalization(?). -crate fn matches_codepattern(a: &str, b: &str) -> bool { +pub(crate) fn matches_codepattern(a: &str, b: &str) -> bool { let mut a_iter = a.chars().peekable(); let mut b_iter = b.chars().peekable(); @@ -109,7 +109,7 @@ struct SpanLabel { label: &'static str, } -crate struct Shared<T: Write> { +pub(crate) struct Shared<T: Write> { pub data: Arc<Mutex<T>>, } diff --git a/compiler/rustc_expand/src/tokenstream/tests.rs b/compiler/rustc_expand/src/tokenstream/tests.rs index 31052bfb54c..270532f8ede 100644 --- a/compiler/rustc_expand/src/tokenstream/tests.rs +++ b/compiler/rustc_expand/src/tokenstream/tests.rs @@ -35,7 +35,7 @@ fn test_concat() { fn test_to_from_bijection() { create_default_session_globals_then(|| { let test_start = string_to_ts("foo::bar(baz)"); - let test_end = test_start.trees().collect(); + let test_end = test_start.trees().cloned().collect(); assert_eq!(test_start, test_end) }) } diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 5c07d9121cc..520769d308e 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -244,7 +244,6 @@ declare_features! ( // Unstable `#[target_feature]` directives. (active, aarch64_ver_target_feature, "1.27.0", Some(44839), None), - (active, adx_target_feature, "1.32.0", Some(44839), None), (active, arm_target_feature, "1.27.0", Some(44839), None), (active, avx512_target_feature, "1.27.0", Some(44839), None), (active, bpf_target_feature, "1.54.0", Some(44839), None), diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index bdbda8bf20c..097493e8985 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -147,6 +147,16 @@ pub enum AttributeDuplicates { FutureWarnPreceding, } +/// A conveniece macro to deal with `$($expr)?`. +macro_rules! or_default { + ($default:expr,) => { + $default + }; + ($default:expr, $next:expr) => { + $next + }; +} + /// A convenience macro for constructing attribute templates. /// E.g., `template!(Word, List: "description")` means that the attribute /// supports forms `#[attr]` and `#[attr(description)]`. @@ -168,9 +178,10 @@ macro_rules! template { } macro_rules! ungated { - ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr $(,)?) => { + ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr $(, @only_local: $only_local:expr)? $(,)?) => { BuiltinAttribute { name: sym::$attr, + only_local: or_default!(false, $($only_local)?), type_: $typ, template: $tpl, gate: Ungated, @@ -180,18 +191,20 @@ macro_rules! ungated { } macro_rules! gated { - ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $gate:ident, $msg:expr $(,)?) => { + ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr $(, @only_local: $only_local:expr)?, $gate:ident, $msg:expr $(,)?) => { BuiltinAttribute { name: sym::$attr, + only_local: or_default!(false, $($only_local)?), type_: $typ, template: $tpl, duplicates: $duplicates, gate: Gated(Stability::Unstable, sym::$gate, $msg, cfg_fn!($gate)), } }; - ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $msg:expr $(,)?) => { + ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr $(, @only_local: $only_local:expr)?, $msg:expr $(,)?) => { BuiltinAttribute { name: sym::$attr, + only_local: or_default!(false, $($only_local)?), type_: $typ, template: $tpl, duplicates: $duplicates, @@ -201,12 +214,13 @@ macro_rules! gated { } macro_rules! rustc_attr { - (TEST, $attr:ident, $typ:expr, $tpl:expr, $duplicate:expr $(,)?) => { + (TEST, $attr:ident, $typ:expr, $tpl:expr, $duplicate:expr $(, @only_local: $only_local:expr)? $(,)?) => { rustc_attr!( $attr, $typ, $tpl, $duplicate, + $(@only_local: $only_local,)? concat!( "the `#[", stringify!($attr), @@ -215,9 +229,10 @@ macro_rules! rustc_attr { ), ) }; - ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $msg:expr $(,)?) => { + ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr $(, @only_local: $only_local:expr)?, $msg:expr $(,)?) => { BuiltinAttribute { name: sym::$attr, + only_local: or_default!(false, $($only_local)?), type_: $typ, template: $tpl, duplicates: $duplicates, @@ -237,6 +252,10 @@ const INTERNAL_UNSTABLE: &str = "this is an internal attribute that will never b pub struct BuiltinAttribute { pub name: Symbol, + /// Whether this attribute is only used in the local crate. + /// + /// If so, it is not encoded in the crate metadata. + pub only_local: bool, pub type_: AttributeType, pub template: AttributeTemplate, pub duplicates: AttributeDuplicates, @@ -295,7 +314,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ungated!(must_use, Normal, template!(Word, NameValueStr: "reason"), FutureWarnFollowing), gated!( must_not_suspend, Normal, template!(Word, NameValueStr: "reason"), WarnFollowing, - must_not_suspend, experimental!(must_not_suspend) + experimental!(must_not_suspend) ), ungated!( deprecated, Normal, @@ -304,8 +323,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ List: r#"/*opt*/ since = "version", /*opt*/ note = "reason""#, NameValueStr: "reason" ), - // This has special duplicate handling in E0550 to handle duplicates with rustc_deprecated - DuplicatesOk + ErrorFollowing ), // Crate properties: @@ -325,8 +343,8 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ungated!(repr, Normal, template!(List: "C"), DuplicatesOk), ungated!(export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding), ungated!(link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding), - ungated!(no_mangle, Normal, template!(Word), WarnFollowing), - ungated!(used, Normal, template!(Word, List: "compiler|linker"), WarnFollowing), + ungated!(no_mangle, Normal, template!(Word), WarnFollowing, @only_local: true), + ungated!(used, Normal, template!(Word, List: "compiler|linker"), WarnFollowing, @only_local: true), // Limits: ungated!(recursion_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing), @@ -359,8 +377,8 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ungated!(panic_handler, Normal, template!(Word), WarnFollowing), // RFC 2070 // Code generation: - ungated!(inline, Normal, template!(Word, List: "always|never"), FutureWarnFollowing), - ungated!(cold, Normal, template!(Word), WarnFollowing), + ungated!(inline, Normal, template!(Word, List: "always|never"), FutureWarnFollowing, @only_local: true), + ungated!(cold, Normal, template!(Word), WarnFollowing, @only_local: true), ungated!(no_builtins, CrateLevel, template!(Word), WarnFollowing), ungated!(target_feature, Normal, template!(List: r#"enable = "name""#), DuplicatesOk), ungated!(track_caller, Normal, template!(Word), WarnFollowing), @@ -386,7 +404,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), // Linking: - gated!(naked, Normal, template!(Word), WarnFollowing, naked_functions, experimental!(naked)), + gated!(naked, Normal, template!(Word), WarnFollowing, @only_local: true, naked_functions, experimental!(naked)), gated!( link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding, raw_dylib, experimental!(link_ordinal) @@ -395,6 +413,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // Plugins: BuiltinAttribute { name: sym::plugin, + only_local: false, type_: CrateLevel, template: template!(List: "name"), duplicates: DuplicatesOk, @@ -469,14 +488,14 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // ========================================================================== ungated!(feature, CrateLevel, template!(List: "name1, name2, ..."), DuplicatesOk), - // DuplicatesOk since it has its own validation + // FIXME(jhpratt) remove this eventually ungated!( rustc_deprecated, Normal, - template!(List: r#"since = "version", note = "...""#), DuplicatesOk // See E0550 + template!(List: r#"since = "version", note = "...""#), ErrorFollowing ), // DuplicatesOk since it has its own validation ungated!( - stable, Normal, template!(List: r#"feature = "name", since = "version""#), DuplicatesOk + stable, Normal, template!(List: r#"feature = "name", since = "version""#), DuplicatesOk, ), ungated!( unstable, Normal, @@ -547,11 +566,11 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // ========================================================================== gated!( - linkage, Normal, template!(NameValueStr: "external|internal|..."), ErrorPreceding, + linkage, Normal, template!(NameValueStr: "external|internal|..."), ErrorPreceding, @only_local: true, "the `linkage` attribute is experimental and not portable across platforms", ), rustc_attr!( - rustc_std_internal_symbol, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE + rustc_std_internal_symbol, Normal, template!(Word), WarnFollowing, @only_local: true, INTERNAL_UNSTABLE ), // ========================================================================== @@ -634,7 +653,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // Internal attributes, Misc: // ========================================================================== gated!( - lang, Normal, template!(NameValueStr: "name"), DuplicatesOk, lang_items, + lang, Normal, template!(NameValueStr: "name"), DuplicatesOk, @only_local: true, lang_items, "language items are subject to change", ), rustc_attr!( @@ -643,15 +662,22 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ "#[rustc_pass_by_value] is used to mark types that must be passed by value instead of reference." ), rustc_attr!( - rustc_coherence_is_core, AttributeType::CrateLevel, template!(Word), ErrorFollowing, + rustc_coherence_is_core, AttributeType::CrateLevel, template!(Word), ErrorFollowing, @only_local: true, "#![rustc_coherence_is_core] allows inherent methods on builtin types, only intended to be used in `core`." ), rustc_attr!( - rustc_allow_incoherent_impl, AttributeType::Normal, template!(Word), ErrorFollowing, + rustc_allow_incoherent_impl, AttributeType::Normal, template!(Word), ErrorFollowing, @only_local: true, "#[rustc_allow_incoherent_impl] has to be added to all impl items of an incoherent inherent impl." ), + rustc_attr!( + rustc_has_incoherent_inherent_impls, AttributeType::Normal, template!(Word), ErrorFollowing, + "#[rustc_has_incoherent_inherent_impls] allows the addition of incoherent inherent impls for \ + the given type by annotating all impl items with #[rustc_allow_incoherent_impl]." + ), BuiltinAttribute { name: sym::rustc_diagnostic_item, + // FIXME: This can be `true` once we always use `tcx.is_diagnostic_item`. + only_local: false, type_: Normal, template: template!(NameValueStr: "name"), duplicates: ErrorFollowing, @@ -672,7 +698,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ "unboxed_closures are still evolving", ), rustc_attr!( - rustc_inherit_overflow_checks, Normal, template!(Word), WarnFollowing, + rustc_inherit_overflow_checks, Normal, template!(Word), WarnFollowing, @only_local: true, "the `#[rustc_inherit_overflow_checks]` attribute is just used to control \ overflow checking behavior of several libcore functions that are inlined \ across crates and will never be stable", @@ -774,6 +800,10 @@ pub fn is_builtin_attr_name(name: Symbol) -> bool { BUILTIN_ATTRIBUTE_MAP.get(&name).is_some() } +pub fn is_builtin_only_local(name: Symbol) -> bool { + BUILTIN_ATTRIBUTE_MAP.get(&name).map_or(false, |attr| attr.only_local) +} + pub static BUILTIN_ATTRIBUTE_MAP: SyncLazy<FxHashMap<Symbol, &BuiltinAttribute>> = SyncLazy::new(|| { let mut map = FxHashMap::default(); diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index 940c4ecdcc2..26e0538b0eb 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -149,7 +149,8 @@ pub use accepted::ACCEPTED_FEATURES; pub use active::{Features, ACTIVE_FEATURES, INCOMPATIBLE_FEATURES}; pub use builtin_attrs::AttributeDuplicates; pub use builtin_attrs::{ - deprecated_attributes, find_gated_cfg, is_builtin_attr_name, AttributeGate, AttributeTemplate, - AttributeType, BuiltinAttribute, GatedCfg, BUILTIN_ATTRIBUTES, BUILTIN_ATTRIBUTE_MAP, + deprecated_attributes, find_gated_cfg, is_builtin_attr_name, is_builtin_only_local, + AttributeGate, AttributeTemplate, AttributeType, BuiltinAttribute, GatedCfg, + BUILTIN_ATTRIBUTES, BUILTIN_ATTRIBUTE_MAP, }; pub use removed::{REMOVED_FEATURES, STABLE_REMOVED_FEATURES}; diff --git a/compiler/rustc_graphviz/src/lib.rs b/compiler/rustc_graphviz/src/lib.rs index e318090ebe1..676c66f41a9 100644 --- a/compiler/rustc_graphviz/src/lib.rs +++ b/compiler/rustc_graphviz/src/lib.rs @@ -4,18 +4,16 @@ //! use with [Graphviz](https://www.graphviz.org/) by walking a labeled //! graph. (Graphviz can then automatically lay out the nodes and edges //! of the graph, and also optionally render the graph as an image or -//! other [output formats]( -//! https://www.graphviz.org/content/output-formats), such as SVG.) +//! other [output formats](https://www.graphviz.org/docs/outputs), such as SVG.) //! //! Rather than impose some particular graph data structure on clients, //! this library exposes two traits that clients can implement on their //! own structs before handing them over to the rendering function. //! //! Note: This library does not yet provide access to the full -//! expressiveness of the [DOT language]( -//! https://www.graphviz.org/doc/info/lang.html). For example, there are -//! many [attributes](https://www.graphviz.org/content/attrs) related to -//! providing layout hints (e.g., left-to-right versus top-down, which +//! expressiveness of the [DOT language](https://www.graphviz.org/doc/info/lang.html). +//! For example, there are many [attributes](https://www.graphviz.org/doc/info/attrs.html) +//! related to providing layout hints (e.g., left-to-right versus top-down, which //! algorithm to use, etc). The current intention of this library is to //! emit a human-readable .dot file with very regular structure suitable //! for easy post-processing. @@ -292,7 +290,7 @@ pub enum LabelText<'a> { LabelStr(Cow<'a, str>), /// This kind of label uses the graphviz label escString type: - /// <https://www.graphviz.org/content/attrs#kescString> + /// <https://www.graphviz.org/docs/attr-types/escString> /// /// Occurrences of backslashes (`\`) are not escaped; instead they /// are interpreted as initiating an escString escape sequence. @@ -307,12 +305,12 @@ pub enum LabelText<'a> { /// printed exactly as given, but between `<` and `>`. **No /// escaping is performed.** /// - /// [html]: https://www.graphviz.org/content/node-shapes#html + /// [html]: https://www.graphviz.org/doc/info/shapes.html#html HtmlStr(Cow<'a, str>), } /// The style for a node or edge. -/// See <https://www.graphviz.org/doc/info/attrs.html#k:style> for descriptions. +/// See <https://www.graphviz.org/docs/attr-types/style/> for descriptions. /// Note that some of these are not valid for edges. #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum Style { @@ -439,7 +437,7 @@ pub trait Labeller<'a> { /// Maps `n` to one of the [graphviz `shape` names][1]. If `None` /// is returned, no `shape` attribute is specified. /// - /// [1]: https://www.graphviz.org/content/node-shapes + /// [1]: https://www.graphviz.org/doc/info/shapes.html fn node_shape(&'a self, _node: &Self::Node) -> Option<LabelText<'a>> { None } diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 7416ad79aef..a639df01a78 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -229,6 +229,43 @@ impl DefKind { _ => false, } } + + /// Whether `query get_codegen_attrs` should be used with this definition. + pub fn has_codegen_attrs(self) -> bool { + match self { + DefKind::Fn + | DefKind::AssocFn + | DefKind::Ctor(..) + | DefKind::Closure + | DefKind::Generator + | DefKind::Static(_) => true, + DefKind::Mod + | DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::Variant + | DefKind::Trait + | DefKind::TyAlias + | DefKind::ForeignTy + | DefKind::TraitAlias + | DefKind::AssocTy + | DefKind::Const + | DefKind::AssocConst + | DefKind::Macro(..) + | DefKind::Use + | DefKind::ForeignMod + | DefKind::OpaqueTy + | DefKind::Impl + | DefKind::Field + | DefKind::TyParam + | DefKind::ConstParam + | DefKind::LifetimeParam + | DefKind::AnonConst + | DefKind::InlineConst + | DefKind::GlobalAsm + | DefKind::ExternCrate => false, + } + } } /// The resolution of a path or export. @@ -312,6 +349,7 @@ pub enum Res<Id = hir::HirId> { /// HACK(min_const_generics): self types also have an optional requirement to **not** mention /// any generic parameters to allow the following with `min_const_generics`: /// ``` + /// # struct Foo; /// impl Foo { fn test() -> [u8; std::mem::size_of::<Self>()] { todo!() } } /// /// struct Bar([u8; baz::<Self>()]); diff --git a/compiler/rustc_hir/src/definitions.rs b/compiler/rustc_hir/src/definitions.rs index bce9ba12ac0..5c32dd372dd 100644 --- a/compiler/rustc_hir/src/definitions.rs +++ b/compiler/rustc_hir/src/definitions.rs @@ -261,14 +261,16 @@ pub enum DefPathData { // they are treated specially by the `def_path` function. /// The crate root (marker). CrateRoot, - // Catch-all for random `DefId` things like `DUMMY_NODE_ID`. - Misc, // Different kinds of items and item-like things: /// An impl. Impl, /// An `extern` block. ForeignMod, + /// A `use` item. + Use, + /// A global asm item. + GlobalAsm, /// Something in the type namespace. TypeNs(Symbol), /// Something in the value namespace. @@ -443,9 +445,8 @@ impl DefPathData { match *self { TypeNs(name) | ValueNs(name) | MacroNs(name) | LifetimeNs(name) => Some(name), - Impl | ForeignMod | CrateRoot | Misc | ClosureExpr | Ctor | AnonConst | ImplTrait => { - None - } + Impl | ForeignMod | CrateRoot | Use | GlobalAsm | ClosureExpr | Ctor | AnonConst + | ImplTrait => None, } } @@ -459,7 +460,8 @@ impl DefPathData { CrateRoot => DefPathDataName::Anon { namespace: kw::Crate }, Impl => DefPathDataName::Anon { namespace: kw::Impl }, ForeignMod => DefPathDataName::Anon { namespace: kw::Extern }, - Misc => DefPathDataName::Anon { namespace: sym::misc }, + Use => DefPathDataName::Anon { namespace: kw::Use }, + GlobalAsm => DefPathDataName::Anon { namespace: sym::global_asm }, ClosureExpr => DefPathDataName::Anon { namespace: sym::closure }, Ctor => DefPathDataName::Anon { namespace: sym::constructor }, AnonConst => DefPathDataName::Anon { namespace: sym::constant }, diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index dfeee3f356f..cebf6876936 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1,6 +1,6 @@ use crate::def::{CtorKind, DefKind, Res}; use crate::def_id::DefId; -crate use crate::hir_id::{HirId, ItemLocalId}; +pub(crate) use crate::hir_id::{HirId, ItemLocalId}; use crate::intravisit::FnKind; use crate::LangItem; @@ -49,15 +49,15 @@ pub enum ParamName { /// Synthetic name generated when user elided a lifetime in an impl header. /// /// E.g., the lifetimes in cases like these: - /// - /// impl Foo for &u32 - /// impl Foo<'_> for u32 - /// + /// ```ignore (fragment) + /// impl Foo for &u32 + /// impl Foo<'_> for u32 + /// ``` /// in that case, we rewrite to - /// - /// impl<'f> Foo for &'f u32 - /// impl<'f> Foo<'f> for u32 - /// + /// ```ignore (fragment) + /// impl<'f> Foo for &'f u32 + /// impl<'f> Foo<'f> for u32 + /// ``` /// where `'f` is something like `Fresh(0)`. The indices are /// unique per impl, but not necessarily continuous. Fresh(LocalDefId), @@ -706,7 +706,7 @@ impl<'hir> WherePredicate<'hir> { pub fn in_where_clause(&self) -> bool { match self { - WherePredicate::BoundPredicate(p) => p.in_where_clause, + WherePredicate::BoundPredicate(p) => p.origin == PredicateOrigin::WhereClause, WherePredicate::RegionPredicate(p) => p.in_where_clause, WherePredicate::EqPredicate(_) => false, } @@ -721,11 +721,19 @@ impl<'hir> WherePredicate<'hir> { } } +#[derive(Debug, HashStable_Generic, PartialEq, Eq)] +pub enum PredicateOrigin { + WhereClause, + GenericParam, + ImplTrait, +} + /// A type bound (e.g., `for<'c> Foo: Send + Clone + 'c`). #[derive(Debug, HashStable_Generic)] pub struct WhereBoundPredicate<'hir> { pub span: Span, - pub in_where_clause: bool, + /// Origin of the predicate. + pub origin: PredicateOrigin, /// Any generics from a `for` binding. pub bound_generic_params: &'hir [GenericParam<'hir>], /// The type being bounded. @@ -788,7 +796,6 @@ impl<'tcx> AttributeMap<'tcx> { /// Map of all HIR nodes inside the current owner. /// These nodes are mapped by `ItemLocalId` alongside the index of their parent node. /// The HIR tree, including bodies, is pre-hashed. -#[derive(Debug)] pub struct OwnerNodes<'tcx> { /// Pre-computed hash of the full HIR. pub hash_including_bodies: Fingerprint, @@ -814,6 +821,18 @@ impl<'tcx> OwnerNodes<'tcx> { } } +impl fmt::Debug for OwnerNodes<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("OwnerNodes") + .field("node", &self.nodes[ItemLocalId::from_u32(0)]) + .field("bodies", &self.bodies) + .field("local_id_to_def_id", &self.local_id_to_def_id) + .field("hash_without_bodies", &self.hash_without_bodies) + .field("hash_including_bodies", &self.hash_including_bodies) + .finish() + } +} + /// Full information resulting from lowering an AST node. #[derive(Debug, HashStable_Generic)] pub struct OwnerInfo<'hir> { @@ -1082,7 +1101,7 @@ pub enum PatKind<'hir> { /// If `slice` exists, then `after` can be non-empty. /// /// The representation for e.g., `[a, b, .., c, d]` is: - /// ``` + /// ```ignore (illustrative) /// PatKind::Slice([Binding(a), Binding(b)], Some(Wild), [Binding(c), Binding(d)]) /// ``` Slice(&'hir [Pat<'hir>], Option<&'hir Pat<'hir>>, &'hir [Pat<'hir>]), @@ -1312,8 +1331,21 @@ pub struct Let<'hir> { #[derive(Debug, HashStable_Generic)] pub enum Guard<'hir> { If(&'hir Expr<'hir>), - // FIXME use hir::Let for this. - IfLet(&'hir Pat<'hir>, &'hir Expr<'hir>), + IfLet(&'hir Let<'hir>), +} + +impl<'hir> Guard<'hir> { + /// Returns the body of the guard + /// + /// In other words, returns the e in either of the following: + /// + /// - `if e` + /// - `if let x = e` + pub fn body(&self) -> &'hir Expr<'hir> { + match self { + Guard::If(e) | Guard::IfLet(Let { init: e, .. }) => e, + } + } } #[derive(Debug, HashStable_Generic)] @@ -1338,7 +1370,7 @@ pub enum UnsafeSource { UserProvided, } -#[derive(Copy, Clone, PartialEq, Eq, Encodable, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, Debug)] pub struct BodyId { pub hir_id: HirId, } @@ -1441,7 +1473,7 @@ pub enum AsyncGeneratorKind { /// An explicit `async` block written by the user. Block, - /// An explicit `async` block written by the user. + /// An explicit `async` closure written by the user. Closure, /// The `async` block generated as the body of an async function. @@ -2078,10 +2110,7 @@ pub enum YieldSource { impl YieldSource { pub fn is_await(&self) -> bool { - match self { - YieldSource::Await { .. } => true, - YieldSource::Yield => false, - } + matches!(self, YieldSource::Await { .. }) } } @@ -2124,7 +2153,7 @@ pub struct FnSig<'hir> { // The bodies for items are stored "out of line", in a separate // hashmap in the `Crate`. Here we just record the hir-id of the item // so it can fetched later. -#[derive(Copy, Clone, PartialEq, Eq, Encodable, Debug, HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug, HashStable_Generic)] pub struct TraitItemId { pub def_id: LocalDefId, } @@ -2187,7 +2216,7 @@ pub enum TraitItemKind<'hir> { // The bodies for items are stored "out of line", in a separate // hashmap in the `Crate`. Here we just record the hir-id of the item // so it can fetched later. -#[derive(Copy, Clone, PartialEq, Eq, Encodable, Debug, HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug, HashStable_Generic)] pub struct ImplItemId { pub def_id: LocalDefId, } @@ -2247,7 +2276,7 @@ pub const FN_OUTPUT_NAME: Symbol = sym::Output; /// wouldn't it be better to make the `ty` field an enum like the /// following? /// -/// ``` +/// ```ignore (pseudo-rust) /// enum TypeBindingKind { /// Equals(...), /// Binding(...), @@ -2782,7 +2811,7 @@ impl<'hir> VariantData<'hir> { // The bodies for items are stored "out of line", in a separate // hashmap in the `Crate`. Here we just record the hir-id of the item // so it can fetched later. -#[derive(Copy, Clone, PartialEq, Eq, Encodable, Debug, Hash, HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug, Hash, HashStable_Generic)] pub struct ItemId { pub def_id: LocalDefId, } @@ -3029,7 +3058,7 @@ pub enum AssocItemKind { // The bodies for items are stored "out of line", in a separate // hashmap in the `Crate`. Here we just record the hir-id of the item // so it can fetched later. -#[derive(Copy, Clone, PartialEq, Eq, Encodable, Debug, HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug, HashStable_Generic)] pub struct ForeignItemId { pub def_id: LocalDefId, } diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 977c0eb3cd2..5b83a29bb33 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -1,7 +1,40 @@ //! HIR walker for walking the contents of nodes. //! -//! **For an overview of the visitor strategy, see the docs on the -//! `super::itemlikevisit::ItemLikeVisitor` trait.** +//! Here are the three available patterns for the visitor strategy, +//! in roughly the order of desirability: +//! +//! 1. **Shallow visit**: Get a simple callback for every item (or item-like thing) in the HIR. +//! - Example: find all items with a `#[foo]` attribute on them. +//! - How: Use the `hir_crate_items` or `hir_module_items` query to traverse over item-like ids +//! (ItemId, TraitItemId, etc.) and use tcx.def_kind and `tcx.hir().item*(id)` to filter and +//! access actual item-like thing, respectively. +//! - Pro: Efficient; just walks the lists of item ids and gives users control whether to access +//! the hir_owners themselves or not. +//! - Con: Don't get information about nesting +//! - Con: Don't have methods for specific bits of HIR, like "on +//! every expr, do this". +//! 2. **Deep visit**: Want to scan for specific kinds of HIR nodes within +//! an item, but don't care about how item-like things are nested +//! within one another. +//! - Example: Examine each expression to look for its type and do some check or other. +//! - How: Implement `intravisit::Visitor` and override the `NestedFilter` type to +//! `nested_filter::OnlyBodies` (and implement `nested_visit_map`), and use +//! `tcx.hir().deep_visit_all_item_likes(&mut visitor)`. Within your +//! `intravisit::Visitor` impl, implement methods like `visit_expr()` (don't forget to invoke +//! `intravisit::walk_expr()` to keep walking the subparts). +//! - Pro: Visitor methods for any kind of HIR node, not just item-like things. +//! - Pro: Integrates well into dependency tracking. +//! - Con: Don't get information about nesting between items +//! 3. **Nested visit**: Want to visit the whole HIR and you care about the nesting between +//! item-like things. +//! - Example: Lifetime resolution, which wants to bring lifetimes declared on the +//! impl into scope while visiting the impl-items, and then back out again. +//! - How: Implement `intravisit::Visitor` and override the `NestedFilter` type to +//! `nested_filter::All` (and implement `nested_visit_map`). Walk your crate with +//! `tcx.hir().walk_toplevel_module(visitor)` invoked on `tcx.hir().krate()`. +//! - Pro: Visitor methods for any kind of HIR node, not just item-like things. +//! - Pro: Preserves nesting information +//! - Con: Does not integrate well into dependency tracking. //! //! If you have decided to use this visitor, here are some general //! notes on how to do so: @@ -32,43 +65,12 @@ //! example generator inference, and possibly also HIR borrowck. use crate::hir::*; -use crate::itemlikevisit::{ItemLikeVisitor, ParItemLikeVisitor}; +use crate::itemlikevisit::ParItemLikeVisitor; use rustc_ast::walk_list; use rustc_ast::{Attribute, Label}; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::Span; -pub struct DeepVisitor<'v, V> { - visitor: &'v mut V, -} - -impl<'v, V> DeepVisitor<'v, V> { - pub fn new(base: &'v mut V) -> Self { - DeepVisitor { visitor: base } - } -} - -impl<'v, 'hir, V> ItemLikeVisitor<'hir> for DeepVisitor<'v, V> -where - V: Visitor<'hir>, -{ - fn visit_item(&mut self, item: &'hir Item<'hir>) { - self.visitor.visit_item(item); - } - - fn visit_trait_item(&mut self, trait_item: &'hir TraitItem<'hir>) { - self.visitor.visit_trait_item(trait_item); - } - - fn visit_impl_item(&mut self, impl_item: &'hir ImplItem<'hir>) { - self.visitor.visit_impl_item(impl_item); - } - - fn visit_foreign_item(&mut self, foreign_item: &'hir ForeignItem<'hir>) { - self.visitor.visit_foreign_item(foreign_item); - } -} - pub trait IntoVisitor<'hir> { type Visitor: Visitor<'hir>; fn into_visitor(&self) -> Self::Visitor; @@ -315,16 +317,6 @@ pub trait Visitor<'v>: Sized { walk_body(self, b); } - /// When invoking `visit_all_item_likes()`, you need to supply an - /// item-like visitor. This method converts an "intra-visit" - /// visitor into an item-like visitor that walks the entire tree. - /// If you use this, you probably don't want to process the - /// contents of nested item-like things, since the outer loop will - /// visit them as well. - fn as_deep_visitor(&mut self) -> DeepVisitor<'_, Self> { - DeepVisitor::new(self) - } - /////////////////////////////////////////////////////////////////////////// fn visit_id(&mut self, _hir_id: HirId) { @@ -1233,9 +1225,8 @@ pub fn walk_arm<'v, V: Visitor<'v>>(visitor: &mut V, arm: &'v Arm<'v>) { if let Some(ref g) = arm.guard { match g { Guard::If(ref e) => visitor.visit_expr(e), - Guard::IfLet(ref pat, ref e) => { - visitor.visit_pat(pat); - visitor.visit_expr(e); + Guard::IfLet(ref l) => { + visitor.visit_let_expr(l); } } } diff --git a/compiler/rustc_hir/src/itemlikevisit.rs b/compiler/rustc_hir/src/itemlikevisit.rs index b2c6ca1354f..a490268dc9f 100644 --- a/compiler/rustc_hir/src/itemlikevisit.rs +++ b/compiler/rustc_hir/src/itemlikevisit.rs @@ -1,55 +1,5 @@ use super::{ForeignItem, ImplItem, Item, TraitItem}; -/// The "item-like visitor" defines only the top-level methods -/// that can be invoked by `Crate::visit_all_item_likes()`. Whether -/// this trait is the right one to implement will depend on the -/// overall pattern you need. Here are the three available patterns, -/// in roughly the order of desirability: -/// -/// 1. **Shallow visit**: Get a simple callback for every item (or item-like thing) in the HIR. -/// - Example: find all items with a `#[foo]` attribute on them. -/// - How: Implement `ItemLikeVisitor` and call `tcx.hir().visit_all_item_likes()`. -/// - Pro: Efficient; just walks the lists of item-like things, not the nodes themselves. -/// - Con: Don't get information about nesting -/// - Con: Don't have methods for specific bits of HIR, like "on -/// every expr, do this". -/// 2. **Deep visit**: Want to scan for specific kinds of HIR nodes within -/// an item, but don't care about how item-like things are nested -/// within one another. -/// - Example: Examine each expression to look for its type and do some check or other. -/// - How: Implement `intravisit::Visitor` and override the `NestedFilter` type to -/// `nested_filter::OnlyBodies` (and implement `nested_visit_map`), and use -/// `tcx.hir().visit_all_item_likes(&mut visitor.as_deep_visitor())`. Within your -/// `intravisit::Visitor` impl, implement methods like `visit_expr()` (don't forget to invoke -/// `intravisit::walk_expr()` to keep walking the subparts). -/// - Pro: Visitor methods for any kind of HIR node, not just item-like things. -/// - Pro: Integrates well into dependency tracking. -/// - Con: Don't get information about nesting between items -/// 3. **Nested visit**: Want to visit the whole HIR and you care about the nesting between -/// item-like things. -/// - Example: Lifetime resolution, which wants to bring lifetimes declared on the -/// impl into scope while visiting the impl-items, and then back out again. -/// - How: Implement `intravisit::Visitor` and override the `NestedFilter` type to -/// `nested_filter::All` (and implement `nested_visit_map`). Walk your crate with -/// `tcx.hir().walk_toplevel_module(visitor)` invoked on `tcx.hir().krate()`. -/// - Pro: Visitor methods for any kind of HIR node, not just item-like things. -/// - Pro: Preserves nesting information -/// - Con: Does not integrate well into dependency tracking. -/// -/// Note: the methods of `ItemLikeVisitor` intentionally have no -/// defaults, so that as we expand the list of item-like things, we -/// revisit the various visitors to see if they need to change. This -/// is harder to do with `intravisit::Visitor`, so when you add a new -/// `visit_nested_foo()` method, it is recommended that you search for -/// existing `fn visit_nested` methods to see where changes are -/// needed. -pub trait ItemLikeVisitor<'hir> { - fn visit_item(&mut self, item: &'hir Item<'hir>); - fn visit_trait_item(&mut self, trait_item: &'hir TraitItem<'hir>); - fn visit_impl_item(&mut self, impl_item: &'hir ImplItem<'hir>); - fn visit_foreign_item(&mut self, foreign_item: &'hir ForeignItem<'hir>); -} - /// A parallel variant of `ItemLikeVisitor`. pub trait ParItemLikeVisitor<'hir> { fn visit_item(&self, item: &'hir Item<'hir>); diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index b3c22d4ec21..9e5d7818924 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -327,8 +327,6 @@ language_item_table! { Range, sym::Range, range_struct, Target::Struct, GenericRequirement::None; RangeToInclusive, sym::RangeToInclusive, range_to_inclusive_struct, Target::Struct, GenericRequirement::None; RangeTo, sym::RangeTo, range_to_struct, Target::Struct, GenericRequirement::None; - - CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None; } pub enum GenericRequirement { diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index d56230e1dc5..7833571f88d 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -4,7 +4,6 @@ #![feature(associated_type_defaults)] #![feature(const_btree_new)] -#![feature(crate_visibility_modifier)] #![feature(let_else)] #![feature(once_cell)] #![feature(min_specialization)] diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 7af9622b2cf..4558a3d10c4 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1915,14 +1915,9 @@ impl<'a> State<'a> { self.print_expr(&e); self.space(); } - hir::Guard::IfLet(pat, e) => { + hir::Guard::IfLet(hir::Let { pat, ty, init, .. }) => { self.word_nbsp("if"); - self.word_nbsp("let"); - self.print_pat(&pat); - self.space(); - self.word_space("="); - self.print_expr(&e); - self.space(); + self.print_let(pat, *ty, init); } } } diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs index 2d06c9d8ec9..a89b9eafaa6 100644 --- a/compiler/rustc_incremental/src/assert_dep_graph.rs +++ b/compiler/rustc_incremental/src/assert_dep_graph.rs @@ -22,7 +22,7 @@ //! //! Example: //! -//! ``` +//! ```ignore (needs flags) //! #[rustc_if_this_changed(Hir)] //! fn foo() { } //! @@ -75,7 +75,7 @@ pub fn assert_dep_graph(tcx: TyCtxt<'_>) { let mut visitor = IfThisChanged { tcx, if_this_changed: vec![], then_this_would_need: vec![] }; visitor.process_attrs(hir::CRATE_HIR_ID); - tcx.hir().visit_all_item_likes(&mut visitor.as_deep_visitor()); + tcx.hir().deep_visit_all_item_likes(&mut visitor); (visitor.if_this_changed, visitor.then_this_would_need) }; diff --git a/compiler/rustc_incremental/src/assert_module_sources.rs b/compiler/rustc_incremental/src/assert_module_sources.rs index 7569abfbb10..61b1dd8cb01 100644 --- a/compiler/rustc_incremental/src/assert_module_sources.rs +++ b/compiler/rustc_incremental/src/assert_module_sources.rs @@ -5,6 +5,7 @@ //! The user adds annotations to the crate of the following form: //! //! ``` +//! # #![feature(rustc_attrs)] //! #![rustc_partition_reused(module="spike", cfg="rpass2")] //! #![rustc_partition_codegened(module="spike-x", cfg="rpass2")] //! ``` diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs index aaf24636598..424164d8760 100644 --- a/compiler/rustc_incremental/src/persist/dirty_clean.rs +++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs @@ -183,10 +183,7 @@ pub struct DirtyCleanVisitor<'tcx> { impl<'tcx> DirtyCleanVisitor<'tcx> { /// Possibly "deserialize" the attribute into a clean/dirty assertion fn assertion_maybe(&mut self, item_id: LocalDefId, attr: &Attribute) -> Option<Assertion> { - if !attr.has_name(sym::rustc_clean) { - // skip: not rustc_clean/dirty - return None; - } + assert!(attr.has_name(sym::rustc_clean)); if !check_config(self.tcx, attr) { // skip: not the correct `cfg=` return None; @@ -384,7 +381,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { fn check_item(&mut self, item_id: LocalDefId) { let item_span = self.tcx.def_span(item_id.to_def_id()); let def_path_hash = self.tcx.def_path_hash(item_id.to_def_id()); - for attr in self.tcx.get_attrs(item_id.to_def_id()).iter() { + for attr in self.tcx.get_attrs(item_id.to_def_id(), sym::rustc_clean) { let Some(assertion) = self.assertion_maybe(item_id, attr) else { continue; }; diff --git a/compiler/rustc_index/src/interval.rs b/compiler/rustc_index/src/interval.rs index ed504938e8a..347c8856022 100644 --- a/compiler/rustc_index/src/interval.rs +++ b/compiler/rustc_index/src/interval.rs @@ -70,7 +70,7 @@ impl<I: Idx> IntervalSet<I> { /// Returns true if we increased the number of elements present. pub fn insert_range(&mut self, range: impl RangeBounds<I> + Clone) -> bool { let start = inclusive_start(range.clone()); - let Some(mut end) = inclusive_end(self.domain, range) else { + let Some(end) = inclusive_end(self.domain, range) else { // empty range return false; }; @@ -78,59 +78,56 @@ impl<I: Idx> IntervalSet<I> { return false; } - loop { - // This condition looks a bit weird, but actually makes sense. - // - // if r.0 == end + 1, then we're actually adjacent, so we want to - // continue to the next range. We're looking here for the first - // range which starts *non-adjacently* to our end. - let next = self.map.partition_point(|r| r.0 <= end + 1); - if let Some(last) = next.checked_sub(1) { - let (prev_start, prev_end) = &mut self.map[last]; - if *prev_end + 1 >= start { - // If the start for the inserted range is adjacent to the - // end of the previous, we can extend the previous range. - if start < *prev_start { - // Our range starts before the one we found. We'll need - // to *remove* it, and then try again. - // - // FIXME: This is not so efficient; we may need to - // recurse a bunch of times here. Instead, it's probably - // better to do something like drain_filter(...) on the - // map to be able to delete or modify all the ranges in - // start..=end and then potentially re-insert a new - // range. - end = std::cmp::max(end, *prev_end); - self.map.remove(last); - } else { - // We overlap with the previous range, increase it to - // include us. - // - // Make sure we're actually going to *increase* it though -- - // it may be that end is just inside the previously existing - // set. - return if end > *prev_end { - *prev_end = end; - true - } else { - false - }; + // This condition looks a bit weird, but actually makes sense. + // + // if r.0 == end + 1, then we're actually adjacent, so we want to + // continue to the next range. We're looking here for the first + // range which starts *non-adjacently* to our end. + let next = self.map.partition_point(|r| r.0 <= end + 1); + if let Some(right) = next.checked_sub(1) { + let (prev_start, prev_end) = self.map[right]; + if prev_end + 1 >= start { + // If the start for the inserted range is adjacent to the + // end of the previous, we can extend the previous range. + if start < prev_start { + // The first range which ends *non-adjacently* to our start. + // And we can ensure that left <= right. + let left = self.map.partition_point(|l| l.1 + 1 < start); + let min = std::cmp::min(self.map[left].0, start); + let max = std::cmp::max(prev_end, end); + self.map[right] = (min, max); + if left != right { + self.map.drain(left..right); } - } else { - // Otherwise, we don't overlap, so just insert - self.map.insert(last + 1, (start, end)); return true; - } - } else { - if self.map.is_empty() { - // Quite common in practice, and expensive to call memcpy - // with length zero. - self.map.push((start, end)); } else { - self.map.insert(next, (start, end)); + // We overlap with the previous range, increase it to + // include us. + // + // Make sure we're actually going to *increase* it though -- + // it may be that end is just inside the previously existing + // set. + return if end > prev_end { + self.map[right].1 = end; + true + } else { + false + }; } + } else { + // Otherwise, we don't overlap, so just insert + self.map.insert(right + 1, (start, end)); return true; } + } else { + if self.map.is_empty() { + // Quite common in practice, and expensive to call memcpy + // with length zero. + self.map.push((start, end)); + } else { + self.map.insert(next, (start, end)); + } + return true; } } diff --git a/compiler/rustc_index/src/vec.rs b/compiler/rustc_index/src/vec.rs index 4656994ac08..a8c611e18ff 100644 --- a/compiler/rustc_index/src/vec.rs +++ b/compiler/rustc_index/src/vec.rs @@ -65,12 +65,6 @@ impl<S: Encoder, I: Idx, T: Encodable<S>> Encodable<S> for IndexVec<I, T> { } } -impl<S: Encoder, I: Idx, T: Encodable<S>> Encodable<S> for &IndexVec<I, T> { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - Encodable::encode(&self.raw, s) - } -} - impl<D: Decoder, I: Idx, T: Decodable<D>> Decodable<D> for IndexVec<I, T> { fn decode(d: &mut D) -> Self { IndexVec { raw: Decodable::decode(d), _marker: PhantomData } diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index 58c309a5c52..39f7d30e81a 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -6,7 +6,7 @@ //! is always the "expected" output from the POV of diagnostics. //! //! Examples: -//! +//! ```ignore (fragment) //! infcx.at(cause, param_env).sub(a, b) //! // requires that `a <: b`, with `a` considered the "expected" type //! @@ -15,11 +15,11 @@ //! //! infcx.at(cause, param_env).eq(a, b) //! // requires that `a == b`, with `a` considered the "expected" type -//! +//! ``` //! For finer-grained control, you can also do use `trace`: -//! +//! ```ignore (fragment) //! infcx.at(...).trace(a, b).sub(&c, &d) -//! +//! ``` //! This will set `a` and `b` as the "root" values for //! error-reporting, but actually operate on `c` and `d`. This is //! sometimes useful when the types of `c` and `d` are not traceable diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 5e67c8cfa27..07e51afd904 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -87,9 +87,9 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { /// /// with a mapping M that maps `'?0` to `'static`. But if we found that there /// exists only one possible impl of `Trait`, and it looks like - /// - /// impl<T> Trait<'static> for T { .. } - /// + /// ```ignore (illustrative) + /// impl<T> Trait<'static> for T { .. } + /// ``` /// then we would prepare a query result R that (among other /// things) includes a mapping to `'?0 := 'static`. When /// canonicalizing this query result R, we would leave this diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index 0ca0fe33614..534106ac446 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -202,7 +202,7 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> { /// /// A good example of this is the following: /// - /// ```rust + /// ```compile_fail,E0308 /// #![feature(generic_const_exprs)] /// /// fn bind<const N: usize>(value: [u8; N]) -> [u8; 3 + 4] { @@ -776,21 +776,6 @@ pub trait ConstEquateRelation<'tcx>: TypeRelation<'tcx> { fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>); } -pub trait RelateResultCompare<'tcx, T> { - fn compare<F>(&self, t: T, f: F) -> RelateResult<'tcx, T> - where - F: FnOnce() -> TypeError<'tcx>; -} - -impl<'tcx, T: Clone + PartialEq> RelateResultCompare<'tcx, T> for RelateResult<'tcx, T> { - fn compare<F>(&self, t: T, f: F) -> RelateResult<'tcx, T> - where - F: FnOnce() -> TypeError<'tcx>, - { - self.clone().and_then(|s| if s == t { self.clone() } else { Err(f()) }) - } -} - pub fn const_unification_error<'tcx>( a_is_expected: bool, (a, b): (ty::Const<'tcx>, ty::Const<'tcx>), diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 2e50dbff510..11c893a7cb6 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -70,7 +70,7 @@ use rustc_middle::ty::{ self, error::TypeError, subst::{GenericArgKind, Subst, SubstsRef}, - Binder, List, Region, Ty, TyCtxt, TypeFoldable, + Binder, EarlyBinder, List, Region, Ty, TyCtxt, TypeFoldable, }; use rustc_span::{sym, BytePos, DesugaringKind, Pos, Span}; use rustc_target::spec::abi; @@ -889,7 +889,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// /// For the following code: /// - /// ```no_run + /// ```ignore (illustrative) /// let x: Foo<Bar<Qux>> = foo::<Bar<Qux>>(); /// ``` /// @@ -961,12 +961,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { for (def_id, actual) in iter::zip(default_params, substs.iter().rev()) { match actual.unpack() { GenericArgKind::Const(c) => { - if self.tcx.const_param_default(def_id).subst(self.tcx, substs) != c { + if EarlyBinder(self.tcx.const_param_default(def_id)).subst(self.tcx, substs) + != c + { break; } } GenericArgKind::Type(ty) => { - if self.tcx.type_of(def_id).subst(self.tcx, substs) != ty { + if self.tcx.bound_type_of(def_id).subst(self.tcx, substs) != ty { break; } } @@ -1383,8 +1385,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => { - let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1); - let sig2 = self.tcx.fn_sig(*did2).subst(self.tcx, substs2); + let sig1 = self.tcx.bound_fn_sig(*did1).subst(self.tcx, substs1); + let sig2 = self.tcx.bound_fn_sig(*did2).subst(self.tcx, substs2); let mut values = self.cmp_fn_sig(&sig1, &sig2); let path1 = format!(" {{{}}}", self.tcx.def_path_str_with_substs(*did1, substs1)); let path2 = format!(" {{{}}}", self.tcx.def_path_str_with_substs(*did2, substs2)); @@ -1395,7 +1397,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } (ty::FnDef(did1, substs1), ty::FnPtr(sig2)) => { - let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1); + let sig1 = self.tcx.bound_fn_sig(*did1).subst(self.tcx, substs1); let mut values = self.cmp_fn_sig(&sig1, sig2); values.0.push_highlighted(format!( " {{{}}}", @@ -1405,7 +1407,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } (ty::FnPtr(sig1), ty::FnDef(did2, substs2)) => { - let sig2 = self.tcx.fn_sig(*did2).subst(self.tcx, substs2); + let sig2 = self.tcx.bound_fn_sig(*did2).subst(self.tcx, substs2); let mut values = self.cmp_fn_sig(sig1, &sig2); values.1.push_normal(format!( " {{{}}}", @@ -1440,6 +1442,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// the message in `secondary_span` as the primary label, and apply the message that would /// otherwise be used for the primary label on the `secondary_span` `Span`. This applies on /// E0271, like `src/test/ui/issues/issue-39970.stderr`. + #[tracing::instrument( + level = "debug", + skip(self, diag, secondary_span, swap_secondary_and_primary, force_label) + )] pub fn note_type_err( &self, diag: &mut Diagnostic, @@ -1451,7 +1457,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { force_label: bool, ) { let span = cause.span(self.tcx); - debug!("note_type_err cause={:?} values={:?}, terr={:?}", cause, values, terr); // For some types of errors, expected-found does not make // sense, so just ignore the values we were given. @@ -1584,9 +1589,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { Variable(ty::error::ExpectedFound<Ty<'a>>), Fixed(&'static str), } - let (expected_found, exp_found, is_simple_error) = match values { - None => (None, Mismatch::Fixed("type"), false), + let (expected_found, exp_found, is_simple_error, values) = match values { + None => (None, Mismatch::Fixed("type"), false, None), Some(values) => { + let values = self.resolve_vars_if_possible(values); let (is_simple_error, exp_found) = match values { ValuePairs::Terms(infer::ExpectedFound { expected: ty::Term::Ty(expected), @@ -1614,13 +1620,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { return; } }; - (vals, exp_found, is_simple_error) + (vals, exp_found, is_simple_error, Some(values)) } }; - // Ignore msg for object safe coercion - // since E0038 message will be printed match terr { + // Ignore msg for object safe coercion + // since E0038 message will be printed TypeError::ObjectUnsafeCoercion(_) => {} _ => { let mut label_or_note = |span: Span, msg: &str| { @@ -1771,6 +1777,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // It reads better to have the error origin as the final // thing. self.note_error_origin(diag, cause, exp_found, terr); + + debug!(?diag); } fn suggest_tuple_pattern( @@ -1846,9 +1854,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // Future::Output let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0]; - let bounds = self.tcx.explicit_item_bounds(*def_id); + let bounds = self.tcx.bound_explicit_item_bounds(*def_id); - for (predicate, _) in bounds { + for predicate in bounds.transpose_iter().map(|e| e.map_bound(|(p, _)| *p)) { let predicate = predicate.subst(self.tcx, substs); let output = predicate .kind() @@ -1872,7 +1880,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// A possible error is to forget to add `.await` when using futures: /// - /// ``` + /// ```compile_fail,E0308 /// async fn make_u32() -> u32 { /// 22 /// } diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index b1eb9f0da87..e1a2a237c23 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -731,6 +731,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // | help: specify type like: `<Impl as Into<u32>>::into(foo_impl)` // | // = note: cannot satisfy `Impl: Into<_>` + debug!(?segment); if !impl_candidates.is_empty() && e.span.contains(span) && let Some(expr) = exprs.first() && let ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind @@ -738,26 +739,51 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { { let mut eraser = TypeParamEraser(self.tcx); let candidate_len = impl_candidates.len(); - let mut suggestions: Vec<_> = impl_candidates.iter().map(|candidate| { + let mut suggestions: Vec<_> = impl_candidates.iter().filter_map(|candidate| { + let trait_item = self.tcx + .associated_items(candidate.def_id) + .find_by_name_and_kind( + self.tcx, + segment.ident, + ty::AssocKind::Fn, + candidate.def_id + ); + if trait_item.is_none() { + return None; + } + let prefix = if let Some(trait_item) = trait_item + && let Some(trait_m) = trait_item.def_id.as_local() + && let hir::TraitItemKind::Fn(fn_, _) = &self.tcx.hir().trait_item(hir::TraitItemId { def_id: trait_m }).kind + { + match fn_.decl.implicit_self { + hir::ImplicitSelfKind::ImmRef => "&", + hir::ImplicitSelfKind::MutRef => "&mut ", + _ => "", + } + } else { + "" + }; let candidate = candidate.super_fold_with(&mut eraser); - vec![ - (expr.span.shrink_to_lo(), format!("{}::{}(", candidate, segment.ident)), + Some(vec![ + (expr.span.shrink_to_lo(), format!("{}::{}({}", candidate, segment.ident, prefix)), if exprs.len() == 1 { (expr.span.shrink_to_hi().with_hi(e.span.hi()), ")".to_string()) } else { (expr.span.shrink_to_hi().with_hi(exprs[1].span.lo()), ", ".to_string()) }, - ] + ]) }).collect(); suggestions.sort_by(|a, b| a[0].1.cmp(&b[0].1)); - err.multipart_suggestions( - &format!( - "use the fully qualified path for the potential candidate{}", - pluralize!(candidate_len), - ), - suggestions.into_iter(), - Applicability::MaybeIncorrect, - ); + if !suggestions.is_empty() { + err.multipart_suggestions( + &format!( + "use the fully qualified path for the potential candidate{}", + pluralize!(candidate_len), + ), + suggestions.into_iter(), + Applicability::MaybeIncorrect, + ); + } } // Suggest specifying type params or point out the return type of the call: // @@ -840,6 +866,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } + self.report_ambiguous_type_parameter(&mut err, arg); err.span_label( span, arg_data.cannot_infer_msg(use_diag.filter(|d| d.applies_to(span))), @@ -907,6 +934,28 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } + fn report_ambiguous_type_parameter(&self, err: &mut Diagnostic, arg: GenericArg<'tcx>) { + if let GenericArgKind::Type(ty) = arg.unpack() + && let ty::Infer(ty::TyVar(ty_vid)) = *ty.kind() + { + let mut inner = self.inner.borrow_mut(); + let ty_vars = &inner.type_variables(); + let var_origin = ty_vars.var_origin(ty_vid); + if let TypeVariableOriginKind::TypeParameterDefinition(_, Some(def_id)) = + var_origin.kind + && let Some(parent_def_id) = self.tcx.parent(def_id).as_local() + && let Some(node) = self.tcx.hir().find_by_def_id(parent_def_id) + { + match node { + hir::Node::Item(item) if matches!(item.kind, hir::ItemKind::Impl(_) | hir::ItemKind::Fn(..)) => (), + hir::Node::ImplItem(impl_item) if matches!(impl_item.kind, hir::ImplItemKind::Fn(..)) => (), + _ => return, + } + err.span_help(self.tcx.def_span(def_id), "type parameter declared here"); + } + } + } + pub fn need_type_info_err_in_generator( &self, kind: hir::GeneratorKind, diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs index be9db6aa25b..cb72cb41a7c 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs @@ -18,7 +18,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { /// /// Consider a case where we have /// - /// ```no_run + /// ```compile_fail,E0623 /// fn foo(x: &mut Vec<&u8>, y: &u8) { /// x.push(y); /// } diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs index 9c5b944c1e9..e5ae835e813 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs @@ -14,7 +14,7 @@ use rustc_middle::ty::{self, Region, TyCtxt}; /// br - the bound region corresponding to the above region which is of type `BrAnon(_)` /// /// # Example -/// ``` +/// ```compile_fail,E0623 /// fn foo(x: &mut Vec<&u8>, y: &u8) /// { x.push(y); } /// ``` diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 6ec929f9895..4ef6f240c48 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -159,9 +159,9 @@ pub struct InferCtxtInner<'tcx> { /// outlive the lifetime 'a". These constraints derive from /// instantiated type parameters. So if you had a struct defined /// like - /// + /// ```ignore (illustrative) /// struct Foo<T:'static> { ... } - /// + /// ``` /// then in some expression `let x = Foo { ... }` it will /// instantiate the type parameter `T` with a fresh type `$0`. At /// the same time, it will record a region obligation of @@ -181,7 +181,7 @@ pub struct InferCtxtInner<'tcx> { /// /// Before running `resolve_regions_and_report_errors`, the creator /// of the inference context is expected to invoke - /// `process_region_obligations` (defined in `self::region_obligations`) + /// [`InferCtxt::process_registered_region_obligations`] /// for each body-id in this map, which will process the /// obligations within. This is expected to be done 'late enough' /// that all type inference variables have been bound and so forth. diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs index 94a795f613e..6592e0ae8ec 100644 --- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs +++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs @@ -859,7 +859,7 @@ where delegate: &'me mut D, - /// After we generalize this type, we are going to relative it to + /// After we generalize this type, we are going to relate it to /// some other type. What will be the variance at this point? ambient_variance: ty::Variance, diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index 78e6f3a05be..92c0ed84057 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -309,14 +309,22 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// the same as generating an outlives constraint on `Tc` itself. /// For example, if we had a function like this: /// - /// ```rust + /// ``` + /// # #![feature(type_alias_impl_trait)] + /// # fn main() {} + /// # trait Foo<'a> {} + /// # impl<'a, T> Foo<'a> for (&'a u32, T) {} /// fn foo<'a, T>(x: &'a u32, y: T) -> impl Foo<'a> { /// (x, y) /// } /// /// // Equivalent to: + /// # mod dummy { use super::*; /// type FooReturn<'a, T> = impl Foo<'a>; - /// fn foo<'a, T>(..) -> FooReturn<'a, T> { .. } + /// fn foo<'a, T>(x: &'a u32, y: T) -> FooReturn<'a, T> { + /// (x, y) + /// } + /// # } /// ``` /// /// then the hidden type `Tc` would be `(&'0 u32, T)` (where `'0` @@ -553,9 +561,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { obligations = self.at(&cause, param_env).eq(prev, hidden_ty)?.obligations; } - let item_bounds = tcx.explicit_item_bounds(def_id); + let item_bounds = tcx.bound_explicit_item_bounds(def_id); - for (predicate, _) in item_bounds { + for predicate in item_bounds.transpose_iter().map(|e| e.map_bound(|(p, _)| *p)) { debug!(?predicate); let predicate = predicate.subst(tcx, substs); @@ -602,17 +610,17 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// Returns `true` if `opaque_hir_id` is a sibling or a child of a sibling of `def_id`. /// /// Example: -/// ```rust +/// ```ignore UNSOLVED (is this a bug?) +/// # #![feature(type_alias_impl_trait)] /// pub mod foo { /// pub mod bar { -/// pub trait Bar { .. } -/// +/// pub trait Bar { /* ... */ } /// pub type Baz = impl Bar; /// -/// fn f1() -> Baz { .. } +/// # impl Bar for () {} +/// fn f1() -> Baz { /* ... */ } /// } -/// -/// fn f2() -> bar::Baz { .. } +/// fn f2() -> bar::Baz { /* ... */ } /// } /// ``` /// diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs index c8236ce79c0..9ddda7b92eb 100644 --- a/compiler/rustc_infer/src/infer/outlives/env.rs +++ b/compiler/rustc_infer/src/infer/outlives/env.rs @@ -103,7 +103,7 @@ impl<'a, 'tcx> OutlivesEnvironment<'tcx> { /// /// Example: /// - /// ``` + /// ```ignore (pseudo-rust) /// fn foo<T>() { /// callback(for<'a> |x: &'a T| { /// // ^^^^^^^ not legal syntax, but probably should be diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index e3312e6c6e1..2aa535da0e5 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -33,9 +33,9 @@ //! Consider: //! //! ``` -//! fn bar<T>(a: T, b: impl for<'a> Fn(&'a T)); +//! fn bar<T>(a: T, b: impl for<'a> Fn(&'a T)) {} //! fn foo<T>(x: T) { -//! bar(x, |y| { ... }) +//! bar(x, |y| { /* ... */}) //! // ^ closure arg //! } //! ``` @@ -136,7 +136,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { /// /// # Parameters /// - /// - `region_bound_pairs`: the set of region bounds implied by + /// - `region_bound_pairs_map`: the set of region bounds implied by /// the parameters and where-clauses. In particular, each pair /// `('a, K)` in this list tells us that the bounds in scope /// indicate that `K: 'a`, where `K` is either a generic @@ -147,12 +147,6 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { /// - `param_env` is the parameter environment for the enclosing function. /// - `body_id` is the body-id whose region obligations are being /// processed. - /// - /// # Returns - /// - /// This function may have to perform normalizations, and hence it - /// returns an `InferOk` with subobligations that must be - /// processed. #[instrument(level = "debug", skip(self, region_bound_pairs_map))] pub fn process_registered_region_obligations( &self, diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index f69212c599b..1c521c90686 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -4,7 +4,7 @@ use rustc_data_structures::captures::Captures; use rustc_data_structures::sso::SsoHashSet; use rustc_hir::def_id::DefId; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst}; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt}; /// The `TypeOutlives` struct has the job of "lowering" a `T: 'a` /// obligation into a series of `'a: 'b` constraints and "verifys", as @@ -290,7 +290,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { debug!("projection_bounds(projection_ty={:?})", projection_ty); let tcx = self.tcx; self.region_bounds_declared_on_associated_item(projection_ty.item_def_id) - .map(move |r| r.subst(tcx, projection_ty.substs)) + .map(move |r| EarlyBinder(r).subst(tcx, projection_ty.substs)) } /// Given the `DefId` of an associated item, returns any region @@ -313,7 +313,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { /// /// It will not, however, work for higher-ranked bounds like: /// - /// ```rust + /// ```compile_fail,E0311 /// trait Foo<'a, 'b> /// where for<'x> <Self as Foo<'x, 'b>>::Bar: 'x /// { diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index 4ea1c3f76c8..efe254387dc 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -174,19 +174,19 @@ pub enum GenericKind<'tcx> { /// Describes the things that some `GenericKind` value `G` is known to /// outlive. Each variant of `VerifyBound` can be thought of as a /// function: -/// -/// fn(min: Region) -> bool { .. } -/// +/// ```ignore (pseudo-rust) +/// fn(min: Region) -> bool { .. } +/// ``` /// where `true` means that the region `min` meets that `G: min`. /// (False means nothing.) /// /// So, for example, if we have the type `T` and we have in scope that /// `T: 'a` and `T: 'b`, then the verify bound might be: -/// -/// fn(min: Region) -> bool { -/// ('a: min) || ('b: min) -/// } -/// +/// ```ignore (pseudo-rust) +/// fn(min: Region) -> bool { +/// ('a: min) || ('b: min) +/// } +/// ``` /// This is described with an `AnyRegion('a, 'b)` node. #[derive(Debug, Clone)] pub enum VerifyBound<'tcx> { @@ -194,7 +194,7 @@ pub enum VerifyBound<'tcx> { /// following, where `G` is the generic for which this verify /// bound was created: /// - /// ```rust + /// ```ignore (pseudo-rust) /// fn(min) -> bool { /// if G == K { /// B(min) @@ -218,7 +218,7 @@ pub enum VerifyBound<'tcx> { /// /// So we would compile to a verify-bound like /// - /// ``` + /// ```ignore (illustrative) /// IfEq(<T as Trait<'a>>::Item, AnyRegion('a)) /// ``` /// @@ -228,7 +228,7 @@ pub enum VerifyBound<'tcx> { /// Given a region `R`, expands to the function: /// - /// ``` + /// ```ignore (pseudo-rust) /// fn(min) -> bool { /// R: min /// } @@ -243,7 +243,7 @@ pub enum VerifyBound<'tcx> { /// Given a set of bounds `B`, expands to the function: /// - /// ```rust + /// ```ignore (pseudo-rust) /// fn(min) -> bool { /// exists (b in B) { b(min) } /// } @@ -255,7 +255,7 @@ pub enum VerifyBound<'tcx> { /// Given a set of bounds `B`, expands to the function: /// - /// ```rust + /// ```ignore (pseudo-rust) /// fn(min) -> bool { /// forall (b in B) { b(min) } /// } diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs index 74c44089045..a0e2965b605 100644 --- a/compiler/rustc_infer/src/infer/type_variable.rs +++ b/compiler/rustc_infer/src/infer/type_variable.rs @@ -73,10 +73,10 @@ pub struct TypeVariableStorage<'tcx> { /// table exists only to help with the occurs check. In particular, /// we want to report constraints like these as an occurs check /// violation: - /// - /// ?1 <: ?3 - /// Box<?3> <: ?1 - /// + /// ``` text + /// ?1 <: ?3 + /// Box<?3> <: ?1 + /// ``` /// Without this second table, what would happen in a case like /// this is that we would instantiate `?1` with a generalized /// type like `Box<?6>`. We would then relate `Box<?3> <: Box<?6>` @@ -287,8 +287,9 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { /// related via equality or subtyping will yield the same root /// variable (per the union-find algorithm), so `sub_root_var(a) /// == sub_root_var(b)` implies that: - /// - /// exists X. (a <: X || X <: a) && (b <: X || X <: b) + /// ```text + /// exists X. (a <: X || X <: a) && (b <: X || X <: b) + /// ``` pub fn sub_root_var(&mut self, vid: ty::TyVid) -> ty::TyVid { self.sub_relations().find(vid) } diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs index 85bb727a6c8..4df4de21a0f 100644 --- a/compiler/rustc_infer/src/traits/mod.rs +++ b/compiler/rustc_infer/src/traits/mod.rs @@ -69,7 +69,7 @@ impl<'tcx> PredicateObligation<'tcx> { } } -impl TraitObligation<'_> { +impl<'tcx> TraitObligation<'tcx> { /// Returns `true` if the trait predicate is considered `const` in its ParamEnv. pub fn is_const(&self) -> bool { match (self.predicate.skip_binder().constness, self.param_env.constness()) { @@ -77,6 +77,13 @@ impl TraitObligation<'_> { _ => false, } } + + pub fn derived_cause( + &self, + variant: impl FnOnce(DerivedObligationCause<'tcx>) -> ObligationCauseCode<'tcx>, + ) -> ObligationCause<'tcx> { + self.cause.clone().derived_cause(self.predicate, variant) + } } // `PredicateObligation` is used a lot. Make sure it doesn't unintentionally get bigger. diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 08987dff660..afbf48fceb5 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -494,13 +494,7 @@ pub fn lower_to_hir<'res, 'tcx>( arena: &'tcx rustc_ast_lowering::Arena<'tcx>, ) -> &'tcx Crate<'tcx> { // Lower AST to HIR. - let hir_crate = rustc_ast_lowering::lower_crate( - sess, - &*krate, - resolver, - rustc_parse::nt_to_tokenstream, - arena, - ); + let hir_crate = rustc_ast_lowering::lower_crate(sess, &*krate, resolver, arena); // Drop AST to free memory sess.time("drop_ast", || std::mem::drop(krate)); @@ -1009,6 +1003,10 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { }); } ); + + // This check has to be run after all lints are done processing. We don't + // define a lint filter, as all lint checks should have finished at this point. + sess.time("check_lint_expectations", || tcx.check_expectations(None)); }); Ok(()) diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 22ab62ac372..136f0443fa0 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -258,10 +258,7 @@ impl<'tcx> Queries<'tcx> { /// an error. fn check_for_rustc_errors_attr(tcx: TyCtxt<'_>) { let Some((def_id, _)) = tcx.entry_fn(()) else { return }; - - let attrs = &*tcx.get_attrs(def_id); - let attrs = attrs.iter().filter(|attr| attr.has_name(sym::rustc_error)); - for attr in attrs { + for attr in tcx.get_attrs(def_id, sym::rustc_error) { match attr.meta_item_list() { // Check if there is a `#[rustc_error(delay_span_bug_from_inside_query)]`. Some(list) diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 3564f15e210..3d50ed2096e 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -36,12 +36,12 @@ use rustc_feature::{deprecated_attributes, AttributeGate, BuiltinAttribute, Gate use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet, CRATE_DEF_ID}; -use rustc_hir::{ForeignItemKind, GenericParamKind, HirId, PatKind}; +use rustc_hir::{ForeignItemKind, GenericParamKind, HirId, PatKind, PredicateOrigin}; use rustc_index::vec::Idx; -use rustc_middle::lint::LintDiagnosticBuilder; +use rustc_middle::lint::{in_external_macro, LintDiagnosticBuilder}; use rustc_middle::ty::layout::{LayoutError, LayoutOf}; use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::subst::{GenericArgKind, Subst}; +use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::Instance; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::lint::{BuiltinLintDiagnostics, FutureIncompatibilityReason}; @@ -551,7 +551,7 @@ impl MissingDoc { } } - let attrs = cx.tcx.get_attrs(def_id.to_def_id()); + let attrs = cx.tcx.hir().attrs(cx.tcx.hir().local_def_id_to_hir_id(def_id)); let has_doc = attrs.iter().any(has_doc); if !has_doc { cx.struct_span_lint( @@ -1252,7 +1252,6 @@ declare_lint_pass!(MutableTransmutes => [MUTABLE_TRANSMUTES]); impl<'tcx> LateLintPass<'tcx> for MutableTransmutes { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { - use rustc_target::spec::abi::Abi::RustIntrinsic; if let Some((&ty::Ref(_, _, from_mt), &ty::Ref(_, _, to_mt))) = get_transmute_from_to(cx, expr).map(|(ty1, ty2)| (ty1.kind(), ty2.kind())) { @@ -1287,8 +1286,7 @@ impl<'tcx> LateLintPass<'tcx> for MutableTransmutes { } fn def_id_is_transmute(cx: &LateContext<'_>, def_id: DefId) -> bool { - cx.tcx.fn_sig(def_id).abi() == RustIntrinsic - && cx.tcx.item_name(def_id) == sym::transmute + cx.tcx.is_intrinsic(def_id) && cx.tcx.item_name(def_id) == sym::transmute } } } @@ -2115,6 +2113,7 @@ impl ExplicitOutlivesRequirements { None } }) + .filter(|(_, span)| !in_external_macro(tcx.sess, *span)) .collect() } @@ -2226,7 +2225,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { Self::lifetimes_outliving_type(inferred_outlives, index), &predicate.bounds, predicate.span, - predicate.in_where_clause, + predicate.origin == PredicateOrigin::WhereClause, ) } _ => { @@ -2706,7 +2705,7 @@ impl SymbolName { } impl ClashingExternDeclarations { - crate fn new() -> Self { + pub(crate) fn new() -> Self { ClashingExternDeclarations { seen_decls: FxHashMap::default() } } /// Insert a new foreign item into the seen set. If a symbol with the same name already exists @@ -2737,11 +2736,7 @@ impl ClashingExternDeclarations { // bottleneck, this does just fine. ( overridden_link_name, - tcx.get_attrs(fi.def_id.to_def_id()) - .iter() - .find(|at| at.has_name(sym::link_name)) - .unwrap() - .span, + tcx.get_attr(fi.def_id.to_def_id(), sym::link_name).unwrap().span, ) }) { @@ -2776,7 +2771,7 @@ impl ClashingExternDeclarations { let mut ty = ty; loop { if let ty::Adt(def, substs) = *ty.kind() { - let is_transparent = def.subst(tcx, substs).repr().transparent(); + let is_transparent = def.repr().transparent(); let is_non_null = crate::types::nonnull_optimization_guaranteed(tcx, def); debug!( "non_transparent_ty({:?}) -- type is transparent? {}, type is non-null? {}", @@ -2836,11 +2831,7 @@ impl ClashingExternDeclarations { ensure_sufficient_stack(|| { match (a_kind, b_kind) { - (Adt(a_def, a_substs), Adt(b_def, b_substs)) => { - let a = a.subst(cx.tcx, a_substs); - let b = b.subst(cx.tcx, b_substs); - debug!("Comparing {:?} and {:?}", a, b); - + (Adt(a_def, _), Adt(b_def, _)) => { // We can immediately rule out these types as structurally same if // their layouts differ. match compare_layouts(a, b) { diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index d7cd5ec04f3..2c6bdef361a 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -819,6 +819,43 @@ pub trait LintContext: Sized { "see issue #89122 <https://github.com/rust-lang/rust/issues/89122> for more information", ); }, + BuiltinLintDiagnostics::SingleUseLifetime { + param_span, + use_span: Some((use_span, elide)), + deletion_span, + } => { + debug!(?param_span, ?use_span, ?deletion_span); + db.span_label(param_span, "this lifetime..."); + db.span_label(use_span, "...is used only here"); + let msg = "elide the single-use lifetime"; + let (use_span, replace_lt) = if elide { + let use_span = sess.source_map().span_extend_while( + use_span, + char::is_whitespace, + ).unwrap_or(use_span); + (use_span, String::new()) + } else { + (use_span, "'_".to_owned()) + }; + db.multipart_suggestion( + msg, + vec![(deletion_span, String::new()), (use_span, replace_lt)], + Applicability::MachineApplicable, + ); + }, + BuiltinLintDiagnostics::SingleUseLifetime { + param_span: _, + use_span: None, + deletion_span, + } => { + debug!(?deletion_span); + db.span_suggestion( + deletion_span, + "elide the unused lifetime", + String::new(), + Applicability::MachineApplicable, + ); + }, } // Rewrap `db`, and pass control to the user. decorate(LintDiagnosticBuilder::new(db)); diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index b4262f184c8..8e505152fc4 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -18,8 +18,7 @@ use crate::context::{EarlyContext, LintContext, LintStore}; use crate::passes::{EarlyLintPass, EarlyLintPassObject}; use rustc_ast::ptr::P; use rustc_ast::visit::{self as ast_visit, Visitor}; -use rustc_ast::AstLike; -use rustc_ast::{self as ast, walk_list}; +use rustc_ast::{self as ast, walk_list, HasAttrs}; use rustc_middle::ty::RegisteredTools; use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass}; use rustc_session::Session; @@ -240,6 +239,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> fn visit_generic_param(&mut self, param: &'a ast::GenericParam) { run_early_pass!(self, check_generic_param, param); + self.check_id(param.id); ast_visit::walk_generic_param(self, param); } @@ -273,7 +273,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> }); } - fn visit_lifetime(&mut self, lt: &'a ast::Lifetime) { + fn visit_lifetime(&mut self, lt: &'a ast::Lifetime, _: ast_visit::LifetimeCtxt) { run_early_pass!(self, check_lifetime, lt); self.check_id(lt.id); } diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs index 67f5aa0540f..dc48ac0a618 100644 --- a/compiler/rustc_lint/src/expect.rs +++ b/compiler/rustc_lint/src/expect.rs @@ -1,10 +1,16 @@ use crate::builtin; use rustc_hir::HirId; +use rustc_middle::ty::query::Providers; use rustc_middle::{lint::LintExpectation, ty::TyCtxt}; use rustc_session::lint::LintExpectationId; use rustc_span::symbol::sym; +use rustc_span::Symbol; -pub fn check_expectations(tcx: TyCtxt<'_>) { +pub(crate) fn provide(providers: &mut Providers) { + *providers = Providers { check_expectations, ..*providers }; +} + +fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option<Symbol>) { if !tcx.sess.features_untracked().enabled(sym::lint_reasons) { return; } @@ -13,7 +19,9 @@ pub fn check_expectations(tcx: TyCtxt<'_>) { let lint_expectations = &tcx.lint_levels(()).lint_expectations; for (id, expectation) in lint_expectations { - if !fulfilled_expectations.contains(id) { + if !fulfilled_expectations.contains(id) + && tool_filter.map_or(true, |filter| expectation.lint_tool == Some(filter)) + { // This check will always be true, since `lint_expectations` only // holds stable ids if let LintExpectationId::Stable { hir_id, .. } = id { diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index 0ac636b878e..772ab7fe226 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -34,7 +34,7 @@ use tracing::debug; /// Extract the `LintStore` from the query context. /// This function exists because we've erased `LintStore` as `dyn Any` in the context. -crate fn unerased_lint_store(tcx: TyCtxt<'_>) -> &LintStore { +pub(crate) fn unerased_lint_store(tcx: TyCtxt<'_>) -> &LintStore { let store: &dyn Any = &*tcx.lint_store; store.downcast_ref().unwrap() } @@ -503,7 +503,4 @@ pub fn check_crate<'tcx, T: LateLintPass<'tcx>>( }); }, ); - - // This check has to be run after all lints are done processing for this crate - tcx.sess.time("check_lint_expectations", || crate::expect::check_expectations(tcx)); } diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 01f1d1e79ac..54f2c725279 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -14,7 +14,7 @@ use rustc_middle::lint::{ use rustc_middle::ty::query::Providers; use rustc_middle::ty::{RegisteredTools, TyCtxt}; use rustc_session::lint::{ - builtin::{self, FORBIDDEN_LINT_GROUPS, UNFULFILLED_LINT_EXPECTATIONS}, + builtin::{self, FORBIDDEN_LINT_GROUPS, SINGLE_USE_LIFETIMES, UNFULFILLED_LINT_EXPECTATIONS}, Level, Lint, LintExpectationId, LintId, }; use rustc_session::parse::{add_feature_diagnostics, feature_err}; @@ -259,6 +259,14 @@ impl<'s> LintLevelsBuilder<'s> { let sess = self.sess; let bad_attr = |span| struct_span_err!(sess, span, E0452, "malformed lint attribute input"); for (attr_index, attr) in attrs.iter().enumerate() { + if attr.has_name(sym::automatically_derived) { + self.current_specs_mut().insert( + LintId::of(SINGLE_USE_LIFETIMES), + (Level::Allow, LintLevelSource::Default), + ); + continue; + } + let level = match Level::from_attr(attr) { None => continue, Some(Level::Expect(unstable_id)) if let Some(hir_id) = source_hir_id => { @@ -371,7 +379,12 @@ impl<'s> LintLevelsBuilder<'s> { }; self.lint_expectations.push(( expect_id, - LintExpectation::new(reason, sp, is_unfulfilled_lint_expectations), + LintExpectation::new( + reason, + sp, + is_unfulfilled_lint_expectations, + tool_name, + ), )); } let src = LintLevelSource::Node( @@ -400,8 +413,10 @@ impl<'s> LintLevelsBuilder<'s> { self.insert_spec(*id, (level, src)); } if let Level::Expect(expect_id) = level { - self.lint_expectations - .push((expect_id, LintExpectation::new(reason, sp, false))); + self.lint_expectations.push(( + expect_id, + LintExpectation::new(reason, sp, false, tool_name), + )); } } Err((Some(ids), ref new_lint_name)) => { @@ -444,8 +459,10 @@ impl<'s> LintLevelsBuilder<'s> { self.insert_spec(*id, (level, src)); } if let Level::Expect(expect_id) = level { - self.lint_expectations - .push((expect_id, LintExpectation::new(reason, sp, false))); + self.lint_expectations.push(( + expect_id, + LintExpectation::new(reason, sp, false, tool_name), + )); } } Err((None, _)) => { @@ -550,8 +567,10 @@ impl<'s> LintLevelsBuilder<'s> { } } if let Level::Expect(expect_id) = level { - self.lint_expectations - .push((expect_id, LintExpectation::new(reason, sp, false))); + self.lint_expectations.push(( + expect_id, + LintExpectation::new(reason, sp, false, tool_name), + )); } } else { panic!("renamed lint does not exist: {}", new_name); diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 54b08dfe840..7c68429e1e9 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -30,7 +30,6 @@ #![feature(array_windows)] #![feature(box_patterns)] #![feature(control_flow_enum)] -#![feature(crate_visibility_modifier)] #![feature(if_let_guard)] #![feature(iter_intersperse)] #![feature(iter_order_by)] @@ -109,6 +108,7 @@ pub use rustc_session::lint::{LintArray, LintPass}; pub fn provide(providers: &mut Providers) { levels::provide(providers); + expect::provide(providers); *providers = Providers { lint_mod, ..*providers }; } @@ -302,6 +302,7 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) { PATH_STATEMENTS, UNUSED_ATTRIBUTES, UNUSED_MACROS, + UNUSED_MACRO_RULES, UNUSED_ALLOCATION, UNUSED_DOC_COMMENTS, UNUSED_EXTERN_CRATES, @@ -490,6 +491,11 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) { "converted into hard error, see RFC 2972 \ <https://github.com/rust-lang/rfcs/blob/master/text/2972-constrained-naked.md> for more information", ); + store.register_removed( + "mutable_borrow_reservation_conflict", + "now allowed, see issue #59159 \ + <https://github.com/rust-lang/rust/issues/59159> for more information", + ); } fn register_internals(store: &mut LintStore) { diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index dfce30171ff..55b1ba9cd96 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -651,7 +651,7 @@ declare_lint! { declare_lint_pass!(ImproperCTypesDefinitions => [IMPROPER_CTYPES_DEFINITIONS]); #[derive(Clone, Copy)] -crate enum CItemKind { +pub(crate) enum CItemKind { Declaration, Definition, } @@ -667,8 +667,11 @@ enum FfiResult<'tcx> { FfiUnsafe { ty: Ty<'tcx>, reason: String, help: Option<String> }, } -crate fn nonnull_optimization_guaranteed<'tcx>(tcx: TyCtxt<'tcx>, def: ty::AdtDef<'tcx>) -> bool { - tcx.get_attrs(def.did()).iter().any(|a| a.has_name(sym::rustc_nonnull_optimization_guaranteed)) +pub(crate) fn nonnull_optimization_guaranteed<'tcx>( + tcx: TyCtxt<'tcx>, + def: ty::AdtDef<'tcx>, +) -> bool { + tcx.has_attr(def.did(), sym::rustc_nonnull_optimization_guaranteed) } /// `repr(transparent)` structs can have a single non-ZST field, this function returns that @@ -766,7 +769,7 @@ fn get_nullable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'t /// Currently restricted to function pointers, boxes, references, `core::num::NonZero*`, /// `core::ptr::NonNull`, and `#[repr(transparent)]` newtypes. /// FIXME: This duplicates code in codegen. -crate fn repr_nullable_ptr<'tcx>( +pub(crate) fn repr_nullable_ptr<'tcx>( cx: &LateContext<'tcx>, ty: Ty<'tcx>, ckind: CItemKind, diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 494bdaa1e2b..8cae95f46dc 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -303,26 +303,25 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { descr_pre_path: &str, descr_post_path: &str, ) -> bool { - for attr in cx.tcx.get_attrs(def_id).iter() { - if attr.has_name(sym::must_use) { - cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| { - let msg = format!( - "unused {}`{}`{} that must be used", - descr_pre_path, - cx.tcx.def_path_str(def_id), - descr_post_path - ); - let mut err = lint.build(&msg); - // check for #[must_use = "..."] - if let Some(note) = attr.value_str() { - err.note(note.as_str()); - } - err.emit(); - }); - return true; - } + if let Some(attr) = cx.tcx.get_attr(def_id, sym::must_use) { + cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| { + let msg = format!( + "unused {}`{}`{} that must be used", + descr_pre_path, + cx.tcx.def_path_str(def_id), + descr_post_path + ); + let mut err = lint.build(&msg); + // check for #[must_use = "..."] + if let Some(note) = attr.value_str() { + err.note(note.as_str()); + } + err.emit(); + }); + true + } else { + false } - false } } } diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index a42e3d5d957..f9429702783 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -749,6 +749,10 @@ declare_lint! { declare_lint! { /// The `unused_macros` lint detects macros that were not used. /// + /// Note that this lint is distinct from the `unused_macro_rules` lint, + /// which checks for single rules that never match of an otherwise used + /// macro, and thus never expand. + /// /// ### Example /// /// ```rust @@ -776,6 +780,46 @@ declare_lint! { } declare_lint! { + /// The `unused_macro_rules` lint detects macro rules that were not used. + /// + /// Note that the lint is distinct from the `unused_macros` lint, which + /// fires if the entire macro is never called, while this lint fires for + /// single unused rules of the macro that is otherwise used. + /// `unused_macro_rules` fires only if `unused_macros` wouldn't fire. + /// + /// ### Example + /// + /// ```rust + /// #[warn(unused_macro_rules)] + /// macro_rules! unused_empty { + /// (hello) => { println!("Hello, world!") }; // This rule is unused + /// () => { println!("empty") }; // This rule is used + /// } + /// + /// fn main() { + /// unused_empty!(hello); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Unused macro rules may signal a mistake or unfinished code. Furthermore, + /// they slow down compilation. Right now, silencing the warning is not + /// supported on a single rule level, so you have to add an allow to the + /// entire macro definition. + /// + /// If you intended to export the macro to make it + /// available outside of the crate, use the [`macro_export` attribute]. + /// + /// [`macro_export` attribute]: https://doc.rust-lang.org/reference/macros-by-example.html#path-based-scope + pub UNUSED_MACRO_RULES, + Allow, + "detects macro rules that were not used" +} + +declare_lint! { /// The `warnings` lint allows you to change the level of other /// lints which produce warnings. /// @@ -1109,7 +1153,8 @@ declare_lint! { /// /// ### Example /// - /// ```rust,compile_fail + /// ```compile_fail + /// #![deny(unaligned_references)] /// #[repr(packed)] /// pub struct Foo { /// field1: u64, @@ -2200,13 +2245,12 @@ declare_lint! { /// used by user code. /// /// This lint is only enabled in the standard library. It works with the - /// use of `#[rustc_deprecated]` with a `since` field of a version in the - /// future. This allows something to be marked as deprecated in a future - /// version, and then this lint will ensure that the item is no longer - /// used in the standard library. See the [stability documentation] for - /// more details. + /// use of `#[deprecated]` with a `since` field of a version in the future. + /// This allows something to be marked as deprecated in a future version, + /// and then this lint will ensure that the item is no longer used in the + /// standard library. See the [stability documentation] for more details. /// - /// [stability documentation]: https://rustc-dev-guide.rust-lang.org/stability.html#rustc_deprecated + /// [stability documentation]: https://rustc-dev-guide.rust-lang.org/stability.html#deprecated pub DEPRECATED_IN_FUTURE, Allow, "detects use of items that will be deprecated in a future version", @@ -2346,40 +2390,6 @@ declare_lint! { } declare_lint! { - /// The `mutable_borrow_reservation_conflict` lint detects the reservation - /// of a two-phased borrow that conflicts with other shared borrows. - /// - /// ### Example - /// - /// ```rust - /// let mut v = vec![0, 1, 2]; - /// let shared = &v; - /// v.push(shared.len()); - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// This is a [future-incompatible] lint to transition this to a hard error - /// in the future. See [issue #59159] for a complete description of the - /// problem, and some possible solutions. - /// - /// [issue #59159]: https://github.com/rust-lang/rust/issues/59159 - /// [future-incompatible]: ../index.md#future-incompatible-lints - pub MUTABLE_BORROW_RESERVATION_CONFLICT, - Warn, - "reservation of a two-phased borrow conflicts with other shared borrows", - @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::Custom( - "this borrowing pattern was not meant to be accepted, \ - and may become a hard error in the future" - ), - reference: "issue #59159 <https://github.com/rust-lang/rust/issues/59159>", - }; -} - -declare_lint! { /// The `soft_unstable` lint detects unstable features that were /// unintentionally allowed on stable. /// @@ -3138,6 +3148,7 @@ declare_lint_pass! { OVERLAPPING_RANGE_ENDPOINTS, BINDINGS_WITH_VARIANT_NAME, UNUSED_MACROS, + UNUSED_MACRO_RULES, WARNINGS, UNUSED_FEATURES, STABLE_FEATURES, @@ -3179,7 +3190,6 @@ declare_lint_pass! { META_VARIABLE_MISUSE, DEPRECATED_IN_FUTURE, AMBIGUOUS_ASSOCIATED_ITEMS, - MUTABLE_BORROW_RESERVATION_CONFLICT, INDIRECT_STRUCTURAL_MATCH, POINTER_STRUCTURAL_MATCH, NONTRIVIAL_STRUCTURAL_MATCH, diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 57b4f96dc10..e50abf6cf25 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -423,7 +423,11 @@ pub enum BuiltinLintDiagnostics { DeprecatedMacro(Option<Symbol>, Span), MissingAbi(Span, Abi), UnusedDocComment(Span), - UnusedBuiltinAttribute { attr_name: Symbol, macro_name: String, invoc_span: Span }, + UnusedBuiltinAttribute { + attr_name: Symbol, + macro_name: String, + invoc_span: Span, + }, PatternsInFnsWithoutBody(Span, Ident), LegacyDeriveHelpers(Span), ProcMacroBackCompat(String), @@ -435,6 +439,16 @@ pub enum BuiltinLintDiagnostics { UnicodeTextFlow(Span, String), UnexpectedCfg((Symbol, Span), Option<(Symbol, Span)>), DeprecatedWhereclauseLocation(Span, String), + SingleUseLifetime { + /// Span of the parameter which declares this lifetime. + param_span: Span, + /// Span of the code that should be removed when eliding this lifetime. + /// This span should include leading or trailing comma. + deletion_span: Span, + /// Span of the single use, or None if the lifetime is never used. + /// If true, the lifetime will be fully elided. + use_span: Option<(Span, bool)>, + }, } /// Lints that are buffered up early on in the `Session` before the diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index ac758c15cca..7729ec6bef4 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -324,9 +324,10 @@ fn main() { let stdcppname = if target.contains("openbsd") { if target.contains("sparc64") { "estdc++" } else { "c++" } - } else if target.contains("freebsd") { - "c++" - } else if target.contains("darwin") { + } else if target.contains("darwin") + || target.contains("freebsd") + || target.contains("windows-gnullvm") + { "c++" } else if target.contains("netbsd") && llvm_static_stdcpp.is_some() { // NetBSD uses a separate library when relocation is required @@ -365,7 +366,7 @@ fn main() { // Libstdc++ depends on pthread which Rust doesn't link on MinGW // since nothing else requires it. - if target.contains("windows-gnu") { + if target.ends_with("windows-gnu") { println!("cargo:rustc-link-lib=static:-bundle=pthread"); } } diff --git a/compiler/rustc_log/src/lib.rs b/compiler/rustc_log/src/lib.rs index f5e7435d36e..c152815eeca 100644 --- a/compiler/rustc_log/src/lib.rs +++ b/compiler/rustc_log/src/lib.rs @@ -67,11 +67,24 @@ pub fn init_env_logger(env: &str) -> Result<(), Error> { Err(VarError::NotUnicode(_value)) => return Err(Error::NonUnicodeColorValue), }; + let verbose_entry_exit = match env::var_os(String::from(env) + "_ENTRY_EXIT") { + None => false, + Some(v) => { + if &v == "0" { + false + } else { + true + } + } + }; + let layer = tracing_tree::HierarchicalLayer::default() .with_writer(io::stderr) .with_indent_lines(true) .with_ansi(color_logs) .with_targets(true) + .with_verbose_exit(verbose_entry_exit) + .with_verbose_entry(verbose_entry_exit) .with_indent_amount(2); #[cfg(parallel_compiler)] let layer = layer.with_thread_ids(true).with_thread_names(true); diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index f49166433fa..dac3e986e7a 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -5,10 +5,10 @@ use crate::diagnostics::error::{ SessionDiagnosticDeriveError, }; use crate::diagnostics::utils::{ - option_inner_ty, report_error_if_not_applied_to_span, type_matches_path, Applicability, - FieldInfo, HasFieldMap, SetOnce, + report_error_if_not_applied_to_span, report_type_error, type_is_unit, type_matches_path, + Applicability, FieldInfo, FieldInnerTy, HasFieldMap, SetOnce, }; -use proc_macro2::TokenStream; +use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; use std::collections::HashMap; use std::str::FromStr; @@ -113,7 +113,7 @@ impl<'a> SessionDiagnosticDerive<'a> { quote! { #diag.set_arg( stringify!(#ident), - #field_binding.into_diagnostic_arg() + #field_binding ); } } else { @@ -353,37 +353,43 @@ impl SessionDiagnosticDeriveBuilder { info: FieldInfo<'_>, ) -> Result<TokenStream, SessionDiagnosticDeriveError> { let field_binding = &info.binding.binding; - let option_ty = option_inner_ty(&info.ty); - let generated_code = self.generate_non_option_field_code( + + let inner_ty = FieldInnerTy::from_type(&info.ty); + let name = attr.path.segments.last().unwrap().ident.to_string(); + let (binding, needs_destructure) = match (name.as_str(), &inner_ty) { + // `primary_span` can accept a `Vec<Span>` so don't destructure that. + ("primary_span", FieldInnerTy::Vec(_)) => (quote! { #field_binding.clone() }, false), + _ => (quote! { *#field_binding }, true), + }; + + let generated_code = self.generate_inner_field_code( attr, FieldInfo { vis: info.vis, binding: info.binding, - ty: option_ty.unwrap_or(&info.ty), + ty: inner_ty.inner_type().unwrap_or(&info.ty), span: info.span, }, + binding, )?; - if option_ty.is_none() { - Ok(quote! { #generated_code }) + if needs_destructure { + Ok(inner_ty.with(field_binding, generated_code)) } else { - Ok(quote! { - if let Some(#field_binding) = #field_binding { - #generated_code - } - }) + Ok(generated_code) } } - fn generate_non_option_field_code( + fn generate_inner_field_code( &mut self, attr: &Attribute, info: FieldInfo<'_>, + binding: TokenStream, ) -> Result<TokenStream, SessionDiagnosticDeriveError> { let diag = &self.diag; - let field_binding = &info.binding.binding; - let name = attr.path.segments.last().unwrap().ident.to_string(); + let ident = &attr.path.segments.last().unwrap().ident; + let name = ident.to_string(); let name = name.as_str(); let meta = attr.parse_meta()?; @@ -397,23 +403,41 @@ impl SessionDiagnosticDeriveBuilder { "primary_span" => { report_error_if_not_applied_to_span(attr, &info)?; Ok(quote! { - #diag.set_span(*#field_binding); + #diag.set_span(#binding); }) } - "label" | "note" | "help" => { + "label" => { report_error_if_not_applied_to_span(attr, &info)?; - Ok(self.add_subdiagnostic(field_binding, name, name)) + Ok(self.add_spanned_subdiagnostic(binding, ident, name)) + } + "note" | "help" => { + if type_matches_path(&info.ty, &["rustc_span", "Span"]) { + Ok(self.add_spanned_subdiagnostic(binding, ident, name)) + } else if type_is_unit(&info.ty) { + Ok(self.add_subdiagnostic(ident, name)) + } else { + report_type_error(attr, "`Span` or `()`")?; + } } - "subdiagnostic" => Ok(quote! { #diag.subdiagnostic(*#field_binding); }), + "subdiagnostic" => Ok(quote! { #diag.subdiagnostic(#binding); }), _ => throw_invalid_attr!(attr, &meta, |diag| { diag .help("only `skip_arg`, `primary_span`, `label`, `note`, `help` and `subdiagnostic` are valid field attributes") }), }, Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(ref s), .. }) => match name { - "label" | "note" | "help" => { + "label" => { report_error_if_not_applied_to_span(attr, &info)?; - Ok(self.add_subdiagnostic(field_binding, name, &s.value())) + Ok(self.add_spanned_subdiagnostic(binding, ident, &s.value())) + } + "note" | "help" => { + if type_matches_path(&info.ty, &["rustc_span", "Span"]) { + Ok(self.add_spanned_subdiagnostic(binding, ident, &s.value())) + } else if type_is_unit(&info.ty) { + Ok(self.add_subdiagnostic(ident, &s.value())) + } else { + report_type_error(attr, "`Span` or `()`")?; + } } _ => throw_invalid_attr!(attr, &meta, |diag| { diag.help("only `label`, `note` and `help` are valid field attributes") @@ -505,12 +529,12 @@ impl SessionDiagnosticDeriveBuilder { } } - /// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug and - /// `fluent_attr_identifier`. - fn add_subdiagnostic( + /// Adds a spanned subdiagnostic by generating a `diag.span_$kind` call with the current slug + /// and `fluent_attr_identifier`. + fn add_spanned_subdiagnostic( &self, - field_binding: &proc_macro2::Ident, - kind: &str, + field_binding: TokenStream, + kind: &Ident, fluent_attr_identifier: &str, ) -> TokenStream { let diag = &self.diag; @@ -520,12 +544,22 @@ impl SessionDiagnosticDeriveBuilder { let fn_name = format_ident!("span_{}", kind); quote! { #diag.#fn_name( - *#field_binding, + #field_binding, rustc_errors::DiagnosticMessage::fluent_attr(#slug, #fluent_attr_identifier) ); } } + /// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug + /// and `fluent_attr_identifier`. + fn add_subdiagnostic(&self, kind: &Ident, fluent_attr_identifier: &str) -> TokenStream { + let diag = &self.diag; + let slug = self.slug.as_ref().map(|(slug, _)| slug.as_str()).unwrap_or("missing-slug"); + quote! { + #diag.#kind(rustc_errors::DiagnosticMessage::fluent_attr(#slug, #fluent_attr_identifier)); + } + } + fn span_and_applicability_of_ty( &self, info: FieldInfo<'_>, diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 961b42f424f..ae5b9dbd9ba 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -5,8 +5,8 @@ use crate::diagnostics::error::{ SessionDiagnosticDeriveError, }; use crate::diagnostics::utils::{ - option_inner_ty, report_error_if_not_applied_to_applicability, - report_error_if_not_applied_to_span, Applicability, FieldInfo, HasFieldMap, SetOnce, + report_error_if_not_applied_to_applicability, report_error_if_not_applied_to_span, + Applicability, FieldInfo, FieldInnerTy, HasFieldMap, SetOnce, }; use proc_macro2::TokenStream; use quote::{format_ident, quote}; @@ -301,11 +301,11 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> { ) -> Result<TokenStream, SessionDiagnosticDeriveError> { let ast = binding.ast(); - let option_ty = option_inner_ty(&ast.ty); + let inner_ty = FieldInnerTy::from_type(&ast.ty); let info = FieldInfo { vis: &ast.vis, binding: binding, - ty: option_ty.unwrap_or(&ast.ty), + ty: inner_ty.inner_type().unwrap_or(&ast.ty), span: &ast.span(), }; @@ -349,19 +349,11 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> { let generated = quote! { #diag.set_arg( stringify!(#ident), - #binding.into_diagnostic_arg() + #binding ); }; - if option_ty.is_none() { - Ok(quote! { #generated }) - } else { - Ok(quote! { - if let Some(#binding) = #binding { - #generated - } - }) - } + Ok(inner_ty.with(binding, generated)) } fn into_tokens(&mut self) -> Result<TokenStream, SessionDiagnosticDeriveError> { diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index 1f36af0a20b..af5a30880e0 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -1,10 +1,10 @@ use crate::diagnostics::error::{span_err, throw_span_err, SessionDiagnosticDeriveError}; use proc_macro::Span; use proc_macro2::TokenStream; -use quote::{format_ident, quote}; +use quote::{format_ident, quote, ToTokens}; use std::collections::BTreeSet; use std::str::FromStr; -use syn::{spanned::Spanned, Attribute, Meta, Type, Visibility}; +use syn::{spanned::Spanned, Attribute, Meta, Type, TypeTuple, Visibility}; use synstructure::BindingInfo; /// Checks whether the type name of `ty` matches `name`. @@ -25,7 +25,35 @@ pub(crate) fn type_matches_path(ty: &Type, name: &[&str]) -> bool { } } -/// Reports an error if the field's type is not `Applicability`. +/// Checks whether the type `ty` is `()`. +pub(crate) fn type_is_unit(ty: &Type) -> bool { + if let Type::Tuple(TypeTuple { elems, .. }) = ty { elems.is_empty() } else { false } +} + +/// Reports a type error for field with `attr`. +pub(crate) fn report_type_error( + attr: &Attribute, + ty_name: &str, +) -> Result<!, SessionDiagnosticDeriveError> { + let name = attr.path.segments.last().unwrap().ident.to_string(); + let meta = attr.parse_meta()?; + + throw_span_err!( + attr.span().unwrap(), + &format!( + "the `#[{}{}]` attribute can only be applied to fields of type {}", + name, + match meta { + Meta::Path(_) => "", + Meta::NameValue(_) => " = ...", + Meta::List(_) => "(...)", + }, + ty_name + ) + ); +} + +/// Reports an error if the field's type does not match `path`. fn report_error_if_not_applied_to_ty( attr: &Attribute, info: &FieldInfo<'_>, @@ -33,23 +61,7 @@ fn report_error_if_not_applied_to_ty( ty_name: &str, ) -> Result<(), SessionDiagnosticDeriveError> { if !type_matches_path(&info.ty, path) { - let name = attr.path.segments.last().unwrap().ident.to_string(); - let name = name.as_str(); - let meta = attr.parse_meta()?; - - throw_span_err!( - attr.span().unwrap(), - &format!( - "the `#[{}{}]` attribute can only be applied to fields of type `{}`", - name, - match meta { - Meta::Path(_) => "", - Meta::NameValue(_) => " = ...", - Meta::List(_) => "(...)", - }, - ty_name - ) - ); + report_type_error(attr, ty_name)?; } Ok(()) @@ -64,7 +76,7 @@ pub(crate) fn report_error_if_not_applied_to_applicability( attr, info, &["rustc_errors", "Applicability"], - "Applicability", + "`Applicability`", ) } @@ -73,25 +85,74 @@ pub(crate) fn report_error_if_not_applied_to_span( attr: &Attribute, info: &FieldInfo<'_>, ) -> Result<(), SessionDiagnosticDeriveError> { - report_error_if_not_applied_to_ty(attr, info, &["rustc_span", "Span"], "Span") + report_error_if_not_applied_to_ty(attr, info, &["rustc_span", "Span"], "`Span`") +} + +/// Inner type of a field and type of wrapper. +pub(crate) enum FieldInnerTy<'ty> { + /// Field is wrapped in a `Option<$inner>`. + Option(&'ty Type), + /// Field is wrapped in a `Vec<$inner>`. + Vec(&'ty Type), + /// Field isn't wrapped in an outer type. + None, } -/// If `ty` is an Option, returns `Some(inner type)`, otherwise returns `None`. -pub(crate) fn option_inner_ty(ty: &Type) -> Option<&Type> { - if type_matches_path(ty, &["std", "option", "Option"]) { +impl<'ty> FieldInnerTy<'ty> { + /// Returns inner type for a field, if there is one. + /// + /// - If `ty` is an `Option`, returns `FieldInnerTy::Option { inner: (inner type) }`. + /// - If `ty` is a `Vec`, returns `FieldInnerTy::Vec { inner: (inner type) }`. + /// - Otherwise returns `None`. + pub(crate) fn from_type(ty: &'ty Type) -> Self { + let variant: &dyn Fn(&'ty Type) -> FieldInnerTy<'ty> = + if type_matches_path(ty, &["std", "option", "Option"]) { + &FieldInnerTy::Option + } else if type_matches_path(ty, &["std", "vec", "Vec"]) { + &FieldInnerTy::Vec + } else { + return FieldInnerTy::None; + }; + if let Type::Path(ty_path) = ty { let path = &ty_path.path; let ty = path.segments.iter().last().unwrap(); if let syn::PathArguments::AngleBracketed(bracketed) = &ty.arguments { if bracketed.args.len() == 1 { if let syn::GenericArgument::Type(ty) = &bracketed.args[0] { - return Some(ty); + return variant(ty); } } } } + + unreachable!(); + } + + /// Returns `Option` containing inner type if there is one. + pub(crate) fn inner_type(&self) -> Option<&'ty Type> { + match self { + FieldInnerTy::Option(inner) | FieldInnerTy::Vec(inner) => Some(inner), + FieldInnerTy::None => None, + } + } + + /// Surrounds `inner` with destructured wrapper type, exposing inner type as `binding`. + pub(crate) fn with(&self, binding: impl ToTokens, inner: impl ToTokens) -> TokenStream { + match self { + FieldInnerTy::Option(..) => quote! { + if let Some(#binding) = #binding { + #inner + } + }, + FieldInnerTy::Vec(..) => quote! { + for #binding in #binding { + #inner + } + }, + FieldInnerTy::None => quote! { #inner }, + } } - None } /// Field information passed to the builder. Deliberately omits attrs to discourage the diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index b01e01414e8..0baebdb7130 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -1,5 +1,6 @@ #![feature(allow_internal_unstable)] #![feature(let_else)] +#![feature(never_type)] #![feature(proc_macro_diagnostic)] #![allow(rustc::default_hash_types)] #![recursion_limit = "128"] diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 3c545e6a0d2..dfc675a0494 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -70,7 +70,7 @@ pub enum LoadedMacro { ProcMacro(SyntaxExtension), } -crate struct Library { +pub(crate) struct Library { pub source: CrateSource, pub metadata: MetadataBlob, } @@ -82,7 +82,7 @@ enum LoadResult { /// A reference to `CrateMetadata` that can also give access to whole crate store when necessary. #[derive(Clone, Copy)] -crate struct CrateMetadataRef<'a> { +pub(crate) struct CrateMetadataRef<'a> { pub cdata: &'a CrateMetadata, pub cstore: &'a CStore, } @@ -133,7 +133,7 @@ impl CStore { CrateNum::new(self.metas.len() - 1) } - crate fn get_crate_data(&self, cnum: CrateNum) -> CrateMetadataRef<'_> { + pub(crate) fn get_crate_data(&self, cnum: CrateNum) -> CrateMetadataRef<'_> { let cdata = self.metas[cnum] .as_ref() .unwrap_or_else(|| panic!("Failed to get crate data for {:?}", cnum)); @@ -145,7 +145,7 @@ impl CStore { self.metas[cnum] = Some(Lrc::new(data)); } - crate fn iter_crate_data(&self) -> impl Iterator<Item = (CrateNum, &CrateMetadata)> { + pub(crate) fn iter_crate_data(&self) -> impl Iterator<Item = (CrateNum, &CrateMetadata)> { self.metas .iter_enumerated() .filter_map(|(cnum, data)| data.as_ref().map(|data| (cnum, &**data))) @@ -164,7 +164,7 @@ impl CStore { } } - crate fn crate_dependencies_in_postorder(&self, cnum: CrateNum) -> Vec<CrateNum> { + pub(crate) fn crate_dependencies_in_postorder(&self, cnum: CrateNum) -> Vec<CrateNum> { let mut deps = Vec::new(); if cnum == LOCAL_CRATE { for (cnum, _) in self.iter_crate_data() { @@ -182,15 +182,15 @@ impl CStore { deps } - crate fn injected_panic_runtime(&self) -> Option<CrateNum> { + pub(crate) fn injected_panic_runtime(&self) -> Option<CrateNum> { self.injected_panic_runtime } - crate fn allocator_kind(&self) -> Option<AllocatorKind> { + pub(crate) fn allocator_kind(&self) -> Option<AllocatorKind> { self.allocator_kind } - crate fn has_global_allocator(&self) -> bool { + pub(crate) fn has_global_allocator(&self) -> bool { self.has_global_allocator } diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs index ddc3e10fa48..245b2076ebc 100644 --- a/compiler/rustc_metadata/src/dependency_format.rs +++ b/compiler/rustc_metadata/src/dependency_format.rs @@ -62,7 +62,7 @@ use rustc_session::cstore::CrateDepKind; use rustc_session::cstore::LinkagePreference::{self, RequireDynamic, RequireStatic}; use rustc_target::spec::PanicStrategy; -crate fn calculate(tcx: TyCtxt<'_>) -> Dependencies { +pub(crate) fn calculate(tcx: TyCtxt<'_>) -> Dependencies { tcx.sess .crate_types() .iter() diff --git a/compiler/rustc_metadata/src/foreign_modules.rs b/compiler/rustc_metadata/src/foreign_modules.rs index 97fcbeb4ccc..2ca4cd17fdf 100644 --- a/compiler/rustc_metadata/src/foreign_modules.rs +++ b/compiler/rustc_metadata/src/foreign_modules.rs @@ -3,10 +3,10 @@ use rustc_hir::def::DefKind; use rustc_middle::ty::TyCtxt; use rustc_session::cstore::ForeignModule; -crate fn collect(tcx: TyCtxt<'_>) -> Vec<ForeignModule> { +pub(crate) fn collect(tcx: TyCtxt<'_>) -> Vec<ForeignModule> { let mut modules = Vec::new(); for id in tcx.hir().items() { - if !matches!(tcx.hir().def_kind(id.def_id), DefKind::ForeignMod) { + if !matches!(tcx.def_kind(id.def_id), DefKind::ForeignMod) { continue; } let item = tcx.hir().item(id); diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index dfed9dd15a7..3df18098a07 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -1,7 +1,8 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![feature(crate_visibility_modifier)] +#![feature(decl_macro)] #![feature(drain_filter)] #![feature(generators)] +#![feature(let_chains)] #![feature(let_else)] #![feature(nll)] #![feature(once_cell)] diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 43bda7c0734..dbe53224e2a 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -239,7 +239,7 @@ use std::{cmp, fmt, fs}; use tracing::{debug, info}; #[derive(Clone)] -crate struct CrateLocator<'a> { +pub(crate) struct CrateLocator<'a> { // Immutable per-session configuration. only_needs_metadata: bool, sysroot: &'a Path, @@ -260,19 +260,19 @@ crate struct CrateLocator<'a> { } #[derive(Clone)] -crate struct CratePaths { +pub(crate) struct CratePaths { name: Symbol, source: CrateSource, } impl CratePaths { - crate fn new(name: Symbol, source: CrateSource) -> CratePaths { + pub(crate) fn new(name: Symbol, source: CrateSource) -> CratePaths { CratePaths { name, source } } } #[derive(Copy, Clone, PartialEq)] -crate enum CrateFlavor { +pub(crate) enum CrateFlavor { Rlib, Rmeta, Dylib, @@ -289,7 +289,7 @@ impl fmt::Display for CrateFlavor { } impl<'a> CrateLocator<'a> { - crate fn new( + pub(crate) fn new( sess: &'a Session, metadata_loader: &'a dyn MetadataLoader, crate_name: Symbol, @@ -344,7 +344,7 @@ impl<'a> CrateLocator<'a> { } } - crate fn reset(&mut self) { + pub(crate) fn reset(&mut self) { self.crate_rejections.via_hash.clear(); self.crate_rejections.via_triple.clear(); self.crate_rejections.via_kind.clear(); @@ -353,7 +353,7 @@ impl<'a> CrateLocator<'a> { self.crate_rejections.via_invalid.clear(); } - crate fn maybe_load_library_crate(&mut self) -> Result<Option<Library>, CrateError> { + pub(crate) fn maybe_load_library_crate(&mut self) -> Result<Option<Library>, CrateError> { if !self.exact_paths.is_empty() { return self.find_commandline_library(); } @@ -728,7 +728,7 @@ impl<'a> CrateLocator<'a> { Ok(self.extract_lib(rlibs, rmetas, dylibs)?.map(|(_, lib)| lib)) } - crate fn into_error(self, root: Option<CratePaths>) -> CrateError { + pub(crate) fn into_error(self, root: Option<CratePaths>) -> CrateError { CrateError::LocatorCombined(CombinedLocatorError { crate_name: self.crate_name, root, @@ -894,7 +894,7 @@ struct CrateRejections { /// Candidate rejection reasons collected during crate search. /// If no candidate is accepted, then these reasons are presented to the user, /// otherwise they are ignored. -crate struct CombinedLocatorError { +pub(crate) struct CombinedLocatorError { crate_name: Symbol, root: Option<CratePaths>, triple: TargetTriple, @@ -903,7 +903,7 @@ crate struct CombinedLocatorError { crate_rejections: CrateRejections, } -crate enum CrateError { +pub(crate) enum CrateError { NonAsciiName(Symbol), ExternLocationNotExist(Symbol, PathBuf), ExternLocationNotFile(Symbol, PathBuf), @@ -937,7 +937,7 @@ impl fmt::Display for MetadataError<'_> { } impl CrateError { - crate fn report(self, sess: &Session, span: Span, missing_core: bool) { + pub(crate) fn report(self, sess: &Session, span: Span, missing_core: bool) { let mut diag = match self { CrateError::NonAsciiName(crate_name) => sess.struct_span_err( span, diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 43b6ecee794..8d044be195a 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -1,4 +1,4 @@ -use rustc_ast::CRATE_NODE_ID; +use rustc_ast::{NestedMetaItem, CRATE_NODE_ID}; use rustc_attr as attr; use rustc_data_structures::fx::FxHashSet; use rustc_errors::struct_span_err; @@ -9,11 +9,10 @@ use rustc_session::cstore::{DllCallingConvention, DllImport, NativeLib}; use rustc_session::parse::feature_err; use rustc_session::utils::NativeLibKind; use rustc_session::Session; -use rustc_span::symbol::{kw, sym, Symbol}; -use rustc_span::Span; +use rustc_span::symbol::{sym, Symbol}; use rustc_target::spec::abi::Abi; -crate fn collect(tcx: TyCtxt<'_>) -> Vec<NativeLib> { +pub(crate) fn collect(tcx: TyCtxt<'_>) -> Vec<NativeLib> { let mut collector = Collector { tcx, libs: Vec::new() }; for id in tcx.hir().items() { collector.process_item(id); @@ -22,7 +21,7 @@ crate fn collect(tcx: TyCtxt<'_>) -> Vec<NativeLib> { collector.libs } -crate fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool { +pub(crate) fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool { match lib.cfg { Some(ref cfg) => attr::cfg_matches(cfg, &sess.parse_sess, CRATE_NODE_ID, None), None => true, @@ -36,7 +35,7 @@ struct Collector<'tcx> { impl<'tcx> Collector<'tcx> { fn process_item(&mut self, id: rustc_hir::ItemId) { - if !matches!(self.tcx.hir().def_kind(id.def_id), DefKind::ForeignMod) { + if !matches!(self.tcx.def_kind(id.def_id), DefKind::ForeignMod) { return; } @@ -51,283 +50,315 @@ impl<'tcx> Collector<'tcx> { // Process all of the #[link(..)]-style arguments let sess = &self.tcx.sess; + let features = self.tcx.features(); for m in self.tcx.hir().attrs(it.hir_id()).iter().filter(|a| a.has_name(sym::link)) { let Some(items) = m.meta_item_list() else { continue; }; - let mut lib = NativeLib { - name: None, - kind: NativeLibKind::Unspecified, - cfg: None, - foreign_module: Some(it.def_id.to_def_id()), - wasm_import_module: None, - verbatim: None, - dll_imports: Vec::new(), - }; - let mut kind_specified = false; + let mut name = None; + let mut kind = None; + let mut modifiers = None; + let mut cfg = None; + let mut wasm_import_module = None; for item in items.iter() { - if item.has_name(sym::kind) { - kind_specified = true; - let Some(kind) = item.value_str() else { - continue; // skip like historical compilers - }; - lib.kind = match kind.as_str() { - "static" => NativeLibKind::Static { bundle: None, whole_archive: None }, - "static-nobundle" => { - sess.struct_span_warn( - item.span(), - "library kind `static-nobundle` has been superseded by specifying \ - modifier `-bundle` with library kind `static`", - ) - .emit(); - if !self.tcx.features().static_nobundle { - feature_err( - &self.tcx.sess.parse_sess, - sym::static_nobundle, - item.span(), - "kind=\"static-nobundle\" is unstable", - ) - .emit(); - } - NativeLibKind::Static { bundle: Some(false), whole_archive: None } + match item.name_or_empty() { + sym::name => { + if name.is_some() { + let msg = "multiple `name` arguments in a single `#[link]` attribute"; + sess.span_err(item.span(), msg); + continue; } - "dylib" => NativeLibKind::Dylib { as_needed: None }, - "framework" => NativeLibKind::Framework { as_needed: None }, - "raw-dylib" => NativeLibKind::RawDylib, - k => { - struct_span_err!(sess, item.span(), E0458, "unknown kind: `{}`", k) - .span_label(item.span(), "unknown kind") - .span_label(m.span, "") + let Some(link_name) = item.value_str() else { + let msg = "link name must be of the form `name = \"string\"`"; + sess.span_err(item.span(), msg); + continue; + }; + let span = item.name_value_literal_span().unwrap(); + if link_name.is_empty() { + struct_span_err!(sess, span, E0454, "link name must not be empty") + .span_label(span, "empty link name") .emit(); - NativeLibKind::Unspecified } - }; - } else if item.has_name(sym::name) { - lib.name = item.value_str(); - } else if item.has_name(sym::cfg) { - let Some(cfg) = item.meta_item_list() else { - continue; // skip like historical compilers - }; - if cfg.is_empty() { - sess.span_err(item.span(), "`cfg()` must have an argument"); - } else if let cfg @ Some(..) = cfg[0].meta_item() { - lib.cfg = cfg.cloned(); - } else { - sess.span_err(cfg[0].span(), "invalid argument for `cfg(..)`"); + name = Some((link_name, span)); } - } else if item.has_name(sym::wasm_import_module) { - match item.value_str() { - Some(s) => lib.wasm_import_module = Some(s), - None => { - let msg = "must be of the form `#[link(wasm_import_module = \"...\")]`"; + sym::kind => { + if kind.is_some() { + let msg = "multiple `kind` arguments in a single `#[link]` attribute"; sess.span_err(item.span(), msg); + continue; } - } - } else { - // currently, like past compilers, ignore unknown - // directives here. - } - } - - // Do this outside the above loop so we don't depend on modifiers coming - // after kinds - let mut modifiers_count = 0; - for item in items.iter().filter(|item| item.has_name(sym::modifiers)) { - if let Some(modifiers) = item.value_str() { - modifiers_count += 1; - let span = item.name_value_literal_span().unwrap(); - let mut has_duplicate_modifiers = false; - for modifier in modifiers.as_str().split(',') { - let (modifier, value) = match modifier.strip_prefix(&['+', '-']) { - Some(m) => (m, modifier.starts_with('+')), - None => { - // Note: this error also excludes the case with empty modifier - // string, like `modifiers = ""`. - sess.span_err( - span, - "invalid linking modifier syntax, expected '+' or '-' prefix \ - before one of: bundle, verbatim, whole-archive, as-needed", - ); - continue; - } + let Some(link_kind) = item.value_str() else { + let msg = "link kind must be of the form `kind = \"string\"`"; + sess.span_err(item.span(), msg); + continue; }; - match (modifier, &mut lib.kind) { - ("bundle", NativeLibKind::Static { bundle, .. }) => { - if bundle.is_some() { - has_duplicate_modifiers = true; - } - *bundle = Some(value); - } - ("bundle", _) => { - sess.span_err( + let span = item.name_value_literal_span().unwrap(); + let link_kind = match link_kind.as_str() { + "static" => NativeLibKind::Static { bundle: None, whole_archive: None }, + "static-nobundle" => { + sess.struct_span_warn( span, - "bundle linking modifier is only compatible with \ - `static` linking kind", - ); - } - - ("verbatim", _) => { - if lib.verbatim.is_some() { - has_duplicate_modifiers = true; + "link kind `static-nobundle` has been superseded by specifying \ + modifier `-bundle` with link kind `static`", + ) + .emit(); + if !features.static_nobundle { + feature_err( + &sess.parse_sess, + sym::static_nobundle, + span, + "link kind `static-nobundle` is unstable", + ) + .emit(); } - lib.verbatim = Some(value); + NativeLibKind::Static { bundle: Some(false), whole_archive: None } } - - ("whole-archive", NativeLibKind::Static { whole_archive, .. }) => { - if whole_archive.is_some() { - has_duplicate_modifiers = true; + "dylib" => NativeLibKind::Dylib { as_needed: None }, + "framework" => { + if !sess.target.is_like_osx { + struct_span_err!( + sess, + span, + E0455, + "link kind `framework` is only supported on Apple targets" + ) + .emit(); } - *whole_archive = Some(value); + NativeLibKind::Framework { as_needed: None } } - ("whole-archive", _) => { - sess.span_err( - span, - "whole-archive linking modifier is only compatible with \ - `static` linking kind", - ); - } - - ("as-needed", NativeLibKind::Dylib { as_needed }) - | ("as-needed", NativeLibKind::Framework { as_needed }) => { - if as_needed.is_some() { - has_duplicate_modifiers = true; + "raw-dylib" => { + if !sess.target.is_like_windows { + struct_span_err!( + sess, + span, + E0455, + "link kind `raw-dylib` is only supported on Windows targets" + ) + .emit(); + } else if !features.raw_dylib { + feature_err( + &sess.parse_sess, + sym::raw_dylib, + span, + "link kind `raw-dylib` is unstable", + ) + .emit(); } - *as_needed = Some(value); + NativeLibKind::RawDylib } - ("as-needed", _) => { - sess.span_err( - span, - "as-needed linking modifier is only compatible with \ - `dylib` and `framework` linking kinds", - ); - } - - _ => { - sess.span_err( - span, - &format!( - "unrecognized linking modifier `{}`, expected one \ - of: bundle, verbatim, whole-archive, as-needed", - modifier - ), + kind => { + let msg = format!( + "unknown link kind `{kind}`, expected one of: \ + static, dylib, framework, raw-dylib" ); + struct_span_err!(sess, span, E0458, "{}", msg) + .span_label(span, "unknown link kind") + .emit(); + continue; } + }; + kind = Some(link_kind); + } + sym::modifiers => { + if modifiers.is_some() { + let msg = + "multiple `modifiers` arguments in a single `#[link]` attribute"; + sess.span_err(item.span(), msg); + continue; + } + let Some(link_modifiers) = item.value_str() else { + let msg = "link modifiers must be of the form `modifiers = \"string\"`"; + sess.span_err(item.span(), msg); + continue; + }; + modifiers = Some((link_modifiers, item.name_value_literal_span().unwrap())); + } + sym::cfg => { + if cfg.is_some() { + let msg = "multiple `cfg` arguments in a single `#[link]` attribute"; + sess.span_err(item.span(), msg); + continue; + } + let Some(link_cfg) = item.meta_item_list() else { + let msg = "link cfg must be of the form `cfg(/* predicate */)`"; + sess.span_err(item.span(), msg); + continue; + }; + let [NestedMetaItem::MetaItem(link_cfg)] = link_cfg else { + let msg = "link cfg must have a single predicate argument"; + sess.span_err(item.span(), msg); + continue; + }; + if !features.link_cfg { + feature_err( + &sess.parse_sess, + sym::link_cfg, + item.span(), + "link cfg is unstable", + ) + .emit(); + } + cfg = Some(link_cfg.clone()); + } + sym::wasm_import_module => { + if wasm_import_module.is_some() { + let msg = "multiple `wasm_import_module` arguments \ + in a single `#[link]` attribute"; + sess.span_err(item.span(), msg); + continue; } + let Some(link_wasm_import_module) = item.value_str() else { + let msg = "wasm import module must be of the form \ + `wasm_import_module = \"string\"`"; + sess.span_err(item.span(), msg); + continue; + }; + wasm_import_module = Some((link_wasm_import_module, item.span())); } - if has_duplicate_modifiers { - let msg = - "same modifier is used multiple times in a single `modifiers` argument"; + _ => { + let msg = "unexpected `#[link]` argument, expected one of: \ + name, kind, modifiers, cfg, wasm_import_module"; sess.span_err(item.span(), msg); } - } else { - let msg = "must be of the form `#[link(modifiers = \"...\")]`"; - sess.span_err(item.span(), msg); } } - if modifiers_count > 1 { - let msg = "multiple `modifiers` arguments in a single `#[link]` attribute"; - sess.span_err(m.span, msg); + // Do this outside the above loop so we don't depend on modifiers coming after kinds + let mut verbatim = None; + if let Some((modifiers, span)) = modifiers { + for modifier in modifiers.as_str().split(',') { + let (modifier, value) = match modifier.strip_prefix(&['+', '-']) { + Some(m) => (m, modifier.starts_with('+')), + None => { + sess.span_err( + span, + "invalid linking modifier syntax, expected '+' or '-' prefix \ + before one of: bundle, verbatim, whole-archive, as-needed", + ); + continue; + } + }; + + macro report_unstable_modifier($feature: ident) { + if !features.$feature { + feature_err( + &sess.parse_sess, + sym::$feature, + span, + &format!("linking modifier `{modifier}` is unstable"), + ) + .emit(); + } + } + let assign_modifier = |dst: &mut Option<bool>| { + if dst.is_some() { + let msg = format!( + "multiple `{modifier}` modifiers in a single `modifiers` argument" + ); + sess.span_err(span, &msg); + } else { + *dst = Some(value); + } + }; + match (modifier, &mut kind) { + ("bundle", Some(NativeLibKind::Static { bundle, .. })) => { + report_unstable_modifier!(native_link_modifiers_bundle); + assign_modifier(bundle) + } + ("bundle", _) => { + sess.span_err( + span, + "linking modifier `bundle` is only compatible with \ + `static` linking kind", + ); + } + + ("verbatim", _) => { + report_unstable_modifier!(native_link_modifiers_verbatim); + assign_modifier(&mut verbatim) + } + + ("whole-archive", Some(NativeLibKind::Static { whole_archive, .. })) => { + assign_modifier(whole_archive) + } + ("whole-archive", _) => { + sess.span_err( + span, + "linking modifier `whole-archive` is only compatible with \ + `static` linking kind", + ); + } + + ("as-needed", Some(NativeLibKind::Dylib { as_needed })) + | ("as-needed", Some(NativeLibKind::Framework { as_needed })) => { + report_unstable_modifier!(native_link_modifiers_as_needed); + assign_modifier(as_needed) + } + ("as-needed", _) => { + sess.span_err( + span, + "linking modifier `as-needed` is only compatible with \ + `dylib` and `framework` linking kinds", + ); + } + + _ => { + sess.span_err( + span, + format!( + "unknown linking modifier `{modifier}`, expected one of: \ + bundle, verbatim, whole-archive, as-needed" + ), + ); + } + } + } } - // In general we require #[link(name = "...")] but we allow - // #[link(wasm_import_module = "...")] without the `name`. - let requires_name = kind_specified || lib.wasm_import_module.is_none(); - if lib.name.is_none() && requires_name { + if let Some((_, span)) = wasm_import_module { + if name.is_some() || kind.is_some() || modifiers.is_some() || cfg.is_some() { + let msg = "`wasm_import_module` is incompatible with \ + other arguments in `#[link]` attributes"; + sess.span_err(span, msg); + } + } else if name.is_none() { struct_span_err!( sess, m.span, E0459, - "`#[link(...)]` specified without \ - `name = \"foo\"`" + "`#[link]` attribute requires a `name = \"string\"` argument" ) .span_label(m.span, "missing `name` argument") .emit(); } - if lib.kind == NativeLibKind::RawDylib { - lib.dll_imports.extend( + let dll_imports = match kind { + Some(NativeLibKind::RawDylib) => { + if let Some((name, span)) = name && name.as_str().contains('\0') { + sess.span_err( + span, + "link name must not contain NUL characters if link kind is `raw-dylib`", + ); + } foreign_mod_items .iter() - .map(|child_item| self.build_dll_import(abi, child_item)), - ); - } - - self.register_native_lib(Some(m.span), lib); - } - } - - fn register_native_lib(&mut self, span: Option<Span>, lib: NativeLib) { - if lib.name.as_ref().map_or(false, |&s| s == kw::Empty) { - match span { - Some(span) => { - struct_span_err!( - self.tcx.sess, - span, - E0454, - "`#[link(name = \"\")]` given with empty name" - ) - .span_label(span, "empty name given") - .emit(); - } - None => { - self.tcx.sess.err("empty library name given via `-l`"); - } - } - return; - } - let is_osx = self.tcx.sess.target.is_like_osx; - if matches!(lib.kind, NativeLibKind::Framework { .. }) && !is_osx { - let msg = "native frameworks are only available on macOS targets"; - match span { - Some(span) => { - struct_span_err!(self.tcx.sess, span, E0455, "{}", msg).emit(); - } - None => { - self.tcx.sess.err(msg); + .map(|child_item| self.build_dll_import(abi, child_item)) + .collect() } - } - } - if lib.cfg.is_some() && !self.tcx.features().link_cfg { - feature_err( - &self.tcx.sess.parse_sess, - sym::link_cfg, - span.unwrap(), - "kind=\"link_cfg\" is unstable", - ) - .emit(); - } - // this just unwraps lib.name; we already established that it isn't empty above. - if let (NativeLibKind::RawDylib, Some(lib_name)) = (lib.kind, lib.name) { - let Some(span) = span else { - bug!("raw-dylib libraries are not supported on the command line"); + _ => Vec::new(), }; - - if !self.tcx.sess.target.options.is_like_windows { - self.tcx.sess.span_fatal( - span, - "`#[link(...)]` with `kind = \"raw-dylib\"` only supported on Windows", - ); - } - - if lib_name.as_str().contains('\0') { - self.tcx.sess.span_err(span, "library name may not contain NUL characters"); - } - - if !self.tcx.features().raw_dylib { - feature_err( - &self.tcx.sess.parse_sess, - sym::raw_dylib, - span, - "kind=\"raw-dylib\" is unstable", - ) - .emit(); - } + self.libs.push(NativeLib { + name: name.map(|(name, _)| name), + kind: kind.unwrap_or(NativeLibKind::Unspecified), + cfg, + foreign_module: Some(it.def_id.to_def_id()), + wasm_import_module: wasm_import_module.map(|(name, _)| name), + verbatim, + dll_imports, + }); } - - self.libs.push(lib); } // Process libs passed on the command line @@ -335,6 +366,10 @@ impl<'tcx> Collector<'tcx> { // First, check for errors let mut renames = FxHashSet::default(); for lib in &self.tcx.sess.opts.libs { + if let NativeLibKind::Framework { .. } = lib.kind && !self.tcx.sess.target.is_like_osx { + // Cannot check this when parsing options because the target is not yet available. + self.tcx.sess.err("library kind `framework` is only supported on Apple targets"); + } if let Some(ref new_name) = lib.new_name { let any_duplicate = self .libs @@ -342,19 +377,19 @@ impl<'tcx> Collector<'tcx> { .filter_map(|lib| lib.name.as_ref()) .any(|n| n.as_str() == lib.name); if new_name.is_empty() { - self.tcx.sess.err(&format!( + self.tcx.sess.err(format!( "an empty renaming target was specified for library `{}`", lib.name )); } else if !any_duplicate { - self.tcx.sess.err(&format!( + self.tcx.sess.err(format!( "renaming of the library `{}` was specified, \ however this crate contains no `#[link(...)]` \ attributes referencing this library", lib.name )); } else if !renames.insert(&lib.name) { - self.tcx.sess.err(&format!( + self.tcx.sess.err(format!( "multiple renamings were \ specified for library `{}`", lib.name @@ -404,7 +439,7 @@ impl<'tcx> Collector<'tcx> { if existing.is_empty() { // Add if not found let new_name: Option<&str> = passed_lib.new_name.as_deref(); - let lib = NativeLib { + self.libs.push(NativeLib { name: Some(Symbol::intern(new_name.unwrap_or(&passed_lib.name))), kind: passed_lib.kind, cfg: None, @@ -412,8 +447,7 @@ impl<'tcx> Collector<'tcx> { wasm_import_module: None, verbatim: passed_lib.verbatim, dll_imports: Vec::new(), - }; - self.register_native_lib(None, lib); + }); } else { // Move all existing libraries with the same name to the // end of the command line. diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index b25522cfd96..4038af38a2c 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -57,7 +57,7 @@ mod cstore_impl; /// A `MetadataBlob` internally is just a reference counted pointer to /// the actual data, so cloning it is cheap. #[derive(Clone)] -crate struct MetadataBlob(Lrc<MetadataRef>); +pub(crate) struct MetadataBlob(Lrc<MetadataRef>); // This is needed so we can create an OwningRef into the blob. // The data behind a `MetadataBlob` has a stable address because it is @@ -78,9 +78,9 @@ impl std::ops::Deref for MetadataBlob { // local crate numbers (as generated during this session). Each external // crate may refer to types in other external crates, and each has their // own crate numbers. -crate type CrateNumMap = IndexVec<CrateNum, CrateNum>; +pub(crate) type CrateNumMap = IndexVec<CrateNum, CrateNum>; -crate struct CrateMetadata { +pub(crate) struct CrateMetadata { /// The primary crate data - binary metadata blob. blob: MetadataBlob, @@ -744,20 +744,20 @@ where implement_ty_decoder!(DecodeContext<'a, 'tcx>); impl<'tcx> MetadataBlob { - crate fn new(metadata_ref: MetadataRef) -> MetadataBlob { + pub(crate) fn new(metadata_ref: MetadataRef) -> MetadataBlob { MetadataBlob(Lrc::new(metadata_ref)) } - crate fn is_compatible(&self) -> bool { + pub(crate) fn is_compatible(&self) -> bool { self.blob().starts_with(METADATA_HEADER) } - crate fn get_rustc_version(&self) -> String { + pub(crate) fn get_rustc_version(&self) -> String { Lazy::<String>::from_position(NonZeroUsize::new(METADATA_HEADER.len() + 4).unwrap()) .decode(self) } - crate fn get_root(&self) -> CrateRoot<'tcx> { + pub(crate) fn get_root(&self) -> CrateRoot<'tcx> { let slice = &self.blob()[..]; let offset = METADATA_HEADER.len(); let pos = (((slice[offset + 0] as u32) << 24) @@ -767,7 +767,7 @@ impl<'tcx> MetadataBlob { Lazy::<CrateRoot<'tcx>>::from_position(NonZeroUsize::new(pos).unwrap()).decode(self) } - crate fn list_crate_metadata(&self, out: &mut dyn io::Write) -> io::Result<()> { + pub(crate) fn list_crate_metadata(&self, out: &mut dyn io::Write) -> io::Result<()> { let root = self.get_root(); writeln!(out, "Crate info:")?; writeln!(out, "name {}{}", root.name, root.extra_filename)?; @@ -792,27 +792,27 @@ impl<'tcx> MetadataBlob { } impl CrateRoot<'_> { - crate fn is_proc_macro_crate(&self) -> bool { + pub(crate) fn is_proc_macro_crate(&self) -> bool { self.proc_macro_data.is_some() } - crate fn name(&self) -> Symbol { + pub(crate) fn name(&self) -> Symbol { self.name } - crate fn hash(&self) -> Svh { + pub(crate) fn hash(&self) -> Svh { self.hash } - crate fn stable_crate_id(&self) -> StableCrateId { + pub(crate) fn stable_crate_id(&self) -> StableCrateId { self.stable_crate_id } - crate fn triple(&self) -> &TargetTriple { + pub(crate) fn triple(&self) -> &TargetTriple { &self.triple } - crate fn decode_crate_deps<'a>( + pub(crate) fn decode_crate_deps<'a>( &self, metadata: &'a MetadataBlob, ) -> impl ExactSizeIterator<Item = CrateDep> + Captures<'a> { @@ -1752,10 +1752,14 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { fn get_may_have_doc_links(self, index: DefIndex) -> bool { self.root.tables.may_have_doc_links.get(self, index).is_some() } + + fn get_is_intrinsic(self, index: DefIndex) -> bool { + self.root.tables.is_intrinsic.get(self, index).is_some() + } } impl CrateMetadata { - crate fn new( + pub(crate) fn new( sess: &Session, cstore: &CStore, blob: MetadataBlob, @@ -1815,15 +1819,15 @@ impl CrateMetadata { cdata } - crate fn dependencies(&self) -> LockGuard<'_, Vec<CrateNum>> { + pub(crate) fn dependencies(&self) -> LockGuard<'_, Vec<CrateNum>> { self.dependencies.borrow() } - crate fn add_dependency(&self, cnum: CrateNum) { + pub(crate) fn add_dependency(&self, cnum: CrateNum) { self.dependencies.borrow_mut().push(cnum); } - crate fn update_extern_crate(&self, new_extern_crate: ExternCrate) -> bool { + pub(crate) fn update_extern_crate(&self, new_extern_crate: ExternCrate) -> bool { let mut extern_crate = self.extern_crate.borrow_mut(); let update = Some(new_extern_crate.rank()) > extern_crate.as_ref().map(ExternCrate::rank); if update { @@ -1832,59 +1836,59 @@ impl CrateMetadata { update } - crate fn source(&self) -> &CrateSource { + pub(crate) fn source(&self) -> &CrateSource { &*self.source } - crate fn dep_kind(&self) -> CrateDepKind { + pub(crate) fn dep_kind(&self) -> CrateDepKind { *self.dep_kind.lock() } - crate fn update_dep_kind(&self, f: impl FnOnce(CrateDepKind) -> CrateDepKind) { + pub(crate) fn update_dep_kind(&self, f: impl FnOnce(CrateDepKind) -> CrateDepKind) { self.dep_kind.with_lock(|dep_kind| *dep_kind = f(*dep_kind)) } - crate fn panic_strategy(&self) -> PanicStrategy { + pub(crate) fn panic_strategy(&self) -> PanicStrategy { self.root.panic_strategy } - crate fn needs_panic_runtime(&self) -> bool { + pub(crate) fn needs_panic_runtime(&self) -> bool { self.root.needs_panic_runtime } - crate fn is_panic_runtime(&self) -> bool { + pub(crate) fn is_panic_runtime(&self) -> bool { self.root.panic_runtime } - crate fn is_profiler_runtime(&self) -> bool { + pub(crate) fn is_profiler_runtime(&self) -> bool { self.root.profiler_runtime } - crate fn needs_allocator(&self) -> bool { + pub(crate) fn needs_allocator(&self) -> bool { self.root.needs_allocator } - crate fn has_global_allocator(&self) -> bool { + pub(crate) fn has_global_allocator(&self) -> bool { self.root.has_global_allocator } - crate fn has_default_lib_allocator(&self) -> bool { + pub(crate) fn has_default_lib_allocator(&self) -> bool { self.root.has_default_lib_allocator } - crate fn is_proc_macro_crate(&self) -> bool { + pub(crate) fn is_proc_macro_crate(&self) -> bool { self.root.is_proc_macro_crate() } - crate fn name(&self) -> Symbol { + pub(crate) fn name(&self) -> Symbol { self.root.name } - crate fn stable_crate_id(&self) -> StableCrateId { + pub(crate) fn stable_crate_id(&self) -> StableCrateId { self.root.stable_crate_id } - crate fn hash(&self) -> Svh { + pub(crate) fn hash(&self) -> Svh { self.root.hash } diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index c00c6ce2f71..065224a2a65 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -129,6 +129,7 @@ provide! { <'tcx> tcx, def_id, other, cdata, type_of => { table } variances_of => { table } fn_sig => { table } + codegen_fn_attrs => { table } impl_trait_ref => { table } const_param_default => { table } thir_abstract_const => { table } @@ -223,6 +224,7 @@ provide! { <'tcx> tcx, def_id, other, cdata, tcx.arena.alloc_slice(&result) } defined_lib_features => { cdata.get_lib_features(tcx) } + is_intrinsic => { cdata.get_is_intrinsic(def_id.index) } defined_lang_items => { cdata.get_lang_items(tcx) } diagnostic_items => { cdata.get_diagnostic_items() } missing_lang_items => { cdata.get_missing_lang_items(tcx) } diff --git a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs index d66f2b031a8..06045bb3e3d 100644 --- a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs +++ b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs @@ -6,7 +6,7 @@ use rustc_hir::def_path_hash_map::{Config as HashMapConfig, DefPathHashMap}; use rustc_serialize::{opaque, Decodable, Decoder, Encodable, Encoder}; use rustc_span::def_id::{DefIndex, DefPathHash}; -crate enum DefPathHashMapRef<'tcx> { +pub(crate) enum DefPathHashMapRef<'tcx> { OwnedFromMetadata(odht::HashTable<HashMapConfig, OwningRef<MetadataBlob, [u8]>>), BorrowedFromTcx(&'tcx DefPathHashMap), } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index b2eafa035db..e66d226a441 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -14,7 +14,6 @@ use rustc_hir::def_id::{ }; use rustc_hir::definitions::DefPathData; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::lang_items; use rustc_hir::{AnonConst, GenericParamKind}; use rustc_index::bit_set::GrowableBitSet; @@ -25,7 +24,6 @@ use rustc_middle::middle::exported_symbols::{ metadata_symbol_name, ExportedSymbol, SymbolExportInfo, }; use rustc_middle::mir::interpret; -use rustc_middle::thir; use rustc_middle::traits::specialization_graph; use rustc_middle::ty::codec::TyEncoder; use rustc_middle::ty::fast_reject::{self, SimplifiedType, TreatParams}; @@ -34,18 +32,14 @@ use rustc_middle::ty::{self, SymbolName, Ty, TyCtxt}; use rustc_serialize::{opaque, Encodable, Encoder}; use rustc_session::config::CrateType; use rustc_session::cstore::{ForeignModule, LinkagePreference, NativeLib}; +use rustc_span::hygiene::{ExpnIndex, HygieneEncodeContext, MacroKind}; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{ self, DebuggerVisualizerFile, ExternalSource, FileName, SourceFile, Span, SyntaxContext, }; -use rustc_span::{ - hygiene::{ExpnIndex, HygieneEncodeContext, MacroKind}, - RealFileName, -}; use rustc_target::abi::VariantIdx; use std::hash::Hash; use std::num::NonZeroUsize; -use std::path::Path; use tracing::{debug, trace}; pub(super) struct EncodeContext<'a, 'tcx> { @@ -351,18 +345,6 @@ impl<'a, 'tcx> TyEncoder<'tcx> for EncodeContext<'a, 'tcx> { } } -impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for &'tcx [thir::abstract_const::Node<'tcx>] { - fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) -> opaque::EncodeResult { - (**self).encode(s) - } -} - -impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for &'tcx [(ty::Predicate<'tcx>, Span)] { - fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) -> opaque::EncodeResult { - (**self).encode(s) - } -} - /// Helper trait to allow overloading `EncodeContext::lazy` for iterators. trait EncodeContentsForLazy<'a, 'tcx, T: ?Sized + LazyMeta> { fn encode_contents_for_lazy(self, ecx: &mut EncodeContext<'a, 'tcx>) -> T::Meta; @@ -453,7 +435,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { return; } - self.tcx.hir().visit_all_item_likes(&mut self.as_deep_visitor()); + self.tcx.hir().deep_visit_all_item_likes(self); } fn encode_def_path_table(&mut self) { @@ -491,6 +473,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // is done. let required_source_files = self.required_source_files.take().unwrap(); + let working_directory = &self.tcx.sess.opts.working_dir; + let adapted = all_source_files .iter() .enumerate() @@ -503,66 +487,40 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { (!source_file.is_imported() || self.is_proc_macro) }) .map(|(_, source_file)| { - let mut adapted = match source_file.name { - FileName::Real(ref realname) => { - let mut adapted = (**source_file).clone(); - adapted.name = FileName::Real(match realname { - RealFileName::LocalPath(path_to_file) => { - // Prepend path of working directory onto potentially - // relative paths, because they could become relative - // to a wrong directory. - // We include `working_dir` as part of the crate hash, - // so it's okay for us to use it as part of the encoded - // metadata. - let working_dir = &self.tcx.sess.opts.working_dir; - match working_dir { - RealFileName::LocalPath(absolute) => { - // Although neither working_dir or the file name were subject - // to path remapping, the concatenation between the two may - // be. Hence we need to do a remapping here. - let joined = Path::new(absolute).join(path_to_file); - let (joined, remapped) = - source_map.path_mapping().map_prefix(joined); - if remapped { - RealFileName::Remapped { - local_path: None, - virtual_name: joined, - } - } else { - RealFileName::LocalPath(joined) - } - } - RealFileName::Remapped { local_path: _, virtual_name } => { - // If working_dir has been remapped, then we emit - // Remapped variant as the expanded path won't be valid - RealFileName::Remapped { - local_path: None, - virtual_name: Path::new(virtual_name) - .join(path_to_file), - } - } - } - } - RealFileName::Remapped { local_path: _, virtual_name } => { - RealFileName::Remapped { - // We do not want any local path to be exported into metadata - local_path: None, - virtual_name: virtual_name.clone(), - } - } - }); - adapted.name_hash = { - let mut hasher: StableHasher = StableHasher::new(); - adapted.name.hash(&mut hasher); - hasher.finish::<u128>() - }; - Lrc::new(adapted) + // At export time we expand all source file paths to absolute paths because + // downstream compilation sessions can have a different compiler working + // directory, so relative paths from this or any other upstream crate + // won't be valid anymore. + // + // At this point we also erase the actual on-disk path and only keep + // the remapped version -- as is necessary for reproducible builds. + match source_file.name { + FileName::Real(ref original_file_name) => { + let adapted_file_name = + source_map.path_mapping().to_embeddable_absolute_path( + original_file_name.clone(), + working_directory, + ); + + if adapted_file_name != *original_file_name { + let mut adapted: SourceFile = (**source_file).clone(); + adapted.name = FileName::Real(adapted_file_name); + adapted.name_hash = { + let mut hasher: StableHasher = StableHasher::new(); + adapted.name.hash(&mut hasher); + hasher.finish::<u128>() + }; + Lrc::new(adapted) + } else { + // Nothing to adapt + source_file.clone() + } } - // expanded code, not from a file _ => source_file.clone(), - }; - + } + }) + .map(|mut source_file| { // We're serializing this `SourceFile` into our crate metadata, // so mark it as coming from this crate. // This also ensures that we don't try to deserialize the @@ -570,9 +528,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // dependencies aren't loaded when we deserialize a proc-macro, // trying to remap the `CrateNum` would fail. if self.is_proc_macro { - Lrc::make_mut(&mut adapted).cnum = LOCAL_CRATE; + Lrc::make_mut(&mut source_file).cnum = LOCAL_CRATE; } - adapted + source_file }) .collect::<Vec<_>>(); @@ -985,11 +943,17 @@ fn should_encode_generics(def_kind: DefKind) -> bool { } impl<'a, 'tcx> EncodeContext<'a, 'tcx> { - fn encode_attrs(&mut self, def_id: DefId) { - let attrs = self.tcx.get_attrs(def_id); - record!(self.tables.attributes[def_id] <- attrs); - if attrs.iter().any(|attr| attr.may_have_doc_links()) { - self.tables.may_have_doc_links.set(def_id.index, ()); + fn encode_attrs(&mut self, def_id: LocalDefId) { + let mut attrs = self + .tcx + .hir() + .attrs(self.tcx.hir().local_def_id_to_hir_id(def_id)) + .iter() + .filter(|attr| !rustc_feature::is_builtin_only_local(attr.name_or_empty())); + + record!(self.tables.attributes[def_id.to_def_id()] <- attrs.clone()); + if attrs.any(|attr| attr.may_have_doc_links()) { + self.tables.may_have_doc_links.set(def_id.local_def_index, ()); } } @@ -1005,8 +969,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let Some(def_kind) = def_kind else { continue }; self.tables.opt_def_kind.set(def_id.index, def_kind); record!(self.tables.def_span[def_id] <- tcx.def_span(def_id)); - self.encode_attrs(def_id); + self.encode_attrs(local_id); record!(self.tables.expn_that_defined[def_id] <- self.tcx.expn_that_defined(def_id)); + if def_kind.has_codegen_attrs() { + record!(self.tables.codegen_fn_attrs[def_id] <- self.tcx.codegen_fn_attrs(def_id)); + } if should_encode_visibility(def_kind) { record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id)); } @@ -1310,6 +1277,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } if impl_item.kind == ty::AssocKind::Fn { record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); + if tcx.is_intrinsic(def_id) { + self.tables.is_intrinsic.set(def_id.index, ()); + } } } @@ -1554,6 +1524,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } if let hir::ItemKind::Fn(..) = item.kind { record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); + if tcx.is_intrinsic(def_id) { + self.tables.is_intrinsic.set(def_id.index, ()); + } } if let hir::ItemKind::Impl { .. } = item.kind { if let Some(trait_ref) = self.tcx.impl_trait_ref(def_id) { @@ -1667,7 +1640,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.tables.opt_def_kind.set(LOCAL_CRATE.as_def_id().index, DefKind::Mod); record!(self.tables.def_span[LOCAL_CRATE.as_def_id()] <- tcx.def_span(LOCAL_CRATE.as_def_id())); - self.encode_attrs(LOCAL_CRATE.as_def_id()); + self.encode_attrs(LOCAL_CRATE.as_def_id().expect_local()); record!(self.tables.visibility[LOCAL_CRATE.as_def_id()] <- tcx.visibility(LOCAL_CRATE.as_def_id())); if let Some(stability) = stability { record!(self.tables.lookup_stability[LOCAL_CRATE.as_def_id()] <- stability); @@ -1708,7 +1681,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let def_id = id.to_def_id(); self.tables.opt_def_kind.set(def_id.index, DefKind::Macro(macro_kind)); record!(self.tables.kind[def_id] <- EntryKind::ProcMacro(macro_kind)); - self.encode_attrs(def_id); + self.encode_attrs(id); record!(self.tables.def_keys[def_id] <- def_key); record!(self.tables.def_ident_span[def_id] <- span); record!(self.tables.def_span[def_id] <- span); @@ -1813,12 +1786,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { FxHashMap::default(); for id in tcx.hir().items() { - if matches!(tcx.hir().def_kind(id.def_id), DefKind::Impl) { + if matches!(tcx.def_kind(id.def_id), DefKind::Impl) { if let Some(trait_ref) = tcx.impl_trait_ref(id.def_id.to_def_id()) { let simplified_self_ty = fast_reject::simplify_type( self.tcx, trait_ref.self_ty(), - TreatParams::AsPlaceholders, + TreatParams::AsInfer, ); fx_hash_map @@ -1950,6 +1923,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.encode_item_type(def_id); if let hir::ForeignItemKind::Fn(..) = nitem.kind { record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); + if tcx.is_intrinsic(def_id) { + self.tables.is_intrinsic.set(def_id.index, ()); + } } } } @@ -2234,26 +2210,16 @@ pub fn provide(providers: &mut Providers) { traits_in_crate: |tcx, cnum| { assert_eq!(cnum, LOCAL_CRATE); - #[derive(Default)] - struct TraitsVisitor { - traits: Vec<DefId>, - } - impl ItemLikeVisitor<'_> for TraitsVisitor { - fn visit_item(&mut self, item: &hir::Item<'_>) { - if let hir::ItemKind::Trait(..) | hir::ItemKind::TraitAlias(..) = item.kind { - self.traits.push(item.def_id.to_def_id()); - } + let mut traits = Vec::new(); + for id in tcx.hir().items() { + if matches!(tcx.def_kind(id.def_id), DefKind::Trait | DefKind::TraitAlias) { + traits.push(id.def_id.to_def_id()) } - fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'_>) {} - fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'_>) {} - fn visit_foreign_item(&mut self, _foreign_item: &hir::ForeignItem<'_>) {} } - let mut visitor = TraitsVisitor::default(); - tcx.hir().visit_all_item_likes(&mut visitor); // Bring everything into deterministic order. - visitor.traits.sort_by_cached_key(|&def_id| tcx.def_path_hash(def_id)); - tcx.arena.alloc_slice(&visitor.traits) + traits.sort_by_cached_key(|&def_id| tcx.def_path_hash(def_id)); + tcx.arena.alloc_slice(&traits) }, ..*providers diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index a0fd9ef4f87..7ae177c3a56 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -14,6 +14,7 @@ use rustc_hir::definitions::DefKey; use rustc_hir::lang_items; use rustc_index::{bit_set::FiniteBitSet, vec::IndexVec}; use rustc_middle::metadata::ModChild; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; use rustc_middle::mir; use rustc_middle::thir; @@ -35,7 +36,7 @@ use std::num::NonZeroUsize; pub use decoder::provide_extern; use decoder::DecodeContext; -crate use decoder::{CrateMetadata, CrateNumMap, MetadataBlob}; +pub(crate) use decoder::{CrateMetadata, CrateNumMap, MetadataBlob}; use encoder::EncodeContext; pub use encoder::{encode_metadata, EncodedMetadata}; use rustc_span::hygiene::SyntaxContextData; @@ -45,7 +46,7 @@ mod def_path_hash_map; mod encoder; mod table; -crate fn rustc_version() -> String { +pub(crate) fn rustc_version() -> String { format!("rustc {}", option_env!("CFG_VERSION").unwrap_or("unknown version")) } @@ -168,7 +169,7 @@ type ExpnDataTable = Lazy<Table<ExpnIndex, Lazy<ExpnData>>>; type ExpnHashTable = Lazy<Table<ExpnIndex, Lazy<ExpnHash>>>; #[derive(MetadataEncodable, MetadataDecodable)] -crate struct ProcMacroData { +pub(crate) struct ProcMacroData { proc_macro_decls_static: DefIndex, stability: Option<attr::Stability>, macros: Lazy<[DefIndex]>, @@ -191,7 +192,7 @@ crate struct ProcMacroData { /// a normal crate, much of what we serialized would be unusable in addition /// to being unused. #[derive(MetadataEncodable, MetadataDecodable)] -crate struct CrateRoot<'tcx> { +pub(crate) struct CrateRoot<'tcx> { name: Symbol, triple: TargetTriple, extra_filename: String, @@ -244,7 +245,7 @@ crate struct CrateRoot<'tcx> { /// This creates a type-safe way to enforce that we remap the CrateNum between the on-disk /// representation and the compilation session. #[derive(Copy, Clone)] -crate struct RawDefId { +pub(crate) struct RawDefId { krate: u32, index: u32, } @@ -264,7 +265,7 @@ impl RawDefId { } #[derive(Encodable, Decodable)] -crate struct CrateDep { +pub(crate) struct CrateDep { pub name: Symbol, pub hash: Svh, pub host_hash: Option<Svh>, @@ -273,13 +274,13 @@ crate struct CrateDep { } #[derive(MetadataEncodable, MetadataDecodable)] -crate struct TraitImpls { +pub(crate) struct TraitImpls { trait_id: (u32, DefIndex), impls: Lazy<[(DefIndex, Option<SimplifiedType>)]>, } #[derive(MetadataEncodable, MetadataDecodable)] -crate struct IncoherentImpls { +pub(crate) struct IncoherentImpls { self_ty: SimplifiedType, impls: Lazy<[DefIndex]>, } @@ -288,7 +289,7 @@ crate struct IncoherentImpls { macro_rules! define_tables { ($($name:ident: Table<$IDX:ty, $T:ty>),+ $(,)?) => { #[derive(MetadataEncodable, MetadataDecodable)] - crate struct LazyTables<'tcx> { + pub(crate) struct LazyTables<'tcx> { $($name: Lazy!(Table<$IDX, $T>)),+ } @@ -329,6 +330,7 @@ define_tables! { type_of: Table<DefIndex, Lazy!(Ty<'tcx>)>, variances_of: Table<DefIndex, Lazy<[ty::Variance]>>, fn_sig: Table<DefIndex, Lazy!(ty::PolyFnSig<'tcx>)>, + codegen_fn_attrs: Table<DefIndex, Lazy!(CodegenFnAttrs)>, impl_trait_ref: Table<DefIndex, Lazy!(ty::TraitRef<'tcx>)>, const_param_default: Table<DefIndex, Lazy<rustc_middle::ty::Const<'tcx>>>, optimized_mir: Table<DefIndex, Lazy!(mir::Body<'tcx>)>, @@ -338,6 +340,7 @@ define_tables! { impl_parent: Table<DefIndex, RawDefId>, impl_polarity: Table<DefIndex, ty::ImplPolarity>, impl_constness: Table<DefIndex, hir::Constness>, + is_intrinsic: Table<DefIndex, ()>, impl_defaultness: Table<DefIndex, hir::Defaultness>, // FIXME(eddyb) perhaps compute this on the fly if cheap enough? coerce_unsized_info: Table<DefIndex, Lazy!(ty::adjustment::CoerceUnsizedInfo)>, diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index 7c90cbb9092..2f0bb24537e 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -33,7 +33,6 @@ macro_rules! arena_types { [] const_allocs: rustc_middle::mir::interpret::Allocation, // Required for the incremental on-disk cache [] mir_keys: rustc_hir::def_id::DefIdSet, - [] region_scope_tree: rustc_middle::middle::region::ScopeTree, [] dropck_outlives: rustc_middle::infer::canonical::Canonical<'tcx, rustc_middle::infer::canonical::QueryResponse<'tcx, @@ -82,7 +81,7 @@ macro_rules! arena_types { [] upvars_mentioned: rustc_data_structures::fx::FxIndexMap<rustc_hir::HirId, rustc_hir::Upvar>, [] object_safety_violations: rustc_middle::traits::ObjectSafetyViolation, [] codegen_unit: rustc_middle::mir::mono::CodegenUnit<'tcx>, - [] attribute: rustc_ast::Attribute, + [decode] attribute: rustc_ast::Attribute, [] name_set: rustc_data_structures::fx::FxHashSet<rustc_span::symbol::Symbol>, [] hir_id_set: rustc_hir::HirIdSet, @@ -95,9 +94,6 @@ macro_rules! arena_types { // since we need to allocate this type on both the `rustc_hir` arena // (during lowering) and the `librustc_middle` arena (for decoding MIR) [decode] asm_template: rustc_ast::InlineAsmTemplatePiece, - - // This is used to decode the &'tcx [Span] for InlineAsm's line_spans. - [decode] span: rustc_span::Span, [decode] used_trait_imports: rustc_data_structures::fx::FxHashSet<rustc_hir::def_id::LocalDefId>, [decode] impl_source: rustc_middle::traits::ImplSource<'tcx, ()>, diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index 8402ca3028c..555baae35f5 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -195,13 +195,16 @@ rustc_dep_node_append!([define_dep_nodes!][ <'tcx> // WARNING: `construct` is generic and does not know that `CompileCodegenUnit` takes `Symbol`s as keys. // Be very careful changing this type signature! -crate fn make_compile_codegen_unit(tcx: TyCtxt<'_>, name: Symbol) -> DepNode { +pub(crate) fn make_compile_codegen_unit(tcx: TyCtxt<'_>, name: Symbol) -> DepNode { DepNode::construct(tcx, DepKind::CompileCodegenUnit, &name) } // WARNING: `construct` is generic and does not know that `CompileMonoItem` takes `MonoItem`s as keys. // Be very careful changing this type signature! -crate fn make_compile_mono_item<'tcx>(tcx: TyCtxt<'tcx>, mono_item: &MonoItem<'tcx>) -> DepNode { +pub(crate) fn make_compile_mono_item<'tcx>( + tcx: TyCtxt<'tcx>, + mono_item: &MonoItem<'tcx>, +) -> DepNode { DepNode::construct(tcx, DepKind::CompileMonoItem, mono_item) } diff --git a/compiler/rustc_middle/src/dep_graph/mod.rs b/compiler/rustc_middle/src/dep_graph/mod.rs index 6bfd1b7ffab..e335cb395f8 100644 --- a/compiler/rustc_middle/src/dep_graph/mod.rs +++ b/compiler/rustc_middle/src/dep_graph/mod.rs @@ -12,7 +12,7 @@ pub use rustc_query_system::dep_graph::{ }; pub use dep_node::{label_strs, DepKind, DepKindStruct, DepNode, DepNodeExt}; -crate use dep_node::{make_compile_codegen_unit, make_compile_mono_item}; +pub(crate) use dep_node::{make_compile_codegen_unit, make_compile_mono_item}; pub type DepGraph = rustc_query_system::dep_graph::DepGraph<DepKind>; pub type TaskDeps = rustc_query_system::dep_graph::TaskDeps<DepKind>; diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 85e53559c29..9976b0e9862 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -9,7 +9,6 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::*; use rustc_index::vec::Idx; use rustc_middle::hir::nested_filter; @@ -161,6 +160,10 @@ impl<'hir> Map<'hir> { self.tcx.hir_crate_items(()).items.iter().copied() } + pub fn module_items(self, module: LocalDefId) -> impl Iterator<Item = ItemId> + 'hir { + self.tcx.hir_module_items(module).items() + } + pub fn par_for_each_item(self, f: impl Fn(ItemId) + Sync + Send) { par_for_each_in(&self.tcx.hir_crate_items(()).items[..], |id| f(*id)); } @@ -308,11 +311,6 @@ impl<'hir> Map<'hir> { Some(def_kind) } - pub fn def_kind(self, local_def_id: LocalDefId) -> DefKind { - self.opt_def_kind(local_def_id) - .unwrap_or_else(|| bug!("def_kind: unsupported node: {:?}", local_def_id)) - } - pub fn find_parent_node(self, id: HirId) -> Option<HirId> { if id.local_id == ItemLocalId::from_u32(0) { Some(self.tcx.hir_owner_parent(id.owner)) @@ -608,16 +606,16 @@ impl<'hir> Map<'hir> { } /// Visits all items in the crate in some deterministic (but - /// unspecified) order. If you just need to process every item, - /// but don't care about nesting, this method is the best choice. + /// unspecified) order. If you need to process every item, + /// and care about nesting -- usually because your algorithm + /// follows lexical scoping rules -- then this method is the best choice. + /// If you don't care about nesting, you should use the `tcx.hir_crate_items()` query + /// or `items()` instead. /// - /// If you do care about nesting -- usually because your algorithm - /// follows lexical scoping rules -- then you want a different - /// approach. You should override `visit_nested_item` in your - /// visitor and then call `intravisit::walk_crate` instead. - pub fn visit_all_item_likes<V>(self, visitor: &mut V) + /// Please see the notes in `intravisit.rs` for more information. + pub fn deep_visit_all_item_likes<V>(self, visitor: &mut V) where - V: itemlikevisit::ItemLikeVisitor<'hir>, + V: Visitor<'hir>, { let krate = self.krate(); for owner in krate.owners.iter().filter_map(|i| i.as_owner()) { @@ -648,9 +646,12 @@ impl<'hir> Map<'hir> { }) } - pub fn visit_item_likes_in_module<V>(self, module: LocalDefId, visitor: &mut V) + /// If you don't care about nesting, you should use the + /// `tcx.hir_module_items()` query or `module_items()` instead. + /// Please see notes in `deep_visit_all_item_likes`. + pub fn deep_visit_item_likes_in_module<V>(self, module: LocalDefId, visitor: &mut V) where - V: ItemLikeVisitor<'hir>, + V: Visitor<'hir>, { let module = self.tcx.hir_module_items(module); @@ -671,7 +672,7 @@ impl<'hir> Map<'hir> { } } - pub fn for_each_module(self, f: impl Fn(LocalDefId)) { + pub fn for_each_module(self, mut f: impl FnMut(LocalDefId)) { let crate_items = self.tcx.hir_crate_items(()); for module in crate_items.submodules.iter() { f(*module) @@ -742,7 +743,7 @@ impl<'hir> Map<'hir> { /// } /// ``` /// - /// ``` + /// ```compile_fail,E0308 /// fn foo(x: usize) -> bool { /// loop { /// true // If `get_return_block` gets passed the `id` corresponding diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index f18067145dd..b50f121eff2 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -36,7 +36,7 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for Owner<'tcx> { /// Gather the LocalDefId for each item-like within a module, including items contained within /// bodies. The Ids are in visitor order. This is used to partition a pass between modules. -#[derive(Debug, HashStable)] +#[derive(Debug, HashStable, Encodable, Decodable)] pub struct ModuleItems { submodules: Box<[LocalDefId]>, items: Box<[ItemId]>, diff --git a/compiler/rustc_middle/src/hir/nested_filter.rs b/compiler/rustc_middle/src/hir/nested_filter.rs index 48efae8045b..d56e87bbb47 100644 --- a/compiler/rustc_middle/src/hir/nested_filter.rs +++ b/compiler/rustc_middle/src/hir/nested_filter.rs @@ -8,7 +8,7 @@ use rustc_hir::intravisit::nested_filter::NestedFilter; /// constant arguments of types, e.g. in `let _: [(); /* HERE */];`. /// /// **This is the most common choice.** A very common pattern is -/// to use `visit_all_item_likes()` as an outer loop, +/// to use `deep_visit_all_item_likes()` as an outer loop, /// and to have the visitor that visits the contents of each item /// using this setting. pub struct OnlyBodies(()); diff --git a/compiler/rustc_middle/src/infer/mod.rs b/compiler/rustc_middle/src/infer/mod.rs index 497d3811f28..2350a6ab155 100644 --- a/compiler/rustc_middle/src/infer/mod.rs +++ b/compiler/rustc_middle/src/infer/mod.rs @@ -10,7 +10,7 @@ use rustc_span::Span; /// Requires that `region` must be equal to one of the regions in `choice_regions`. /// We often denote this using the syntax: /// -/// ``` +/// ```text /// R0 member of [O1..On] /// ``` #[derive(Debug, Clone, HashStable, TypeFoldable, Lift)] diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 04529ed1161..b4dd253b839 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -35,6 +35,7 @@ #![feature(get_mut_unchecked)] #![feature(if_let_guard)] #![feature(map_first_last)] +#![feature(negative_impls)] #![feature(never_type)] #![feature(extern_types)] #![feature(new_uninit)] @@ -45,7 +46,6 @@ #![feature(min_specialization)] #![feature(trusted_len)] #![feature(type_alias_impl_trait)] -#![feature(crate_visibility_modifier)] #![feature(associated_type_bounds)] #![feature(rustc_attrs)] #![feature(half_open_range_patterns)] diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index e55b0454eef..c7c5f56867a 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -210,6 +210,10 @@ pub struct LintExpectation { /// adjusted to include an additional note. Therefore, we have to track if /// the expectation is for the lint. pub is_unfulfilled_lint_expectations: bool, + /// This will hold the name of the tool that this lint belongs to. For + /// the lint `clippy::some_lint` the tool would be `clippy`, the same + /// goes for `rustdoc`. This will be `None` for rustc lints + pub lint_tool: Option<Symbol>, } impl LintExpectation { @@ -217,8 +221,9 @@ impl LintExpectation { reason: Option<Symbol>, emission_span: Span, is_unfulfilled_lint_expectations: bool, + lint_tool: Option<Symbol>, ) -> Self { - Self { reason, emission_span, is_unfulfilled_lint_expectations } + Self { reason, emission_span, is_unfulfilled_lint_expectations, lint_tool } } } diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 54eb2dc9e28..321fcd43797 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -95,7 +95,9 @@ bitflags! { } impl CodegenFnAttrs { - pub fn new() -> CodegenFnAttrs { + pub const EMPTY: &'static Self = &Self::new(); + + pub const fn new() -> CodegenFnAttrs { CodegenFnAttrs { flags: CodegenFnAttrFlags::empty(), inline: InlineAttr::None, diff --git a/compiler/rustc_middle/src/middle/region.rs b/compiler/rustc_middle/src/middle/region.rs index af16e5e3fc8..30ef6b775f5 100644 --- a/compiler/rustc_middle/src/middle/region.rs +++ b/compiler/rustc_middle/src/middle/region.rs @@ -203,7 +203,7 @@ impl Scope { pub type ScopeDepth = u32; /// The region scope tree encodes information about region relationships. -#[derive(Default, Debug)] +#[derive(TyEncodable, TyDecodable, Default, Debug)] pub struct ScopeTree { /// If not empty, this body is the root of this region hierarchy. pub root_body: Option<hir::HirId>, @@ -223,15 +223,12 @@ pub struct ScopeTree { /// Maps from a `NodeId` to the associated destruction scope (if any). destruction_scopes: FxIndexMap<hir::ItemLocalId, Scope>, - /// `rvalue_scopes` includes entries for those expressions whose - /// cleanup scope is larger than the default. The map goes from the - /// expression ID to the cleanup scope id. For rvalues not present in - /// this table, the appropriate cleanup scope is the innermost - /// enclosing statement, conditional expression, or repeating - /// block (see `terminating_scopes`). - /// In constants, None is used to indicate that certain expressions - /// escape into 'static and should have no local cleanup scope. - rvalue_scopes: FxHashMap<hir::ItemLocalId, Option<Scope>>, + /// Identifies expressions which, if captured into a temporary, ought to + /// have a temporary whose lifetime extends to the end of the enclosing *block*, + /// and not the enclosing *statement*. Expressions that are not present in this + /// table are not rvalue candidates. The set of rvalue candidates is computed + /// during type check based on a traversal of the AST. + pub rvalue_candidates: FxHashMap<hir::HirId, RvalueCandidateType>, /// If there are any `yield` nested within a scope, this map /// stores the `Span` of the last one and its index in the @@ -315,6 +312,17 @@ pub struct ScopeTree { pub body_expr_count: FxHashMap<hir::BodyId, usize>, } +/// Identifies the reason that a given expression is an rvalue candidate +/// (see the `rvalue_candidates` field for more information what rvalue +/// candidates in general). In constants, the `lifetime` field is None +/// to indicate that certain expressions escape into 'static and +/// should have no local cleanup scope. +#[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)] +pub enum RvalueCandidateType { + Borrow { target: hir::ItemLocalId, lifetime: Option<Scope> }, + Pattern { target: hir::ItemLocalId, lifetime: Option<Scope> }, +} + #[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)] pub struct YieldData { /// The `Span` of the yield. @@ -349,12 +357,20 @@ impl ScopeTree { self.var_map.insert(var, lifetime); } - pub fn record_rvalue_scope(&mut self, var: hir::ItemLocalId, lifetime: Option<Scope>) { - debug!("record_rvalue_scope(sub={:?}, sup={:?})", var, lifetime); - if let Some(lifetime) = lifetime { - assert!(var != lifetime.item_local_id()); + pub fn record_rvalue_candidate( + &mut self, + var: hir::HirId, + candidate_type: RvalueCandidateType, + ) { + debug!("record_rvalue_candidate(var={var:?}, type={candidate_type:?})"); + match &candidate_type { + RvalueCandidateType::Borrow { lifetime: Some(lifetime), .. } + | RvalueCandidateType::Pattern { lifetime: Some(lifetime), .. } => { + assert!(var.local_id != lifetime.item_local_id()) + } + _ => {} } - self.rvalue_scopes.insert(var, lifetime); + self.rvalue_candidates.insert(var, candidate_type); } /// Returns the narrowest scope that encloses `id`, if any. @@ -367,34 +383,6 @@ impl ScopeTree { self.var_map.get(&var_id).cloned() } - /// Returns the scope when the temp created by `expr_id` will be cleaned up. - pub fn temporary_scope(&self, expr_id: hir::ItemLocalId) -> Option<Scope> { - // Check for a designated rvalue scope. - if let Some(&s) = self.rvalue_scopes.get(&expr_id) { - debug!("temporary_scope({:?}) = {:?} [custom]", expr_id, s); - return s; - } - - // Otherwise, locate the innermost terminating scope - // if there's one. Static items, for instance, won't - // have an enclosing scope, hence no scope will be - // returned. - let mut id = Scope { id: expr_id, data: ScopeData::Node }; - - while let Some(&(p, _)) = self.parent_map.get(&id) { - match p.data { - ScopeData::Destruction => { - debug!("temporary_scope({:?}) = {:?} [enclosing]", expr_id, id); - return Some(id); - } - _ => id = p, - } - } - - debug!("temporary_scope({:?}) = None", expr_id); - None - } - /// Returns `true` if `subscope` is equal to or is lexically nested inside `superscope`, and /// `false` otherwise. /// @@ -439,7 +427,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for ScopeTree { ref parent_map, ref var_map, ref destruction_scopes, - ref rvalue_scopes, + ref rvalue_candidates, ref yield_in_scope, } = *self; @@ -448,7 +436,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for ScopeTree { parent_map.hash_stable(hcx, hasher); var_map.hash_stable(hcx, hasher); destruction_scopes.hash_stable(hcx, hasher); - rvalue_scopes.hash_stable(hcx, hasher); + rvalue_candidates.hash_stable(hcx, hasher); yield_in_scope.hash_stable(hcx, hasher); } } diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 32041143240..8b11e35a7c3 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -29,7 +29,7 @@ pub enum StabilityLevel { } /// An entry in the `depr_map`. -#[derive(Copy, Clone, HashStable, Debug)] +#[derive(Copy, Clone, HashStable, Debug, Encodable, Decodable)] pub struct DeprecationEntry { /// The metadata of the attribute associated with this entry. pub attr: Deprecation, @@ -118,8 +118,7 @@ pub fn deprecation_in_effect(depr: &Deprecation) -> bool { } if !is_since_rustc_version { - // The `since` field doesn't have semantic purpose in the stable `deprecated` - // attribute, only in `rustc_deprecated`. + // The `since` field doesn't have semantic purpose without `#![staged_api]`. return true; } @@ -336,7 +335,7 @@ impl<'tcx> TyCtxt<'tcx> { // topmost deprecation. For example, if a struct is deprecated, // the use of a field won't be linted. // - // #[rustc_deprecated] however wants to emit down the whole + // With #![staged_api], we want to emit down the whole // hierarchy. let depr_attr = &depr_entry.attr; if !skip || depr_attr.is_since_rustc_version { diff --git a/compiler/rustc_middle/src/mir/generic_graph.rs b/compiler/rustc_middle/src/mir/generic_graph.rs index dbebed67c2b..a4d78911b27 100644 --- a/compiler/rustc_middle/src/mir/generic_graph.rs +++ b/compiler/rustc_middle/src/mir/generic_graph.rs @@ -24,7 +24,7 @@ pub fn mir_fn_to_generic_graph<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Grap let terminator = body[source].terminator(); let labels = terminator.kind.fmt_successor_labels(); - for (&target, label) in terminator.successors().zip(labels) { + for (target, label) in terminator.successors().zip(labels) { let src = node(def_id, source); let trg = node(def_id, target); edges.push(Edge::new(src, trg, label.to_string())); diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 8cfc5ed0a95..760fe1edbdb 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -350,19 +350,22 @@ impl<Tag: Provenance, Extra> Allocation<Tag, Extra> { /// Reading and writing. impl<Tag: Provenance, Extra> Allocation<Tag, Extra> { /// Validates that `ptr.offset` and `ptr.offset + size` do not point to the middle of a - /// relocation. If `allow_uninit_and_ptr` is `false`, also enforces that the memory in the - /// given range contains neither relocations nor uninitialized bytes. + /// relocation. If `allow_uninit`/`allow_ptr` is `false`, also enforces that the memory in the + /// given range contains no uninitialized bytes/relocations. pub fn check_bytes( &self, cx: &impl HasDataLayout, range: AllocRange, - allow_uninit_and_ptr: bool, + allow_uninit: bool, + allow_ptr: bool, ) -> AllocResult { // Check bounds and relocations on the edges. self.get_bytes_with_uninit_and_ptr(cx, range)?; // Check uninit and ptr. - if !allow_uninit_and_ptr { + if !allow_uninit { self.check_init(range)?; + } + if !allow_ptr { self.check_relocations(cx, range)?; } Ok(()) @@ -770,11 +773,11 @@ impl InitMask { /// /// Note that all examples below are written with 8 (instead of 64) bit blocks for simplicity, /// and with the least significant bit (and lowest block) first: - /// - /// 00000000|00000000 - /// ^ ^ ^ ^ - /// index: 0 7 8 15 - /// + /// ```text + /// 00000000|00000000 + /// ^ ^ ^ ^ + /// index: 0 7 8 15 + /// ``` /// Also, if not stated, assume that `is_init = true`, that is, we are searching for the first 1 bit. fn find_bit_fast( init_mask: &InitMask, diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 9afe9523fca..bb6b10149ab 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -1,7 +1,7 @@ use super::{AllocId, ConstAlloc, Pointer, Scalar}; use crate::mir::interpret::ConstValue; -use crate::ty::{layout, query::TyCtxtAt, tls, FnSig, Ty}; +use crate::ty::{layout, query::TyCtxtAt, tls, FnSig, Ty, ValTree}; use rustc_data_structures::sync::Lock; use rustc_errors::{pluralize, struct_span_err, DiagnosticBuilder, ErrorGuaranteed}; @@ -35,6 +35,7 @@ TrivialTypeFoldableAndLiftImpls! { pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>; pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>; +pub type EvalToValTreeResult<'tcx> = Result<Option<ValTree<'tcx>>, ErrorHandled>; pub fn struct_error<'tcx>( tcx: TyCtxtAt<'tcx>, diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index d8cba39c6d9..16ef8d68be3 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -119,9 +119,9 @@ use crate::ty::{self, Instance, Ty, TyCtxt}; pub use self::error::{ struct_error, CheckInAllocMsg, ErrorHandled, EvalToAllocationRawResult, EvalToConstValueResult, - InterpError, InterpErrorInfo, InterpResult, InvalidProgramInfo, MachineStopType, - ResourceExhaustionInfo, ScalarSizeMismatch, UndefinedBehaviorInfo, UninitBytesAccess, - UnsupportedOpInfo, + EvalToValTreeResult, InterpError, InterpErrorInfo, InterpResult, InvalidProgramInfo, + MachineStopType, ResourceExhaustionInfo, ScalarSizeMismatch, UndefinedBehaviorInfo, + UninitBytesAccess, UnsupportedOpInfo, }; pub use self::value::{get_slice_bytes, ConstAlloc, ConstValue, Scalar, ScalarMaybeUninit}; @@ -414,7 +414,7 @@ impl<'tcx> GlobalAlloc<'tcx> { } } -crate struct AllocMap<'tcx> { +pub(crate) struct AllocMap<'tcx> { /// Maps `AllocId`s to their corresponding allocations. alloc_map: FxHashMap<AllocId, GlobalAlloc<'tcx>>, @@ -430,7 +430,7 @@ crate struct AllocMap<'tcx> { } impl<'tcx> AllocMap<'tcx> { - crate fn new() -> Self { + pub(crate) fn new() -> Self { AllocMap { alloc_map: Default::default(), dedup: Default::default(), diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs index c71aea417ec..26da93b9dce 100644 --- a/compiler/rustc_middle/src/mir/interpret/pointer.rs +++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs @@ -120,9 +120,11 @@ pub trait Provenance: Copy + fmt::Debug { where Self: Sized; - /// Provenance must always be able to identify the allocation this ptr points to. + /// If `OFFSET_IS_ADDR == false`, provenance must always be able to + /// identify the allocation this ptr points to (i.e., this must return `Some`). + /// Otherwise this function is best-effort (but must agree with `Machine::ptr_get_alloc`). /// (Identifying the offset in that allocation, however, is harder -- use `Memory::ptr_get_alloc` for that.) - fn get_alloc_id(self) -> AllocId; + fn get_alloc_id(self) -> Option<AllocId>; } impl Provenance for AllocId { @@ -147,8 +149,8 @@ impl Provenance for AllocId { Ok(()) } - fn get_alloc_id(self) -> AllocId { - self + fn get_alloc_id(self) -> Option<AllocId> { + Some(self) } } diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 7e5989b4112..5fb8e911124 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -110,11 +110,22 @@ impl<'tcx> TyCtxt<'tcx> { Ok(self.global_alloc(raw_const.alloc_id).unwrap_memory()) } - /// Destructure a constant ADT or array into its variant index and its field values. + /// Destructure a type-level constant ADT or array into its variant index and its field values. + /// Panics if the destructuring fails, use `try_destructure_const` for fallible version. pub fn destructure_const( self, param_env_and_val: ty::ParamEnvAnd<'tcx, ty::Const<'tcx>>, ) -> mir::DestructuredConst<'tcx> { self.try_destructure_const(param_env_and_val).unwrap() } + + /// Destructure a mir constant ADT or array into its variant index and its field values. + /// Panics if the destructuring fails, use `try_destructure_const` for fallible version. + pub fn destructure_mir_constant( + self, + param_env: ty::ParamEnv<'tcx>, + constant: mir::ConstantKind<'tcx>, + ) -> mir::DestructuredMirConstant<'tcx> { + self.try_destructure_mir_constant(param_env.and(constant)).unwrap() + } } diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index 9cffdf2993e..eeee170f43f 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -344,7 +344,8 @@ impl<'tcx, Tag: Provenance> Scalar<Tag> { } else { // We know `offset` is relative, since `OFFSET_IS_ADDR == false`. let (tag, offset) = ptr.into_parts(); - Err(Scalar::Ptr(Pointer::new(tag.get_alloc_id(), offset), sz)) + // Because `OFFSET_IS_ADDR == false`, this unwrap can never fail. + Err(Scalar::Ptr(Pointer::new(tag.get_alloc_id().unwrap(), offset), sz)) } } } diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 924bacb7aae..1eca22d3812 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -3,7 +3,7 @@ //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/mir/index.html use crate::mir::coverage::{CodeRegion, CoverageKind}; -use crate::mir::interpret::{ConstAllocation, ConstValue, GlobalAlloc, Scalar}; +use crate::mir::interpret::{ConstAllocation, ConstValue, GlobalAlloc, LitToConstInput, Scalar}; use crate::mir::visit::MirVisitable; use crate::ty::adjustment::PointerCast; use crate::ty::codec::{TyDecoder, TyEncoder}; @@ -189,7 +189,7 @@ pub enum MirPhase { /// /// Beginning with this phase, the following variants are disallowed: /// * [`TerminatorKind::Yield`](terminator::TerminatorKind::Yield) - /// * [`TerminatorKind::GeneratorDrop](terminator::TerminatorKind::GeneratorDrop) + /// * [`TerminatorKind::GeneratorDrop`](terminator::TerminatorKind::GeneratorDrop) GeneratorsLowered = 5, Optimized = 6, } @@ -580,6 +580,8 @@ impl<'tcx> Body<'tcx> { self.predecessor_cache.compute(&self.basic_blocks) } + /// `body.switch_sources()[&(target, switch)]` returns a list of switch + /// values that lead to a `target` block from a `switch` block. #[inline] pub fn switch_sources(&self) -> &SwitchSources { self.switch_source_cache.compute(&self.basic_blocks) @@ -737,14 +739,14 @@ pub enum BorrowKind { /// This is used when lowering matches: when matching on a place we want to /// ensure that place have the same value from the start of the match until /// an arm is selected. This prevents this code from compiling: - /// - /// let mut x = &Some(0); - /// match *x { - /// None => (), - /// Some(_) if { x = &None; false } => (), - /// Some(_) => (), - /// } - /// + /// ```compile_fail,E0510 + /// let mut x = &Some(0); + /// match *x { + /// None => (), + /// Some(_) if { x = &None; false } => (), + /// Some(_) => (), + /// } + /// ``` /// This can't be a shared borrow because mutably borrowing (*x as Some).0 /// should not prevent `if let None = x { ... }`, for example, because the /// mutating `(*x as Some).0` can't affect the discriminant of `x`. @@ -755,27 +757,30 @@ pub enum BorrowKind { /// cannot currently be expressed by the user and is used only in /// implicit closure bindings. It is needed when the closure is /// borrowing or mutating a mutable referent, e.g.: - /// - /// let x: &mut isize = ...; - /// let y = || *x += 5; - /// + /// ``` + /// let mut z = 3; + /// let x: &mut isize = &mut z; + /// let y = || *x += 5; + /// ``` /// If we were to try to translate this closure into a more explicit /// form, we'd encounter an error with the code as written: - /// - /// struct Env { x: & &mut isize } - /// let x: &mut isize = ...; - /// let y = (&mut Env { &x }, fn_ptr); // Closure is pair of env and fn - /// fn fn_ptr(env: &mut Env) { **env.x += 5; } - /// + /// ```compile_fail,E0594 + /// struct Env<'a> { x: &'a &'a mut isize } + /// let mut z = 3; + /// let x: &mut isize = &mut z; + /// let y = (&mut Env { x: &x }, fn_ptr); // Closure is pair of env and fn + /// fn fn_ptr(env: &mut Env) { **env.x += 5; } + /// ``` /// This is then illegal because you cannot mutate an `&mut` found /// in an aliasable location. To solve, you'd have to translate with /// an `&mut` borrow: - /// - /// struct Env { x: &mut &mut isize } - /// let x: &mut isize = ...; - /// let y = (&mut Env { &mut x }, fn_ptr); // changed from &x to &mut x - /// fn fn_ptr(env: &mut Env) { **env.x += 5; } - /// + /// ```compile_fail,E0596 + /// struct Env<'a> { x: &'a mut &'a mut isize } + /// let mut z = 3; + /// let x: &mut isize = &mut z; + /// let y = (&mut Env { x: &mut x }, fn_ptr); // changed from &x to &mut x + /// fn fn_ptr(env: &mut Env) { **env.x += 5; } + /// ``` /// Now the assignment to `**env.x` is legal, but creating a /// mutable pointer to `x` is not because `x` is not mutable. We /// could fix this by declaring `x` as `let mut x`. This is ok in @@ -1016,7 +1021,7 @@ pub struct LocalDecl<'tcx> { /// ``` /// fn foo(x: &str) { /// match { - /// match x.parse().unwrap() { + /// match x.parse::<u32>().unwrap() { /// y => y + 2 /// } /// } { @@ -1350,10 +1355,7 @@ pub enum InlineAsmOperand<'tcx> { /// Type for MIR `Assert` terminator error messages. pub type AssertMessage<'tcx> = AssertKind<Operand<'tcx>>; -// FIXME: Change `Successors` to `impl Iterator<Item = BasicBlock>`. -#[allow(rustc::pass_by_value)] -pub type Successors<'a> = - iter::Chain<option::IntoIter<&'a BasicBlock>, slice::Iter<'a, BasicBlock>>; +pub type Successors<'a> = impl Iterator<Item = BasicBlock> + 'a; pub type SuccessorsMut<'a> = iter::Chain<option::IntoIter<&'a mut BasicBlock>, slice::IterMut<'a, BasicBlock>>; @@ -1692,9 +1694,9 @@ pub enum StatementKind<'tcx> { /// Encodes a user's type ascription. These need to be preserved /// intact so that NLL can respect them. For example: - /// - /// let a: T = y; - /// + /// ```ignore (illustrative) + /// let a: T = y; + /// ``` /// The effect of this annotation is to relate the type `T_y` of the place `y` /// to the user-given type `T`. The effect depends on the specified variance: /// @@ -1987,7 +1989,7 @@ pub enum ProjectionElem<V, T> { /// These indices are generated by slice patterns. Easiest to explain /// by example: /// - /// ``` + /// ```ignore (illustrative) /// [X, _, .._, _, _] => { offset: 0, min_length: 4, from_end: false }, /// [_, X, .._, _, _] => { offset: 1, min_length: 4, from_end: false }, /// [_, _, .._, X, _] => { offset: 2, min_length: 4, from_end: true }, @@ -2079,12 +2081,18 @@ rustc_index::newtype_index! { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct PlaceRef<'tcx> { pub local: Local, pub projection: &'tcx [PlaceElem<'tcx>], } +// Once we stop implementing `Ord` for `DefId`, +// this impl will be unnecessary. Until then, we'll +// leave this impl in place to prevent re-adding a +// dependnecy on the `Ord` impl for `DefId` +impl<'tcx> !PartialOrd for PlaceRef<'tcx> {} + impl<'tcx> Place<'tcx> { // FIXME change this to a const fn by also making List::empty a const fn. pub fn return_place() -> Place<'tcx> { @@ -2382,7 +2390,7 @@ impl<'tcx> Operand<'tcx> { substs: SubstsRef<'tcx>, span: Span, ) -> Self { - let ty = tcx.type_of(def_id).subst(tcx, substs); + let ty = tcx.bound_type_of(def_id).subst(tcx, substs); Operand::Constant(Box::new(Constant { span, user_ty: None, @@ -3068,6 +3076,58 @@ impl<'tcx> ConstantKind<'tcx> { } #[instrument(skip(tcx), level = "debug")] + pub fn from_inline_const(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self { + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + let body_id = match tcx.hir().get(hir_id) { + hir::Node::AnonConst(ac) => ac.body, + _ => span_bug!( + tcx.def_span(def_id.to_def_id()), + "from_inline_const can only process anonymous constants" + ), + }; + let expr = &tcx.hir().body(body_id).value; + let ty = tcx.typeck(def_id).node_type(hir_id); + + let lit_input = match expr.kind { + hir::ExprKind::Lit(ref lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: false }), + hir::ExprKind::Unary(hir::UnOp::Neg, ref expr) => match expr.kind { + hir::ExprKind::Lit(ref lit) => { + Some(LitToConstInput { lit: &lit.node, ty, neg: true }) + } + _ => None, + }, + _ => None, + }; + if let Some(lit_input) = lit_input { + // If an error occurred, ignore that it's a literal and leave reporting the error up to + // mir. + match tcx.at(expr.span).lit_to_mir_constant(lit_input) { + Ok(c) => return c, + Err(_) => {} + } + } + + let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id()); + let parent_substs = + tcx.erase_regions(InternalSubsts::identity_for_item(tcx, typeck_root_def_id)); + let substs = + ty::InlineConstSubsts::new(tcx, ty::InlineConstSubstsParts { parent_substs, ty }) + .substs; + let uneval_const = tcx.mk_const(ty::ConstS { + val: ty::ConstKind::Unevaluated(ty::Unevaluated { + def: ty::WithOptConstParam::unknown(def_id).to_global(), + substs, + promoted: None, + }), + ty, + }); + debug!(?uneval_const); + debug_assert!(!uneval_const.has_free_regions()); + + Self::Ty(uneval_const) + } + + #[instrument(skip(tcx), level = "debug")] fn from_opt_const_arg_anon_const( tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalDefId>, @@ -3175,7 +3235,7 @@ impl<'tcx> ConstantKind<'tcx> { /// /// An example: /// -/// ```rust +/// ```ignore (illustrative) /// struct S<'a>((i32, &'a str), String); /// let S((_, w): (i32, &'static str), _): S = ...; /// // ------ ^^^^^^^^^^^^^^^^^^^ (1) @@ -3423,13 +3483,13 @@ impl<'tcx> graph::WithStartNode for Body<'tcx> { impl<'tcx> graph::WithSuccessors for Body<'tcx> { #[inline] fn successors(&self, node: Self::Node) -> <Self as GraphSuccessors<'_>>::Iter { - self.basic_blocks[node].terminator().successors().cloned() + self.basic_blocks[node].terminator().successors() } } impl<'a, 'b> graph::GraphSuccessors<'b> for Body<'a> { type Item = BasicBlock; - type Iter = iter::Cloned<Successors<'b>>; + type Iter = Successors<'b>; } impl<'tcx, 'graph> graph::GraphPredecessors<'graph> for Body<'tcx> { diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index f76217d921d..d389fa8c0eb 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -438,7 +438,7 @@ impl<'tcx> CodegenUnitNameBuilder<'tcx> { /// /// This function will build CGU names of the form: /// - /// ``` + /// ```text /// <crate-name>.<crate-disambiguator>[-in-<local-crate-id>](-<component>)*[.<special-suffix>] /// <local-crate-id> = <local-crate-name>.<local-crate-disambiguator> /// ``` diff --git a/compiler/rustc_middle/src/mir/patch.rs b/compiler/rustc_middle/src/mir/patch.rs index d03f9235efd..3bcb8f9c34c 100644 --- a/compiler/rustc_middle/src/mir/patch.rs +++ b/compiler/rustc_middle/src/mir/patch.rs @@ -166,9 +166,7 @@ impl<'tcx> MirPatch<'tcx> { // get terminator's targets and apply the statement to all of them. if loc.statement_index > body[loc.block].statements.len() { let term = body[loc.block].terminator(); - let successors = term.successors().clone(); - - for i in successors { + for i in term.successors() { stmts_and_targets .push((Statement { source_info, kind: stmt.clone() }, i.clone())); } diff --git a/compiler/rustc_middle/src/mir/predecessors.rs b/compiler/rustc_middle/src/mir/predecessors.rs index 4fe2cde7532..ad09328585d 100644 --- a/compiler/rustc_middle/src/mir/predecessors.rs +++ b/compiler/rustc_middle/src/mir/predecessors.rs @@ -43,7 +43,7 @@ impl PredecessorCache { let mut preds = IndexVec::from_elem(SmallVec::new(), basic_blocks); for (bb, data) in basic_blocks.iter_enumerated() { if let Some(term) = &data.terminator { - for &succ in term.successors() { + for succ in term.successors() { preds[succ].push(bb); } } diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index b7f695da544..eaa68bf1b38 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -448,6 +448,12 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { self.push(&format!("+ user_ty: {:?}", user_ty)); } + let fmt_val = |val: &ConstValue<'tcx>| match val { + ConstValue::Scalar(s) => format!("Scalar({:?})", s), + ConstValue::Slice { .. } => format!("Slice(..)"), + ConstValue::ByRef { .. } => format!("ByRef(..)"), + }; + let val = match literal { ConstantKind::Ty(ct) => match ct.val() { ty::ConstKind::Param(p) => format!("Param({})", p), @@ -457,7 +463,7 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { uv.substs, uv.promoted, ), - ty::ConstKind::Value(val) => format!("Value({:?})", val), + ty::ConstKind::Value(val) => format!("Value({})", fmt_val(&val)), ty::ConstKind::Error(_) => "Error".to_string(), // These variants shouldn't exist in the MIR. ty::ConstKind::Placeholder(_) @@ -467,7 +473,7 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { // To keep the diffs small, we render this like we render `ty::Const::Value`. // // This changes once `ty::Const::Value` is represented using valtrees. - ConstantKind::Val(val, _) => format!("Value({:?})", val), + ConstantKind::Val(val, _) => format!("Value({})", fmt_val(&val)), }; self.push(&format!("+ literal: Const {{ ty: {}, val: {} }}", literal.ty(), val)); @@ -1001,10 +1007,11 @@ fn write_user_type_annotations( for (index, annotation) in body.user_type_annotations.iter_enumerated() { writeln!( w, - "| {:?}: {:?} at {}", + "| {:?}: user_ty: {:?}, span: {}, inferred_ty: {:?}", index.index(), annotation.user_ty, - tcx.sess.source_map().span_to_embeddable_string(annotation.span) + tcx.sess.source_map().span_to_embeddable_string(annotation.span), + annotation.inferred_ty, )?; } if !body.user_type_annotations.is_empty() { diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 4d4eed179ca..7f7b8bdfc14 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -1,6 +1,6 @@ //! Values computed by queries that use MIR. -use crate::mir::{Body, Promoted}; +use crate::mir::{self, Body, Promoted}; use crate::ty::{self, OpaqueHiddenType, Ty, TyCtxt}; use rustc_data_structures::stable_map::FxHashMap; use rustc_data_structures::vec_map::VecMap; @@ -413,13 +413,20 @@ pub enum ClosureOutlivesSubject<'tcx> { Region(ty::RegionVid), } -/// The constituent parts of an ADT or array. +/// The constituent parts of a type level constant of kind ADT or array. #[derive(Copy, Clone, Debug, HashStable)] pub struct DestructuredConst<'tcx> { pub variant: Option<VariantIdx>, pub fields: &'tcx [ty::Const<'tcx>], } +/// The constituent parts of a mir constant of kind ADT or array. +#[derive(Copy, Clone, Debug, HashStable)] +pub struct DestructuredMirConstant<'tcx> { + pub variant: Option<VariantIdx>, + pub fields: &'tcx [mir::ConstantKind<'tcx>], +} + /// Coverage information summarized from a MIR if instrumented for source code coverage (see /// compiler option `-Cinstrument-coverage`). This information is generated by the /// `InstrumentCoverage` MIR pass and can be retrieved via the `coverageinfo` query. diff --git a/compiler/rustc_middle/src/mir/switch_sources.rs b/compiler/rustc_middle/src/mir/switch_sources.rs index 7f62b4d0dba..adeeec70d1c 100644 --- a/compiler/rustc_middle/src/mir/switch_sources.rs +++ b/compiler/rustc_middle/src/mir/switch_sources.rs @@ -2,6 +2,7 @@ //! `Predecessors`/`PredecessorCache`. use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::stable_map::FxHashMap; use rustc_data_structures::sync::OnceCell; use rustc_index::vec::IndexVec; use rustc_serialize as serialize; @@ -9,7 +10,7 @@ use smallvec::SmallVec; use crate::mir::{BasicBlock, BasicBlockData, Terminator, TerminatorKind}; -pub type SwitchSources = IndexVec<BasicBlock, IndexVec<BasicBlock, SmallVec<[Option<u128>; 1]>>>; +pub type SwitchSources = FxHashMap<(BasicBlock, BasicBlock), SmallVec<[Option<u128>; 1]>>; #[derive(Clone, Debug)] pub(super) struct SwitchSourceCache { @@ -35,19 +36,16 @@ impl SwitchSourceCache { basic_blocks: &IndexVec<BasicBlock, BasicBlockData<'_>>, ) -> &SwitchSources { self.cache.get_or_init(|| { - let mut switch_sources = IndexVec::from_elem( - IndexVec::from_elem(SmallVec::new(), basic_blocks), - basic_blocks, - ); + let mut switch_sources: SwitchSources = FxHashMap::default(); for (bb, data) in basic_blocks.iter_enumerated() { if let Some(Terminator { kind: TerminatorKind::SwitchInt { targets, .. }, .. }) = &data.terminator { for (value, target) in targets.iter() { - switch_sources[target][bb].push(Some(value)); + switch_sources.entry((target, bb)).or_default().push(Some(value)); } - switch_sources[targets.otherwise()][bb].push(None); + switch_sources.entry((targets.otherwise(), bb)).or_default().push(None); } } diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index f1d5201454d..c93b7a95502 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -202,7 +202,9 @@ impl<'tcx> Rvalue<'tcx> { Rvalue::Aggregate(ref ak, ref ops) => match **ak { AggregateKind::Array(ty) => tcx.mk_array(ty, ops.len() as u64), AggregateKind::Tuple => tcx.mk_tup(ops.iter().map(|op| op.ty(local_decls, tcx))), - AggregateKind::Adt(did, _, substs, _, _) => tcx.type_of(did).subst(tcx, substs), + AggregateKind::Adt(did, _, substs, _, _) => { + tcx.bound_type_of(did).subst(tcx, substs) + } AggregateKind::Closure(did, substs) => tcx.mk_closure(did, substs), AggregateKind::Generator(did, substs, movability) => { tcx.mk_generator(did, substs, movability) diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index cc08857463d..fb3856b4952 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -202,7 +202,7 @@ pub enum TerminatorKind<'tcx> { /// This assignment occurs both in the unwind and the regular code paths. The semantics are best /// explained by the elaboration: /// - /// ``` + /// ```ignore (MIR) /// BB0 { /// DropAndReplace(P <- V, goto BB1, unwind BB2) /// } @@ -210,7 +210,7 @@ pub enum TerminatorKind<'tcx> { /// /// becomes /// - /// ``` + /// ```ignore (MIR) /// BB0 { /// Drop(P, goto BB1, unwind BB2) /// } @@ -416,32 +416,36 @@ impl<'tcx> TerminatorKind<'tcx> { | Return | Unreachable | Call { destination: None, cleanup: None, .. } - | InlineAsm { destination: None, cleanup: None, .. } => None.into_iter().chain(&[]), - Goto { target: ref t } - | Call { destination: None, cleanup: Some(ref t), .. } - | Call { destination: Some((_, ref t)), cleanup: None, .. } - | Yield { resume: ref t, drop: None, .. } - | DropAndReplace { target: ref t, unwind: None, .. } - | Drop { target: ref t, unwind: None, .. } - | Assert { target: ref t, cleanup: None, .. } - | FalseUnwind { real_target: ref t, unwind: None } - | InlineAsm { destination: Some(ref t), cleanup: None, .. } - | InlineAsm { destination: None, cleanup: Some(ref t), .. } => { - Some(t).into_iter().chain(&[]) + | InlineAsm { destination: None, cleanup: None, .. } => { + None.into_iter().chain((&[]).into_iter().copied()) + } + Goto { target: t } + | Call { destination: None, cleanup: Some(t), .. } + | Call { destination: Some((_, t)), cleanup: None, .. } + | Yield { resume: t, drop: None, .. } + | DropAndReplace { target: t, unwind: None, .. } + | Drop { target: t, unwind: None, .. } + | Assert { target: t, cleanup: None, .. } + | FalseUnwind { real_target: t, unwind: None } + | InlineAsm { destination: Some(t), cleanup: None, .. } + | InlineAsm { destination: None, cleanup: Some(t), .. } => { + Some(t).into_iter().chain((&[]).into_iter().copied()) } - Call { destination: Some((_, ref t)), cleanup: Some(ref u), .. } - | Yield { resume: ref t, drop: Some(ref u), .. } - | DropAndReplace { target: ref t, unwind: Some(ref u), .. } - | Drop { target: ref t, unwind: Some(ref u), .. } - | Assert { target: ref t, cleanup: Some(ref u), .. } - | FalseUnwind { real_target: ref t, unwind: Some(ref u) } - | InlineAsm { destination: Some(ref t), cleanup: Some(ref u), .. } => { - Some(t).into_iter().chain(slice::from_ref(u)) + Call { destination: Some((_, t)), cleanup: Some(ref u), .. } + | Yield { resume: t, drop: Some(ref u), .. } + | DropAndReplace { target: t, unwind: Some(ref u), .. } + | Drop { target: t, unwind: Some(ref u), .. } + | Assert { target: t, cleanup: Some(ref u), .. } + | FalseUnwind { real_target: t, unwind: Some(ref u) } + | InlineAsm { destination: Some(t), cleanup: Some(ref u), .. } => { + Some(t).into_iter().chain(slice::from_ref(u).into_iter().copied()) } - SwitchInt { ref targets, .. } => None.into_iter().chain(&targets.targets), - FalseEdge { ref real_target, ref imaginary_target } => { - Some(real_target).into_iter().chain(slice::from_ref(imaginary_target)) + SwitchInt { ref targets, .. } => { + None.into_iter().chain(targets.targets.iter().copied()) } + FalseEdge { real_target, ref imaginary_target } => Some(real_target) + .into_iter() + .chain(slice::from_ref(imaginary_target).into_iter().copied()), } } diff --git a/compiler/rustc_middle/src/mir/traversal.rs b/compiler/rustc_middle/src/mir/traversal.rs index 8d831cc73b8..1cbfed62156 100644 --- a/compiler/rustc_middle/src/mir/traversal.rs +++ b/compiler/rustc_middle/src/mir/traversal.rs @@ -180,7 +180,7 @@ impl<'a, 'tcx> Postorder<'a, 'tcx> { // two iterations yield `C` and finally `A` for a final traversal of [E, D, B, C, A] loop { let bb = if let Some(&mut (_, ref mut iter)) = self.visit_stack.last_mut() { - if let Some(&bb) = iter.next() { + if let Some(bb) = iter.next() { bb } else { break; diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 45b1ad6df82..ef4f1f5e84e 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -29,7 +29,7 @@ //! //! For example, the `super_basic_block_data` method begins like this: //! -//! ```rust +//! ```ignore (pseudo-rust) //! fn super_basic_block_data(&mut self, //! block: BasicBlock, //! data: & $($mutability)? BasicBlockData<'tcx>) { @@ -258,6 +258,7 @@ macro_rules! make_mir_visitor { // for best performance, we want to use an iterator rather // than a for-loop, to avoid calling `body::Body::invalidate` for // each basic block. + #[cfg_attr(not(bootstrap), allow(unused_macro_rules))] macro_rules! basic_blocks { (mut) => (body.basic_blocks_mut().iter_enumerated_mut()); () => (body.basic_blocks().iter_enumerated()); @@ -279,6 +280,7 @@ macro_rules! make_mir_visitor { self.visit_local_decl(local, & $($mutability)? body.local_decls[local]); } + #[cfg_attr(not(bootstrap), allow(unused_macro_rules))] macro_rules! type_annotations { (mut) => (body.user_type_annotations.iter_enumerated_mut()); () => (body.user_type_annotations.iter_enumerated()); @@ -932,6 +934,7 @@ macro_rules! make_mir_visitor { body: &$($mutability)? Body<'tcx>, location: Location ) { + #[cfg_attr(not(bootstrap), allow(unused_macro_rules))] macro_rules! basic_blocks { (mut) => (body.basic_blocks_mut()); () => (body.basic_blocks()); @@ -1170,10 +1173,10 @@ pub enum NonMutatingUseContext { AddressOf, /// Used as base for another place, e.g., `x` in `x.y`. Will not mutate the place. /// For example, the projection `x.y` is not marked as a mutation in these cases: - /// - /// z = x.y; - /// f(&x.y); - /// + /// ```ignore (illustrative) + /// z = x.y; + /// f(&x.y); + /// ``` Projection, } @@ -1199,10 +1202,10 @@ pub enum MutatingUseContext { AddressOf, /// Used as base for another place, e.g., `x` in `x.y`. Could potentially mutate the place. /// For example, the projection `x.y` is marked as a mutation in these cases: - /// - /// x.y = ...; - /// f(&mut x.y); - /// + /// ```ignore (illustrative) + /// x.y = ...; + /// f(&mut x.y); + /// ``` Projection, /// Retagging, a "Stacked Borrows" shadow state operation Retag, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index e439d128dbc..0c936b7ae10 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -59,6 +59,7 @@ rustc_queries! { query hir_module_items(key: LocalDefId) -> rustc_middle::hir::ModuleItems { storage(ArenaCacheSelector<'tcx>) desc { |tcx| "HIR module items in `{}`", tcx.def_path_str(key.to_def_id()) } + cache_on_disk_if { true } } /// Gives access to the HIR node for the HIR owner `key`. @@ -128,6 +129,7 @@ rustc_queries! { /// parameter. e.g. `fn example<const N: usize=3>` called on `N` would return `3`. query const_param_default(param: DefId) -> ty::Const<'tcx> { desc { |tcx| "compute const default for a given parameter `{}`", tcx.def_path_str(param) } + cache_on_disk_if { param.is_local() } separate_provide_extern } @@ -157,6 +159,25 @@ rustc_queries! { desc { "running analysis passes on this crate" } } + /// This query checks the fulfillment of collected lint expectations. + /// All lint emitting queries have to be done before this is executed + /// to ensure that all expectations can be fulfilled. + /// + /// This is an extra query to enable other drivers (like rustdoc) to + /// only execute a small subset of the `analysis` query, while allowing + /// lints to be expected. In rustc, this query will be executed as part of + /// the `analysis` query and doesn't have to be called a second time. + /// + /// Tools can additionally pass in a tool filter. That will restrict the + /// expectations to only trigger for lints starting with the listed tool + /// name. This is useful for cases were not all linting code from rustc + /// was called. With the default `None` all registered lints will also + /// be checked for expectation fulfillment. + query check_expectations(key: Option<Symbol>) -> () { + eval_always + desc { "checking lint expectations (RFC 2383)" } + } + /// Maps from the `DefId` of an item (trait/struct/enum/fn) to its /// associated generics. query generics_of(key: DefId) -> ty::Generics { @@ -204,6 +225,7 @@ rustc_queries! { /// Bounds from the parent (e.g. with nested impl trait) are not included. query explicit_item_bounds(key: DefId) -> &'tcx [(ty::Predicate<'tcx>, Span)] { desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } separate_provide_extern } @@ -489,6 +511,7 @@ rustc_queries! { /// Returns the predicates written explicitly by the user. query explicit_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { desc { |tcx| "computing explicit predicates of `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } separate_provide_extern } @@ -496,6 +519,7 @@ rustc_queries! { /// Foo<'a, T> { x: &'a T }`, this would return `T: 'a`). query inferred_outlives_of(key: DefId) -> &'tcx [(ty::Predicate<'tcx>, Span)] { desc { |tcx| "computing inferred outlives predicates of `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } separate_provide_extern } @@ -507,6 +531,7 @@ rustc_queries! { /// additional acyclicity requirements). query super_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { desc { |tcx| "computing the super predicates of `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } separate_provide_extern } @@ -530,6 +555,7 @@ rustc_queries! { query trait_def(key: DefId) -> ty::TraitDef { desc { |tcx| "computing trait definition for `{}`", tcx.def_path_str(key) } storage(ArenaCacheSelector<'tcx>) + cache_on_disk_if { key.is_local() } separate_provide_extern } query adt_def(key: DefId) -> ty::AdtDef<'tcx> { @@ -539,6 +565,7 @@ rustc_queries! { } query adt_destructor(key: DefId) -> Option<ty::Destructor> { desc { |tcx| "computing `Drop` impl for `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } separate_provide_extern } @@ -568,11 +595,13 @@ rustc_queries! { /// `is_const_fn` function. query impl_constness(key: DefId) -> hir::Constness { desc { |tcx| "checking if item is const fn: `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } separate_provide_extern } query asyncness(key: DefId) -> hir::IsAsync { desc { |tcx| "checking if the function is async: `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } separate_provide_extern } @@ -590,12 +619,14 @@ rustc_queries! { /// Returns `true` if this is a foreign item (i.e., linked via `extern { ... }`). query is_foreign_item(key: DefId) -> bool { desc { |tcx| "checking if `{}` is a foreign item", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } separate_provide_extern } /// Returns `Some(generator_kind)` if the node pointed to by `def_id` is a generator. query generator_kind(def_id: DefId) -> Option<hir::GeneratorKind> { desc { |tcx| "looking up generator kind of `{}`", tcx.def_path_str(def_id) } + cache_on_disk_if { def_id.is_local() } separate_provide_extern } @@ -608,6 +639,7 @@ rustc_queries! { /// Maps from the `DefId` of a type or region parameter to its (inferred) variance. query variances_of(def_id: DefId) -> &'tcx [ty::Variance] { desc { |tcx| "computing the variances of `{}`", tcx.def_path_str(def_id) } + cache_on_disk_if { def_id.is_local() } separate_provide_extern } @@ -620,6 +652,7 @@ rustc_queries! { /// Maps from an impl/trait `DefId` to a list of the `DefId`s of its items. query associated_item_def_ids(key: DefId) -> &'tcx [DefId] { desc { |tcx| "collecting associated items of `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } separate_provide_extern } @@ -627,6 +660,7 @@ rustc_queries! { query associated_item(key: DefId) -> ty::AssocItem { desc { |tcx| "computing associated item data for `{}`", tcx.def_path_str(key) } storage(ArenaCacheSelector<'tcx>) + cache_on_disk_if { key.is_local() } separate_provide_extern } @@ -666,10 +700,12 @@ rustc_queries! { /// Return `None` if this is an inherent impl. query impl_trait_ref(impl_id: DefId) -> Option<ty::TraitRef<'tcx>> { desc { |tcx| "computing trait implemented by `{}`", tcx.def_path_str(impl_id) } + cache_on_disk_if { impl_id.is_local() } separate_provide_extern } query impl_polarity(impl_id: DefId) -> ty::ImplPolarity { desc { |tcx| "computing implementation polarity of `{}`", tcx.def_path_str(impl_id) } + cache_on_disk_if { impl_id.is_local() } separate_provide_extern } @@ -682,6 +718,7 @@ rustc_queries! { /// Methods in these implementations don't need to be exported. query inherent_impls(key: DefId) -> &'tcx [DefId] { desc { |tcx| "collecting inherent impls for `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } separate_provide_extern } @@ -726,6 +763,7 @@ rustc_queries! { /// Computes the signature of the function. query fn_sig(key: DefId) -> ty::PolyFnSig<'tcx> { desc { |tcx| "computing function signature of `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } separate_provide_extern } @@ -801,6 +839,7 @@ rustc_queries! { /// Caches `CoerceUnsized` kinds for impls on custom types. query coerce_unsized_info(key: DefId) -> ty::adjustment::CoerceUnsizedInfo { desc { |tcx| "computing CoerceUnsized info for `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } separate_provide_extern } @@ -927,12 +966,12 @@ rustc_queries! { cache_on_disk_if { true } } - /// Convert an evaluated constant to a type level constant or + /// Evaluate a constant and convert it to a type level constant or /// return `None` if that is not possible. - query const_to_valtree( - key: ty::ParamEnvAnd<'tcx, ConstAlloc<'tcx>> - ) -> Option<ty::ValTree<'tcx>> { - desc { "destructure constant" } + query eval_to_valtree( + key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>> + ) -> EvalToValTreeResult<'tcx> { + desc { "evaluate type-level constant" } remap_env_constness } @@ -945,10 +984,14 @@ rustc_queries! { /// field values or return `None` if constant is invalid. /// /// Use infallible `TyCtxt::destructure_const` when you know that constant is valid. - query try_destructure_const( - key: ty::ParamEnvAnd<'tcx, ty::Const<'tcx>> - ) -> Option<mir::DestructuredConst<'tcx>> { - desc { "destructure constant" } + query try_destructure_const(key: ty::ParamEnvAnd<'tcx, ty::Const<'tcx>>) -> Option<mir::DestructuredConst<'tcx>> { + desc { "destructure type level constant"} + } + + /// Tries to destructure an `mir::ConstantKind` ADT or array into its variant index + /// and its field values. + query try_destructure_mir_constant(key: ty::ParamEnvAnd<'tcx, mir::ConstantKind<'tcx>>) -> Option<mir::DestructuredMirConstant<'tcx>> { + desc { "destructure mir constant"} remap_env_constness } @@ -961,6 +1004,15 @@ rustc_queries! { remap_env_constness } + /// Dereference a constant reference or raw pointer and turn the result into a constant + /// again. + query deref_mir_constant( + key: ty::ParamEnvAnd<'tcx, mir::ConstantKind<'tcx>> + ) -> mir::ConstantKind<'tcx> { + desc { "deref constant" } + remap_env_constness + } + query const_caller_location(key: (rustc_span::Symbol, u32, u32)) -> ConstValue<'tcx> { desc { "get a &core::panic::Location referring to a span" } } @@ -972,6 +1024,10 @@ rustc_queries! { desc { "converting literal to const" } } + query lit_to_mir_constant(key: LitToConstInput<'tcx>) -> Result<mir::ConstantKind<'tcx>, LitToConstError> { + desc { "converting literal to mir constant" } + } + query check_match(key: DefId) { desc { |tcx| "match-checking `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } @@ -992,12 +1048,6 @@ rustc_queries! { desc { "reachability" } } - /// Per-body `region::ScopeTree`. The `DefId` should be the owner `DefId` for the body; - /// in the case of closures, this will be redirected to the enclosing function. - query region_scope_tree(def_id: DefId) -> &'tcx region::ScopeTree { - desc { |tcx| "computing drop scopes for `{}`", tcx.def_path_str(def_id) } - } - /// Generates a MIR body for the shim. query mir_shims(key: ty::InstanceDef<'tcx>) -> mir::Body<'tcx> { storage(ArenaCacheSelector<'tcx>) @@ -1014,28 +1064,33 @@ rustc_queries! { query opt_def_kind(def_id: DefId) -> Option<DefKind> { desc { |tcx| "looking up definition kind of `{}`", tcx.def_path_str(def_id) } + cache_on_disk_if { def_id.is_local() } separate_provide_extern } /// Gets the span for the definition. query def_span(def_id: DefId) -> Span { desc { |tcx| "looking up span for `{}`", tcx.def_path_str(def_id) } + cache_on_disk_if { def_id.is_local() } separate_provide_extern } /// Gets the span for the identifier of the definition. query def_ident_span(def_id: DefId) -> Option<Span> { desc { |tcx| "looking up span for `{}`'s identifier", tcx.def_path_str(def_id) } + cache_on_disk_if { def_id.is_local() } separate_provide_extern } query lookup_stability(def_id: DefId) -> Option<attr::Stability> { desc { |tcx| "looking up stability of `{}`", tcx.def_path_str(def_id) } + cache_on_disk_if { def_id.is_local() } separate_provide_extern } query lookup_const_stability(def_id: DefId) -> Option<attr::ConstStability> { desc { |tcx| "looking up const stability of `{}`", tcx.def_path_str(def_id) } + cache_on_disk_if { def_id.is_local() } separate_provide_extern } @@ -1045,6 +1100,7 @@ rustc_queries! { query lookup_deprecation_entry(def_id: DefId) -> Option<DeprecationEntry> { desc { |tcx| "checking whether `{}` is deprecated", tcx.def_path_str(def_id) } + cache_on_disk_if { def_id.is_local() } separate_provide_extern } @@ -1053,6 +1109,9 @@ rustc_queries! { desc { |tcx| "checking whether `{}` is `doc(hidden)`", tcx.def_path_str(def_id) } } + /// 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] { desc { |tcx| "collecting attributes of `{}`", tcx.def_path_str(def_id) } separate_provide_extern @@ -1061,7 +1120,8 @@ rustc_queries! { query codegen_fn_attrs(def_id: DefId) -> CodegenFnAttrs { desc { |tcx| "computing codegen attributes of `{}`", tcx.def_path_str(def_id) } storage(ArenaCacheSelector<'tcx>) - cache_on_disk_if { true } + cache_on_disk_if { def_id.is_local() } + separate_provide_extern } query asm_target_features(def_id: DefId) -> &'tcx FxHashSet<Symbol> { @@ -1070,6 +1130,7 @@ rustc_queries! { query fn_arg_names(def_id: DefId) -> &'tcx [rustc_span::symbol::Ident] { desc { |tcx| "looking up function parameter names for `{}`", tcx.def_path_str(def_id) } + cache_on_disk_if { def_id.is_local() } separate_provide_extern } /// Gets the rendered value of the specified constant or associated constant. @@ -1077,10 +1138,12 @@ rustc_queries! { query rendered_const(def_id: DefId) -> String { storage(ArenaCacheSelector<'tcx>) desc { |tcx| "rendering constant intializer of `{}`", tcx.def_path_str(def_id) } + cache_on_disk_if { def_id.is_local() } separate_provide_extern } query impl_parent(def_id: DefId) -> Option<DefId> { desc { |tcx| "computing specialization parent impl of `{}`", tcx.def_path_str(def_id) } + cache_on_disk_if { def_id.is_local() } separate_provide_extern } @@ -1088,15 +1151,18 @@ rustc_queries! { /// Return `None` if the `DefId` is not an associated item. query trait_of_item(associated_item: DefId) -> Option<DefId> { desc { |tcx| "finding trait defining `{}`", tcx.def_path_str(associated_item) } + cache_on_disk_if { associated_item.is_local() } separate_provide_extern } query is_ctfe_mir_available(key: DefId) -> bool { desc { |tcx| "checking if item has ctfe mir available: `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } separate_provide_extern } query is_mir_available(key: DefId) -> bool { desc { |tcx| "checking if item has mir available: `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } separate_provide_extern } @@ -1125,7 +1191,7 @@ rustc_queries! { query codegen_fulfill_obligation( key: (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>) - ) -> Result<&'tcx ImplSource<'tcx, ()>, ErrorGuaranteed> { + ) -> Result<&'tcx ImplSource<'tcx, ()>, traits::CodegenObligationError> { cache_on_disk_if { true } desc { |tcx| "checking if `{}` fulfills its obligations", @@ -1338,6 +1404,7 @@ rustc_queries! { query impl_defaultness(def_id: DefId) -> hir::Defaultness { desc { |tcx| "looking up whether `{}` is a default impl", tcx.def_path_str(def_id) } + cache_on_disk_if { def_id.is_local() } separate_provide_extern } @@ -1371,6 +1438,7 @@ rustc_queries! { } query is_reachable_non_generic(def_id: DefId) -> bool { desc { |tcx| "checking whether `{}` is an exported symbol", tcx.def_path_str(def_id) } + cache_on_disk_if { def_id.is_local() } separate_provide_extern } query is_unreachable_local_definition(def_id: LocalDefId) -> bool { @@ -1578,6 +1646,11 @@ rustc_queries! { desc { "calculating the lib features defined in a crate" } separate_provide_extern } + /// Whether the function is an intrinsic + query is_intrinsic(def_id: DefId) -> bool { + desc { |tcx| "is_intrinsic({})", tcx.def_path_str(def_id) } + separate_provide_extern + } /// Returns the lang items defined in another crate by loading it from metadata. query get_lang_items(_: ()) -> LanguageItems { storage(ArenaCacheSelector<'tcx>) @@ -1685,9 +1758,9 @@ rustc_queries! { /// - All names contained in `exported_symbols(cnum)` are guaranteed to /// correspond to a publicly visible symbol in `cnum` machine code. /// - The `exported_symbols` sets of different crates do not intersect. - query exported_symbols(_: CrateNum) - -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)] { + query exported_symbols(cnum: CrateNum) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)] { desc { "exported_symbols" } + cache_on_disk_if { *cnum == LOCAL_CRATE } separate_provide_extern } diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index fdf5ecfdaf7..b99e7573000 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -18,15 +18,11 @@ use rustc_index::vec::IndexVec; use rustc_middle::infer::canonical::Canonical; use rustc_middle::middle::region; use rustc_middle::mir::interpret::AllocId; -use rustc_middle::mir::{ - self, BinOp, BorrowKind, FakeReadCause, Field, Mutability, UnOp, UserTypeProjection, -}; +use rustc_middle::mir::{self, BinOp, BorrowKind, FakeReadCause, Field, Mutability, UnOp}; use rustc_middle::ty::adjustment::PointerCast; use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::CanonicalUserTypeAnnotation; use rustc_middle::ty::{self, AdtDef, Ty, UpvarSubsts, UserType}; -use rustc_middle::ty::{ - CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, -}; use rustc_span::{Span, Symbol, DUMMY_SP}; use rustc_target::abi::VariantIdx; use rustc_target::asm::InlineAsmRegOrRegClass; @@ -540,13 +536,13 @@ pub enum BindingMode { ByRef(BorrowKind), } -#[derive(Clone, Debug, PartialEq, HashStable)] +#[derive(Clone, Debug, HashStable)] pub struct FieldPat<'tcx> { pub field: Field, pub pattern: Pat<'tcx>, } -#[derive(Clone, Debug, PartialEq, HashStable)] +#[derive(Clone, Debug, HashStable)] pub struct Pat<'tcx> { pub ty: Ty<'tcx>, pub span: Span, @@ -559,37 +555,10 @@ impl<'tcx> Pat<'tcx> { } } -#[derive(Copy, Clone, Debug, PartialEq, HashStable)] -pub struct PatTyProj<'tcx> { - pub user_ty: CanonicalUserType<'tcx>, -} - -impl<'tcx> PatTyProj<'tcx> { - pub fn from_user_type(user_annotation: CanonicalUserType<'tcx>) -> Self { - Self { user_ty: user_annotation } - } - - pub fn user_ty( - self, - annotations: &mut CanonicalUserTypeAnnotations<'tcx>, - inferred_ty: Ty<'tcx>, - span: Span, - ) -> UserTypeProjection { - UserTypeProjection { - base: annotations.push(CanonicalUserTypeAnnotation { - span, - user_ty: self.user_ty, - inferred_ty, - }), - projs: Vec::new(), - } - } -} - -#[derive(Copy, Clone, Debug, PartialEq, HashStable)] +#[derive(Clone, Debug, HashStable)] pub struct Ascription<'tcx> { - pub user_ty: PatTyProj<'tcx>, - /// Variance to use when relating the type `user_ty` to the **type of the value being + pub annotation: CanonicalUserTypeAnnotation<'tcx>, + /// Variance to use when relating the `user_ty` to the **type of the value being /// matched**. Typically, this is `Variance::Covariant`, since the value being matched must /// have a type that is some subtype of the ascribed type. /// @@ -608,12 +577,11 @@ pub struct Ascription<'tcx> { /// probably be checking for a `PartialEq` impl instead, but this preserves the behavior /// of the old type-check for now. See #57280 for details. pub variance: ty::Variance, - pub user_ty_span: Span, } -#[derive(Clone, Debug, PartialEq, HashStable)] +#[derive(Clone, Debug, HashStable)] pub enum PatKind<'tcx> { - /// A wildward pattern: `_`. + /// A wildcard pattern: `_`. Wild, AscribeUserType { @@ -662,7 +630,7 @@ pub enum PatKind<'tcx> { /// * Opaque constants, that must not be matched structurally. So anything that does not derive /// `PartialEq` and `Eq`. Constant { - value: ty::Const<'tcx>, + value: mir::ConstantKind<'tcx>, }, Range(PatRange<'tcx>), @@ -692,8 +660,8 @@ pub enum PatKind<'tcx> { #[derive(Copy, Clone, Debug, PartialEq, HashStable)] pub struct PatRange<'tcx> { - pub lo: ty::Const<'tcx>, - pub hi: ty::Const<'tcx>, + pub lo: mir::ConstantKind<'tcx>, + pub hi: mir::ConstantKind<'tcx>, pub end: RangeEnd, } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 7c3d08b26bf..4d30c0e35e4 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -47,7 +47,8 @@ pub enum Reveal { /// impl. Concretely, that means that the following example will /// fail to compile: /// - /// ``` + /// ```compile_fail,E0308 + /// #![feature(specialization)] /// trait Assoc { /// type Output; /// } @@ -57,9 +58,12 @@ pub enum Reveal { /// } /// /// fn main() { - /// let <() as Assoc>::Output = true; + /// let x: <() as Assoc>::Output = true; /// } /// ``` + /// + /// We also do not reveal the hidden type of opaque types during + /// type-checking. UserFacing, /// At codegen time, all monomorphic projections will succeed. @@ -96,9 +100,7 @@ pub struct ObligationCause<'tcx> { /// information. pub body_id: hir::HirId, - /// `None` for `MISC_OBLIGATION_CAUSE_CODE` (a common case, occurs ~60% of - /// the time). `Some` otherwise. - code: Option<Lrc<ObligationCauseCode<'tcx>>>, + code: InternedObligationCauseCode<'tcx>, } // This custom hash function speeds up hashing for `Obligation` deduplication @@ -122,11 +124,7 @@ impl<'tcx> ObligationCause<'tcx> { body_id: hir::HirId, code: ObligationCauseCode<'tcx>, ) -> ObligationCause<'tcx> { - ObligationCause { - span, - body_id, - code: if code == MISC_OBLIGATION_CAUSE_CODE { None } else { Some(Lrc::new(code)) }, - } + ObligationCause { span, body_id, code: code.into() } } pub fn misc(span: Span, body_id: hir::HirId) -> ObligationCause<'tcx> { @@ -135,15 +133,12 @@ impl<'tcx> ObligationCause<'tcx> { #[inline(always)] pub fn dummy() -> ObligationCause<'tcx> { - ObligationCause { span: DUMMY_SP, body_id: hir::CRATE_HIR_ID, code: None } + ObligationCause::dummy_with_span(DUMMY_SP) } + #[inline(always)] pub fn dummy_with_span(span: Span) -> ObligationCause<'tcx> { - ObligationCause { span, body_id: hir::CRATE_HIR_ID, code: None } - } - - pub fn make_mut_code(&mut self) -> &mut ObligationCauseCode<'tcx> { - Lrc::make_mut(self.code.get_or_insert_with(|| Lrc::new(MISC_OBLIGATION_CAUSE_CODE))) + ObligationCause { span, body_id: hir::CRATE_HIR_ID, code: Default::default() } } pub fn span(&self, tcx: TyCtxt<'tcx>) -> Span { @@ -163,14 +158,37 @@ impl<'tcx> ObligationCause<'tcx> { #[inline] pub fn code(&self) -> &ObligationCauseCode<'tcx> { - self.code.as_deref().unwrap_or(&MISC_OBLIGATION_CAUSE_CODE) + &self.code } - pub fn clone_code(&self) -> Lrc<ObligationCauseCode<'tcx>> { - match &self.code { - Some(code) => code.clone(), - None => Lrc::new(MISC_OBLIGATION_CAUSE_CODE), - } + pub fn map_code( + &mut self, + f: impl FnOnce(InternedObligationCauseCode<'tcx>) -> ObligationCauseCode<'tcx>, + ) { + self.code = f(std::mem::take(&mut self.code)).into(); + } + + pub fn derived_cause( + mut self, + parent_trait_pred: ty::PolyTraitPredicate<'tcx>, + variant: impl FnOnce(DerivedObligationCause<'tcx>) -> ObligationCauseCode<'tcx>, + ) -> ObligationCause<'tcx> { + /*! + * Creates a cause for obligations that are derived from + * `obligation` by a recursive search (e.g., for a builtin + * bound, or eventually a `auto trait Foo`). If `obligation` + * is itself a derived obligation, this is just a clone, but + * otherwise we create a "derived obligation" cause so as to + * keep track of the original root obligation for error + * reporting. + */ + + // NOTE(flaper87): As of now, it keeps track of the whole error + // chain. Ideally, we should have a way to configure this either + // by using -Z verbose or just a CLI argument. + self.code = + variant(DerivedObligationCause { parent_trait_pred, parent_code: self.code }).into(); + self } } @@ -181,6 +199,30 @@ pub struct UnifyReceiverContext<'tcx> { pub substs: SubstsRef<'tcx>, } +#[derive(Clone, Debug, PartialEq, Eq, Hash, Lift, Default)] +pub struct InternedObligationCauseCode<'tcx> { + /// `None` for `MISC_OBLIGATION_CAUSE_CODE` (a common case, occurs ~60% of + /// the time). `Some` otherwise. + code: Option<Lrc<ObligationCauseCode<'tcx>>>, +} + +impl<'tcx> ObligationCauseCode<'tcx> { + #[inline(always)] + fn into(self) -> InternedObligationCauseCode<'tcx> { + InternedObligationCauseCode { + code: if let MISC_OBLIGATION_CAUSE_CODE = self { None } else { Some(Lrc::new(self)) }, + } + } +} + +impl<'tcx> std::ops::Deref for InternedObligationCauseCode<'tcx> { + type Target = ObligationCauseCode<'tcx>; + + fn deref(&self) -> &Self::Target { + self.code.as_deref().unwrap_or(&MISC_OBLIGATION_CAUSE_CODE) + } +} + #[derive(Clone, Debug, PartialEq, Eq, Hash, Lift)] pub enum ObligationCauseCode<'tcx> { /// Not well classified or should be obvious from the span. @@ -268,7 +310,7 @@ pub enum ObligationCauseCode<'tcx> { /// The node of the function call. call_hir_id: hir::HirId, /// The obligation introduced by this argument. - parent_code: Lrc<ObligationCauseCode<'tcx>>, + parent_code: InternedObligationCauseCode<'tcx>, }, /// Error derived when matching traits/impls; see ObligationCause for more details @@ -403,25 +445,27 @@ pub struct ImplDerivedObligationCause<'tcx> { pub span: Span, } -impl ObligationCauseCode<'_> { +impl<'tcx> ObligationCauseCode<'tcx> { // Return the base obligation, ignoring derived obligations. pub fn peel_derives(&self) -> &Self { let mut base_cause = self; - loop { - match base_cause { - BuiltinDerivedObligation(DerivedObligationCause { parent_code, .. }) - | DerivedObligation(DerivedObligationCause { parent_code, .. }) - | FunctionArgumentObligation { parent_code, .. } => { - base_cause = &parent_code; - } - ImplDerivedObligation(obligation_cause) => { - base_cause = &*obligation_cause.derived.parent_code; - } - _ => break, - } + while let Some((parent_code, _)) = base_cause.parent() { + base_cause = parent_code; } base_cause } + + pub fn parent(&self) -> Option<(&Self, Option<ty::PolyTraitPredicate<'tcx>>)> { + match self { + FunctionArgumentObligation { parent_code, .. } => Some((parent_code, None)), + BuiltinDerivedObligation(derived) + | DerivedObligation(derived) + | ImplDerivedObligation(box ImplDerivedObligationCause { derived, .. }) => { + Some((&derived.parent_code, Some(derived.parent_trait_pred))) + } + _ => None, + } + } } // `ObligationCauseCode` is used a lot. Make sure it doesn't unintentionally get bigger. @@ -471,7 +515,7 @@ pub struct DerivedObligationCause<'tcx> { pub parent_trait_pred: ty::PolyTraitPredicate<'tcx>, /// The parent trait had this cause. - pub parent_code: Lrc<ObligationCauseCode<'tcx>>, + pub parent_code: InternedObligationCauseCode<'tcx>, } #[derive(Clone, Debug, TypeFoldable, Lift)] @@ -515,7 +559,7 @@ pub type SelectionResult<'tcx, T> = Result<Option<T>, SelectionError<'tcx>>; /// For example, the obligation may be satisfied by a specific impl (case A), /// or it may be relative to some bound that is in scope (case B). /// -/// ``` +/// ```ignore (illustrative) /// impl<T:Clone> Clone<T> for Option<T> { ... } // Impl_1 /// impl<T:Clone> Clone<T> for Box<T> { ... } // Impl_2 /// impl Clone for i32 { ... } // Impl_3 @@ -962,3 +1006,21 @@ pub enum MethodViolationCode { /// the method's receiver (`self` argument) can't be dispatched on UndispatchableReceiver, } + +/// These are the error cases for `codegen_fulfill_obligation`. +#[derive(Copy, Clone, Debug, Hash, HashStable, Encodable, Decodable)] +pub enum CodegenObligationError { + /// Ambiguity can happen when monomorphizing during trans + /// expands to some humongous type that never occurred + /// statically -- this humongous type can then overflow, + /// leading to an ambiguous result. So report this as an + /// overflow bug, since I believe this is the only case + /// where ambiguity can result. + Ambiguity, + /// This can trigger when we probe for the source of a `'static` lifetime requirement + /// on a trait object: `impl Foo for dyn Trait {}` has an implicit `'static` bound. + /// This can also trigger when we have a global bound that is not actually satisfied, + /// but was included during typeck due to the trivial_bounds feature. + Unimplemented, + FulfillmentError, +} diff --git a/compiler/rustc_middle/src/traits/specialization_graph.rs b/compiler/rustc_middle/src/traits/specialization_graph.rs index c43ec048c3f..ce13825b016 100644 --- a/compiler/rustc_middle/src/traits/specialization_graph.rs +++ b/compiler/rustc_middle/src/traits/specialization_graph.rs @@ -180,6 +180,7 @@ pub struct LeafDef { /// Example: /// /// ``` + /// #![feature(specialization)] /// trait Tr { /// fn assoc(&self); /// } diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index 2676b7ab521..d9332f6896a 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -59,7 +59,7 @@ pub enum PointerCast { /// sized struct to a dynamically sized one. E.g., `&[i32; 4]` -> `&[i32]` is /// represented by: /// -/// ``` +/// ```ignore (illustrative) /// Deref(None) -> [i32; 4], /// Borrow(AutoBorrow::Ref) -> &[i32; 4], /// Unsize -> &[i32], diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index fc6710f07e3..bf7cb610a90 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -82,7 +82,7 @@ bitflags! { /// /// is essentially represented with [`Ty`] as the following pseudocode: /// -/// ``` +/// ```ignore (illustrative) /// struct S { x } /// ``` /// @@ -230,8 +230,7 @@ impl AdtDefData { flags |= AdtFlags::HAS_CTOR; } - let attrs = tcx.get_attrs(did); - if tcx.sess.contains_name(&attrs, sym::fundamental) { + if tcx.has_attr(did, sym::fundamental) { flags |= AdtFlags::IS_FUNDAMENTAL; } if Some(did) == tcx.lang_items().phantom_data() { diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index 49f846562a3..2c93af50667 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -9,7 +9,7 @@ use rustc_span::symbol::{Ident, Symbol}; use super::{TyCtxt, Visibility}; -#[derive(Clone, Copy, PartialEq, Eq, Debug, HashStable, Hash)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, HashStable, Hash, Encodable, Decodable)] pub enum AssocItemContainer { TraitContainer(DefId), ImplContainer(DefId), @@ -41,7 +41,7 @@ impl AssocItemContainer { } /// Information about an associated item -#[derive(Copy, Clone, Debug, PartialEq, HashStable, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, HashStable, Eq, Hash, Encodable, Decodable)] pub struct AssocItem { pub def_id: DefId, pub name: Symbol, @@ -81,7 +81,7 @@ impl AssocItem { } } -#[derive(Copy, Clone, PartialEq, Debug, HashStable, Eq, Hash)] +#[derive(Copy, Clone, PartialEq, Debug, HashStable, Eq, Hash, Encodable, Decodable)] pub enum AssocKind { Const, Fn, diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs index 3bddf7fb6ff..c88cac30a19 100644 --- a/compiler/rustc_middle/src/ty/closure.rs +++ b/compiler/rustc_middle/src/ty/closure.rs @@ -293,7 +293,7 @@ pub struct CaptureInfo { /// let mut t = (0,1); /// /// let c = || { - /// println!("{t}"); // L1 + /// println!("{t:?}"); // L1 /// t.1 = 4; // L2 /// }; /// ``` @@ -309,7 +309,7 @@ pub struct CaptureInfo { /// let x = 5; /// /// let c = || { - /// let _ = x + /// let _ = x; /// }; /// ``` /// @@ -373,17 +373,19 @@ pub enum BorrowKind { /// is borrowing or mutating a mutable referent, e.g.: /// /// ``` - /// let x: &mut isize = ...; + /// let mut z = 3; + /// let x: &mut isize = &mut z; /// let y = || *x += 5; /// ``` /// /// If we were to try to translate this closure into a more explicit /// form, we'd encounter an error with the code as written: /// - /// ``` - /// struct Env { x: & &mut isize } - /// let x: &mut isize = ...; - /// let y = (&mut Env { &x }, fn_ptr); // Closure is pair of env and fn + /// ```compile_fail,E0594 + /// struct Env<'a> { x: &'a &'a mut isize } + /// let mut z = 3; + /// let x: &mut isize = &mut z; + /// let y = (&mut Env { x: &x }, fn_ptr); // Closure is pair of env and fn /// fn fn_ptr(env: &mut Env) { **env.x += 5; } /// ``` /// @@ -391,10 +393,11 @@ pub enum BorrowKind { /// in an aliasable location. To solve, you'd have to translate with /// an `&mut` borrow: /// - /// ``` - /// struct Env { x: &mut &mut isize } - /// let x: &mut isize = ...; - /// let y = (&mut Env { &mut x }, fn_ptr); // changed from &x to &mut x + /// ```compile_fail,E0596 + /// struct Env<'a> { x: &'a mut &'a mut isize } + /// let mut z = 3; + /// let x: &mut isize = &mut z; + /// let y = (&mut Env { x: &mut x }, fn_ptr); // changed from &x to &mut x /// fn fn_ptr(env: &mut Env) { **env.x += 5; } /// ``` /// diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index 23c377651cc..2a5191008a9 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -168,25 +168,6 @@ impl<'tcx, E: TyEncoder<'tcx>> Encodable<E> for AllocId { } } -macro_rules! encodable_via_deref { - ($($t:ty),+) => { - $(impl<'tcx, E: TyEncoder<'tcx>> Encodable<E> for $t { - fn encode(&self, e: &mut E) -> Result<(), E::Error> { - (**self).encode(e) - } - })* - } -} - -encodable_via_deref! { - &'tcx ty::TypeckResults<'tcx>, - &'tcx traits::ImplSource<'tcx, ()>, - &'tcx mir::Body<'tcx>, - &'tcx mir::UnsafetyCheckResult, - &'tcx mir::BorrowCheckResult<'tcx>, - &'tcx mir::coverage::CodeRegion -} - pub trait TyDecoder<'tcx>: Decoder { const CLEAR_CROSS_CRATE: bool; @@ -466,6 +447,33 @@ macro_rules! impl_arena_allocatable_decoders { rustc_hir::arena_types!(impl_arena_allocatable_decoders); arena_types!(impl_arena_allocatable_decoders); +macro_rules! impl_arena_copy_decoder { + (<$tcx:tt> $($ty:ty,)*) => { + $(impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for $ty { + #[inline] + fn decode(decoder: &mut D) -> &'tcx Self { + decoder.tcx().arena.alloc(Decodable::decode(decoder)) + } + } + + impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [$ty] { + #[inline] + fn decode(decoder: &mut D) -> &'tcx Self { + decoder.tcx().arena.alloc_from_iter(<Vec<_> as Decodable<D>>::decode(decoder)) + } + })* + }; +} + +impl_arena_copy_decoder! {<'tcx> + Span, + rustc_span::symbol::Ident, + ty::Variance, + rustc_span::def_id::DefId, + rustc_span::def_id::LocalDefId, + (rustc_middle::middle::exported_symbols::ExportedSymbol<'tcx>, rustc_middle::middle::exported_symbols::SymbolExportInfo), +} + #[macro_export] macro_rules! implement_ty_decoder { ($DecoderName:ident <$($typaram:tt),*>) => { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index f9ef264f68e..6b92a4c0a2e 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -5,6 +5,8 @@ use crate::dep_graph::{DepGraph, DepKind, DepKindStruct}; use crate::hir::place::Place as HirPlace; use crate::infer::canonical::{Canonical, CanonicalVarInfo, CanonicalVarInfos}; use crate::lint::{struct_lint_level, LintDiagnosticBuilder, LintLevelSource}; +use crate::middle::codegen_fn_attrs::CodegenFnAttrs; +use crate::middle::region::ScopeTree; use crate::middle::resolve_lifetime::{self, LifetimeScopeForPath}; use crate::middle::stability; use crate::mir::interpret::{self, Allocation, ConstAllocation, ConstValue, Scalar}; @@ -73,6 +75,8 @@ use std::mem; use std::ops::{Bound, Deref}; use std::sync::Arc; +use super::RvalueScopes; + pub trait OnDiskCache<'tcx>: rustc_data_structures::sync::Sync { /// Creates a new `OnDiskCache` instance from the serialized data in `data`. fn new(sess: &'tcx Session, data: Mmap, start_pos: usize) -> Self @@ -455,12 +459,13 @@ pub struct TypeckResults<'tcx> { /// # Example /// /// ```rust + /// # use std::fmt::Debug; /// fn foo(x: &u32) -> impl Debug { *x } /// ``` /// /// The function signature here would be: /// - /// ``` + /// ```ignore (illustrative) /// for<'a> fn(&'a u32) -> Foo /// ``` /// @@ -469,7 +474,7 @@ pub struct TypeckResults<'tcx> { /// /// The *liberated* form of this would be /// - /// ``` + /// ```ignore (illustrative) /// fn(&'a u32) -> u32 /// ``` /// @@ -533,6 +538,17 @@ pub struct TypeckResults<'tcx> { /// issue by fake reading `t`. pub closure_fake_reads: FxHashMap<DefId, Vec<(HirPlace<'tcx>, FakeReadCause, hir::HirId)>>, + /// Tracks critical information about regions in a body. + /// This includes containment relationship between regions, + /// liveness relationship between variables and regions and + /// information about yield points. + pub region_scope_tree: ScopeTree, + + /// Tracks the rvalue scoping rules which defines finer scoping for rvalue expressions + /// by applying extended parameter rules. + /// Details may be find in `rustc_typeck::check::rvalue_scopes`. + pub rvalue_scopes: RvalueScopes, + /// Stores the type, expression, span and optional scope span of all types /// that are live across the yield of this generator (if a generator). pub generator_interior_types: ty::Binder<'tcx, Vec<GeneratorInteriorTypeCause<'tcx>>>, @@ -570,6 +586,8 @@ impl<'tcx> TypeckResults<'tcx> { concrete_opaque_types: Default::default(), closure_min_captures: Default::default(), closure_fake_reads: Default::default(), + region_scope_tree: Default::default(), + rvalue_scopes: Default::default(), generator_interior_types: ty::Binder::dummy(Default::default()), treat_byte_string_as_slice: Default::default(), closure_size_eval: Default::default(), @@ -1065,6 +1083,28 @@ pub struct GlobalCtxt<'tcx> { } impl<'tcx> TyCtxt<'tcx> { + /// Expects a body and returns its codegen attributes. + /// + /// Unlike `codegen_fn_attrs`, this returns `CodegenFnAttrs::EMPTY` for + /// constants. + pub fn body_codegen_attrs(self, def_id: DefId) -> &'tcx CodegenFnAttrs { + let def_kind = self.def_kind(def_id); + if def_kind.has_codegen_attrs() { + self.codegen_fn_attrs(def_id) + } else if matches!( + def_kind, + DefKind::AnonConst | DefKind::AssocConst | DefKind::Const | DefKind::InlineConst + ) { + CodegenFnAttrs::EMPTY + } else { + bug!( + "body_codegen_fn_attrs called on unexpected definition: {:?} {:?}", + def_id, + def_kind + ) + } + } + pub fn typeck_opt_const_arg( self, def: ty::WithOptConstParam<LocalDefId>, @@ -1124,9 +1164,8 @@ impl<'tcx> TyCtxt<'tcx> { /// `rustc_layout_scalar_valid_range` attribute. // FIXME(eddyb) this is an awkward spot for this method, maybe move it? pub fn layout_scalar_valid_range(self, def_id: DefId) -> (Bound<u128>, Bound<u128>) { - let attrs = self.get_attrs(def_id); let get = |name| { - let Some(attr) = attrs.iter().find(|a| a.has_name(name)) else { + let Some(attr) = self.get_attr(def_id, name) else { return Bound::Unbounded; }; debug!("layout_scalar_valid_range: attr={:?}", attr); @@ -1207,7 +1246,7 @@ impl<'tcx> TyCtxt<'tcx> { } } - crate fn query_kind(self, k: DepKind) -> &'tcx DepKindStruct { + pub(crate) fn query_kind(self, k: DepKind) -> &'tcx DepKindStruct { &self.query_kinds[k as usize] } @@ -1580,7 +1619,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn caller_location_ty(self) -> Ty<'tcx> { self.mk_imm_ref( self.lifetimes.re_static, - self.type_of(self.require_lang_item(LangItem::PanicLocation, None)) + self.bound_type_of(self.require_lang_item(LangItem::PanicLocation, None)) .subst(self, self.mk_substs([self.lifetimes.re_static.into()].iter())), ) } @@ -2309,7 +2348,7 @@ impl<'tcx> TyCtxt<'tcx> { ty_param.into() } else { assert!(has_default); - self.type_of(param.def_id).subst(self, substs).into() + self.bound_type_of(param.def_id).subst(self, substs).into() } } }); @@ -2768,7 +2807,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn is_const_fn(self, def_id: DefId) -> bool { if self.is_const_fn_raw(def_id) { match self.lookup_const_stability(def_id) { - Some(stability) if stability.level.is_unstable() => { + Some(stability) if stability.is_const_unstable() => { // has a `rustc_const_unstable` attribute, check whether the user enabled the // corresponding feature gate. self.features() @@ -2785,6 +2824,21 @@ impl<'tcx> TyCtxt<'tcx> { false } } + + /// Whether the trait impl is marked const. This does not consider stability or feature gates. + pub fn is_const_trait_impl_raw(self, def_id: DefId) -> bool { + let Some(local_def_id) = def_id.as_local() else { return false }; + let hir_id = self.local_def_id_to_hir_id(local_def_id); + let node = self.hir().get(hir_id); + + matches!( + node, + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { constness: hir::Constness::Const, .. }), + .. + }) + ) + } } impl<'tcx> TyCtxtAt<'tcx> { diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index da0934b67c5..a6c14ea0de3 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -135,11 +135,10 @@ impl<'tcx> fmt::Display for TypeError<'tcx> { ArgCount => write!(f, "incorrect number of function parameters"), FieldMisMatch(adt, field) => write!(f, "field type mismatch: {}.{}", adt, field), RegionsDoesNotOutlive(..) => write!(f, "lifetime mismatch"), - RegionsInsufficientlyPolymorphic(br, _) => write!( - f, - "expected bound lifetime parameter{}, found concrete lifetime", - br_string(br) - ), + // Actually naming the region here is a bit confusing because context is lacking + RegionsInsufficientlyPolymorphic(..) => { + write!(f, "one type is more general than the other") + } RegionsOverlyPolymorphic(br, _) => write!( f, "expected concrete lifetime, found bound lifetime parameter{}", @@ -568,11 +567,8 @@ impl<T> Trait<T> for X { } } TargetFeatureCast(def_id) => { - let attrs = self.get_attrs(*def_id); - let target_spans = attrs - .iter() - .filter(|attr| attr.has_name(sym::target_feature)) - .map(|attr| attr.span); + let target_spans = + self.get_attrs(*def_id, sym::target_feature).map(|attr| attr.span); diag.note( "functions with `#[target_feature]` can only be coerced to `unsafe` function pointers" ); diff --git a/compiler/rustc_middle/src/ty/fast_reject.rs b/compiler/rustc_middle/src/ty/fast_reject.rs index 9c018dc685c..de4cc67893b 100644 --- a/compiler/rustc_middle/src/ty/fast_reject.rs +++ b/compiler/rustc_middle/src/ty/fast_reject.rs @@ -1,11 +1,8 @@ use crate::mir::Mutability; -use crate::ty::{self, Ty, TyCtxt}; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use crate::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc_hir::def_id::DefId; -use rustc_query_system::ich::StableHashingContext; use std::fmt::Debug; use std::hash::Hash; -use std::mem; use self::SimplifiedTypeGen::*; @@ -17,7 +14,7 @@ pub type SimplifiedType = SimplifiedTypeGen<DefId>; /// because we sometimes need to use SimplifiedTypeGen values as stable sorting /// keys (in which case we use a DefPathHash as id-type) but in the general case /// the non-stable but fast to construct DefId-version is the better choice. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] pub enum SimplifiedTypeGen<D> where D: Copy + Debug + Eq, @@ -45,34 +42,49 @@ where GeneratorWitnessSimplifiedType(usize), OpaqueSimplifiedType(D), FunctionSimplifiedType(usize), - ParameterSimplifiedType, + PlaceholderSimplifiedType, } +/// Generic parameters are pretty much just bound variables, e.g. +/// the type of `fn foo<'a, T>(x: &'a T) -> u32 { ... }` can be thought of as +/// `for<'a, T> fn(&'a T) -> u32`. +/// +/// Typecheck of `foo` has to succeed for all possible generic arguments, so +/// during typeck, we have to treat its generic parameters as if they +/// were placeholders. +/// +/// But when calling `foo` we only have to provide a specific generic argument. +/// In that case the generic parameters are instantiated with inference variables. +/// As we use `simplify_type` before that instantiation happens, we just treat +/// generic parameters as if they were inference variables in that case. #[derive(PartialEq, Eq, Debug, Clone, Copy)] pub enum TreatParams { - /// Treat parameters as bound types in the given environment. + /// Treat parameters as placeholders in the given environment. /// - /// For this to be correct the input has to be fully normalized - /// in its param env as it may otherwise cause us to ignore - /// potentially applying impls. - AsBoundTypes, - AsPlaceholders, + /// Note that this also causes us to treat projections as if they were + /// placeholders. This is only correct if the given projection cannot + /// be normalized in the current context. Even if normalization fails, + /// it may still succeed later if the projection contains any inference + /// variables. + AsPlaceholder, + AsInfer, } /// Tries to simplify a type by only returning the outermost injective¹ layer, if one exists. /// /// The idea is to get something simple that we can use to quickly decide if two types could unify, -/// for example during method lookup. +/// for example during method lookup. If this function returns `Some(x)` it can only unify with +/// types for which this method returns either `Some(x)` as well or `None`. /// /// A special case here are parameters and projections, which are only injective -/// if they are treated as bound types. +/// if they are treated as placeholders. /// /// For example when storing impls based on their simplified self type, we treat -/// generic parameters as placeholders. We must not simplify them here, +/// generic parameters as if they were inference variables. We must not simplify them here, /// as they can unify with any other type. /// -/// With projections we have to be even more careful, as even when treating them as bound types -/// this is still only correct if they are fully normalized. +/// With projections we have to be even more careful, as treating them as placeholders +/// is only correct if they are fully normalized. /// /// ¹ meaning that if the outermost layers are different, then the whole types are also different. pub fn simplify_type<'tcx>( @@ -104,20 +116,25 @@ pub fn simplify_type<'tcx>( ty::Never => Some(NeverSimplifiedType), ty::Tuple(tys) => Some(TupleSimplifiedType(tys.len())), ty::FnPtr(f) => Some(FunctionSimplifiedType(f.skip_binder().inputs().len())), - ty::Param(_) | ty::Projection(_) => match treat_params { - // When treated as bound types, projections don't unify with - // anything as long as they are fully normalized. + ty::Placeholder(..) => Some(PlaceholderSimplifiedType), + ty::Param(_) => match treat_params { + TreatParams::AsPlaceholder => Some(PlaceholderSimplifiedType), + TreatParams::AsInfer => None, + }, + ty::Projection(_) => match treat_params { + // When treating `ty::Param` as a placeholder, projections also + // don't unify with anything else as long as they are fully normalized. // // We will have to be careful with lazy normalization here. - TreatParams::AsBoundTypes => { - debug!("treating `{}` as a bound type", ty); - Some(ParameterSimplifiedType) + TreatParams::AsPlaceholder if !ty.has_infer_types_or_consts() => { + debug!("treating `{}` as a placeholder", ty); + Some(PlaceholderSimplifiedType) } - TreatParams::AsPlaceholders => None, + TreatParams::AsPlaceholder | TreatParams::AsInfer => None, }, ty::Opaque(def_id, _) => Some(OpaqueSimplifiedType(def_id)), ty::Foreign(def_id) => Some(ForeignSimplifiedType(def_id)), - ty::Placeholder(..) | ty::Bound(..) | ty::Infer(_) | ty::Error(_) => None, + ty::Bound(..) | ty::Infer(_) | ty::Error(_) => None, } } @@ -161,41 +178,7 @@ impl<D: Copy + Debug + Eq> SimplifiedTypeGen<D> { GeneratorWitnessSimplifiedType(n) => GeneratorWitnessSimplifiedType(n), OpaqueSimplifiedType(d) => OpaqueSimplifiedType(map(d)), FunctionSimplifiedType(n) => FunctionSimplifiedType(n), - ParameterSimplifiedType => ParameterSimplifiedType, - } - } -} - -impl<'a, D> HashStable<StableHashingContext<'a>> for SimplifiedTypeGen<D> -where - D: Copy + Debug + Eq + HashStable<StableHashingContext<'a>>, -{ - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - mem::discriminant(self).hash_stable(hcx, hasher); - match *self { - BoolSimplifiedType - | CharSimplifiedType - | StrSimplifiedType - | ArraySimplifiedType - | SliceSimplifiedType - | NeverSimplifiedType - | ParameterSimplifiedType - | MarkerTraitObjectSimplifiedType => { - // nothing to do - } - RefSimplifiedType(m) | PtrSimplifiedType(m) => m.hash_stable(hcx, hasher), - IntSimplifiedType(t) => t.hash_stable(hcx, hasher), - UintSimplifiedType(t) => t.hash_stable(hcx, hasher), - FloatSimplifiedType(t) => t.hash_stable(hcx, hasher), - AdtSimplifiedType(d) => d.hash_stable(hcx, hasher), - TupleSimplifiedType(n) => n.hash_stable(hcx, hasher), - TraitSimplifiedType(d) => d.hash_stable(hcx, hasher), - ClosureSimplifiedType(d) => d.hash_stable(hcx, hasher), - GeneratorSimplifiedType(d) => d.hash_stable(hcx, hasher), - GeneratorWitnessSimplifiedType(n) => n.hash_stable(hcx, hasher), - OpaqueSimplifiedType(d) => d.hash_stable(hcx, hasher), - FunctionSimplifiedType(n) => n.hash_stable(hcx, hasher), - ForeignSimplifiedType(d) => d.hash_stable(hcx, hasher), + PlaceholderSimplifiedType => PlaceholderSimplifiedType, } } } diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index 075928c889d..a2a450d76f1 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -41,7 +41,7 @@ //! //! For example, if you have `struct S(Ty, U)` where `S: TypeFoldable` and `U: //! TypeFoldable`, and an instance `S(ty, u)`, it would be visited like so: -//! ``` +//! ```text //! s.visit_with(visitor) calls //! - s.super_visit_with(visitor) calls //! - ty.visit_with(visitor) calls @@ -486,13 +486,13 @@ impl<'tcx> TyCtxt<'tcx> { /// traversed. If we encounter a bound region bound by this /// binder or one outer to it, it appears free. Example: /// - /// ``` - /// for<'a> fn(for<'b> fn(), T) - /// ^ ^ ^ ^ - /// | | | | here, would be shifted in 1 - /// | | | here, would be shifted in 2 - /// | | here, would be `INNERMOST` shifted in by 1 - /// | here, initially, binder would be `INNERMOST` + /// ```ignore (illustrative) + /// for<'a> fn(for<'b> fn(), T) + /// // ^ ^ ^ ^ + /// // | | | | here, would be shifted in 1 + /// // | | | here, would be shifted in 2 + /// // | | here, would be `INNERMOST` shifted in by 1 + /// // | here, initially, binder would be `INNERMOST` /// ``` /// /// You see that, initially, *any* bound value is free, @@ -1349,7 +1349,7 @@ impl<'tcx> TypeVisitor<'tcx> for LateBoundRegionsCollector { // ignore the inputs to a projection, as they may not appear // in the normalized form if self.just_constrained { - if let ty::Projection(..) | ty::Opaque(..) = t.kind() { + if let ty::Projection(..) = t.kind() { return ControlFlow::CONTINUE; } } diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index 0bd96f8f865..d9b82ee0a76 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -1,6 +1,7 @@ use crate::middle::resolve_lifetime::ObjectLifetimeDefault; use crate::ty; use crate::ty::subst::{Subst, SubstsRef}; +use crate::ty::EarlyBinder; use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; @@ -229,7 +230,11 @@ impl<'tcx> GenericPredicates<'tcx> { substs: SubstsRef<'tcx>, ) -> InstantiatedPredicates<'tcx> { InstantiatedPredicates { - predicates: self.predicates.iter().map(|(p, _)| p.subst(tcx, substs)).collect(), + predicates: self + .predicates + .iter() + .map(|(p, _)| EarlyBinder(*p).subst(tcx, substs)) + .collect(), spans: self.predicates.iter().map(|(_, sp)| *sp).collect(), } } @@ -243,7 +248,9 @@ impl<'tcx> GenericPredicates<'tcx> { if let Some(def_id) = self.parent { tcx.predicates_of(def_id).instantiate_into(tcx, instantiated, substs); } - instantiated.predicates.extend(self.predicates.iter().map(|(p, _)| p.subst(tcx, substs))); + instantiated + .predicates + .extend(self.predicates.iter().map(|(p, _)| EarlyBinder(*p).subst(tcx, substs))); instantiated.spans.extend(self.predicates.iter().map(|(_, sp)| *sp)); } diff --git a/compiler/rustc_middle/src/ty/impls_ty.rs b/compiler/rustc_middle/src/ty/impls_ty.rs index 2009364b24e..65c9b1aed05 100644 --- a/compiler/rustc_middle/src/ty/impls_ty.rs +++ b/compiler/rustc_middle/src/ty/impls_ty.rs @@ -86,18 +86,16 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ty::subst::GenericArgKin // // In order to make it very unlikely for the sequence of bytes being hashed for // a `GenericArgKind::Type` to be the same as the sequence of bytes being - // hashed for one of the other variants, we hash a `0xFF` byte before hashing - // their discriminant (since the discriminant of `TyKind` is unlikely to ever start - // with 0xFF). + // hashed for one of the other variants, we hash some very high number instead + // of their actual discriminant since `TyKind` should never start with anything + // that high. ty::subst::GenericArgKind::Type(ty) => ty.hash_stable(hcx, hasher), ty::subst::GenericArgKind::Const(ct) => { - 0xFFu8.hash_stable(hcx, hasher); - mem::discriminant(self).hash_stable(hcx, hasher); + 0xF3u8.hash_stable(hcx, hasher); ct.hash_stable(hcx, hasher); } ty::subst::GenericArgKind::Lifetime(lt) => { - 0xFFu8.hash_stable(hcx, hasher); - mem::discriminant(self).hash_stable(hcx, hasher); + 0xF5u8.hash_stable(hcx, hasher); lt.hash_stable(hcx, hasher); } } diff --git a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs index 226456588e7..b8088766cca 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs @@ -56,7 +56,9 @@ impl<'tcx> TyCtxt<'tcx> { /// Checks whether a type is visibly uninhabited from a particular module. /// /// # Example - /// ```rust + /// ``` + /// #![feature(never_type)] + /// # fn main() {} /// enum Void {} /// mod a { /// pub mod b { @@ -67,6 +69,7 @@ impl<'tcx> TyCtxt<'tcx> { /// } /// /// mod c { + /// use super::Void; /// pub struct AlsoSecretlyUninhabited { /// _priv: Void, /// } @@ -84,7 +87,7 @@ impl<'tcx> TyCtxt<'tcx> { /// contain `Foo`. /// /// # Example - /// ```rust + /// ```ignore (illustrative) /// let foo_result: Result<T, Foo> = ... ; /// let Ok(t) = foo_result; /// ``` diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 00b3639a997..f088db00d02 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -1,13 +1,14 @@ use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::ty::print::{FmtPrinter, Printer}; use crate::ty::subst::{InternalSubsts, Subst}; -use crate::ty::{self, SubstsRef, Ty, TyCtxt, TypeFoldable}; +use crate::ty::{self, EarlyBinder, SubstsRef, Ty, TyCtxt, TypeFoldable}; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::Namespace; use rustc_hir::def_id::{CrateNum, DefId}; use rustc_hir::lang_items::LangItem; use rustc_macros::HashStable; use rustc_middle::ty::normalize_erasing_regions::NormalizationError; +use rustc_span::Symbol; use std::fmt; @@ -185,8 +186,8 @@ impl<'tcx> InstanceDef<'tcx> { } #[inline] - pub fn attrs(&self, tcx: TyCtxt<'tcx>) -> ty::Attributes<'tcx> { - tcx.get_attrs(self.def_id()) + pub fn get_attrs(&self, tcx: TyCtxt<'tcx>, attr: Symbol) -> ty::Attributes<'tcx> { + tcx.get_attrs(self.def_id(), attr) } /// Returns `true` if the LLVM version of this instance is unconditionally @@ -246,7 +247,7 @@ impl<'tcx> InstanceDef<'tcx> { match *self { InstanceDef::Item(ty::WithOptConstParam { did: def_id, .. }) | InstanceDef::Virtual(def_id, _) => { - tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::TRACK_CALLER) + tcx.body_codegen_attrs(def_id).flags.contains(CodegenFnAttrFlags::TRACK_CALLER) } InstanceDef::ClosureOnceShim { call_once: _, track_caller } => track_caller, _ => false, @@ -337,7 +338,7 @@ impl<'tcx> Instance<'tcx> { /// Returns `Ok(None)` if we cannot resolve `Instance` to a specific instance. /// For example, in a context like this, /// - /// ``` + /// ```ignore (illustrative) /// fn foo<T: Debug>(t: T) { ... } /// ``` /// @@ -557,7 +558,11 @@ impl<'tcx> Instance<'tcx> { where T: TypeFoldable<'tcx> + Copy, { - if let Some(substs) = self.substs_for_mir_body() { v.subst(tcx, substs) } else { *v } + if let Some(substs) = self.substs_for_mir_body() { + EarlyBinder(*v).subst(tcx, substs) + } else { + *v + } } #[inline(always)] diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index cd4b23fca39..a7488fd44cd 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -2,10 +2,11 @@ use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::mir::{GeneratorLayout, GeneratorSavedLocal}; use crate::ty::normalize_erasing_regions::NormalizationError; use crate::ty::subst::Subst; -use crate::ty::{self, subst::SubstsRef, ReprOptions, Ty, TyCtxt, TypeFoldable}; +use crate::ty::{self, subst::SubstsRef, EarlyBinder, ReprOptions, Ty, TyCtxt, TypeFoldable}; use rustc_ast as ast; use rustc_attr as attr; use rustc_hir as hir; +use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_index::bit_set::BitSet; use rustc_index::vec::{Idx, IndexVec}; @@ -220,6 +221,111 @@ impl<'tcx> fmt::Display for LayoutError<'tcx> { } } +/// Enforce some basic invariants on layouts. +fn sanity_check_layout<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + layout: &TyAndLayout<'tcx>, +) { + // Type-level uninhabitedness should always imply ABI uninhabitedness. + if tcx.conservative_is_privately_uninhabited(param_env.and(layout.ty)) { + assert!(layout.abi.is_uninhabited()); + } + + if cfg!(debug_assertions) { + fn check_layout_abi<'tcx>(tcx: TyCtxt<'tcx>, layout: Layout<'tcx>) { + match layout.abi() { + Abi::Scalar(_scalar) => { + // No padding in scalars. + /* FIXME(#96185): + assert_eq!( + layout.align().abi, + scalar.align(&tcx).abi, + "alignment mismatch between ABI and layout in {layout:#?}" + ); + assert_eq!( + layout.size(), + scalar.size(&tcx), + "size mismatch between ABI and layout in {layout:#?}" + );*/ + } + Abi::Vector { count, element } => { + // No padding in vectors. Alignment can be strengthened, though. + assert!( + layout.align().abi >= element.align(&tcx).abi, + "alignment mismatch between ABI and layout in {layout:#?}" + ); + let size = element.size(&tcx) * count; + assert_eq!( + layout.size(), + size.align_to(tcx.data_layout().vector_align(size).abi), + "size mismatch between ABI and layout in {layout:#?}" + ); + } + Abi::ScalarPair(scalar1, scalar2) => { + // Sanity-check scalar pairs. These are a bit more flexible and support + // padding, but we can at least ensure both fields actually fit into the layout + // and the alignment requirement has not been weakened. + let align1 = scalar1.align(&tcx).abi; + let align2 = scalar2.align(&tcx).abi; + assert!( + layout.align().abi >= cmp::max(align1, align2), + "alignment mismatch between ABI and layout in {layout:#?}", + ); + let field2_offset = scalar1.size(&tcx).align_to(align2); + assert!( + layout.size() >= field2_offset + scalar2.size(&tcx), + "size mismatch between ABI and layout in {layout:#?}" + ); + } + Abi::Uninhabited | Abi::Aggregate { .. } => {} // Nothing to check. + } + } + + check_layout_abi(tcx, layout.layout); + + if let Variants::Multiple { variants, .. } = &layout.variants { + for variant in variants { + check_layout_abi(tcx, *variant); + // No nested "multiple". + assert!(matches!(variant.variants(), Variants::Single { .. })); + // Skip empty variants. + if variant.size() == Size::ZERO + || variant.fields().count() == 0 + || variant.abi().is_uninhabited() + { + // These are never actually accessed anyway, so we can skip them. (Note that + // sometimes, variants with fields have size 0, and sometimes, variants without + // fields have non-0 size.) + continue; + } + // Variants should have the same or a smaller size as the full thing. + if variant.size() > layout.size { + bug!( + "Type with size {} bytes has variant with size {} bytes: {layout:#?}", + layout.size.bytes(), + variant.size().bytes(), + ) + } + // The top-level ABI and the ABI of the variants should be coherent. + let abi_coherent = match (layout.abi, variant.abi()) { + (Abi::Scalar(..), Abi::Scalar(..)) => true, + (Abi::ScalarPair(..), Abi::ScalarPair(..)) => true, + (Abi::Uninhabited, _) => true, + (Abi::Aggregate { .. }, _) => true, + _ => false, + }; + if !abi_coherent { + bug!( + "Variant ABI is incompatible with top-level ABI:\nvariant={:#?}\nTop-level: {layout:#?}", + variant + ); + } + } + } + } +} + #[instrument(skip(tcx, query), level = "debug")] fn layout_of<'tcx>( tcx: TyCtxt<'tcx>, @@ -263,10 +369,7 @@ fn layout_of<'tcx>( cx.record_layout_for_printing(layout); - // Type-level uninhabitedness should always imply ABI uninhabitedness. - if tcx.conservative_is_privately_uninhabited(param_env.and(ty)) { - assert!(layout.abi.is_uninhabited()); - } + sanity_check_layout(tcx, param_env, &layout); Ok(layout) }) @@ -1313,9 +1416,11 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { }; let mut abi = Abi::Aggregate { sized: true }; - // Without latter check aligned enums with custom discriminant values - // Would result in ICE see the issue #92464 for more info - if tag.size(dl) == size || variants.iter().all(|layout| layout.is_empty()) { + if layout_variants.iter().all(|v| v.abi.is_uninhabited()) { + abi = Abi::Uninhabited; + } else if tag.size(dl) == size || variants.iter().all(|layout| layout.is_empty()) { + // Without latter check aligned enums with custom discriminant values + // Would result in ICE see the issue #92464 for more info abi = Abi::Scalar(tag); } else { // Try to use a ScalarPair for all tagged enums. @@ -1389,8 +1494,22 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { } } - if layout_variants.iter().all(|v| v.abi.is_uninhabited()) { - abi = Abi::Uninhabited; + // If we pick a "clever" (by-value) ABI, we might have to adjust the ABI of the + // variants to ensure they are consistent. This is because a downcast is + // semantically a NOP, and thus should not affect layout. + if matches!(abi, Abi::Scalar(..) | Abi::ScalarPair(..)) { + for variant in &mut layout_variants { + // We only do this for variants with fields; the others are not accessed anyway. + // Also do not overwrite any already existing "clever" ABIs. + if variant.fields.count() > 0 + && matches!(variant.abi, Abi::Aggregate { .. }) + { + variant.abi = abi; + // Also need to bump up the size and alignment, so that the entire value fits in here. + variant.size = cmp::max(variant.size, size); + variant.align.abi = cmp::max(variant.align.abi, align.abi); + } + } } let largest_niche = Niche::from_scalar(dl, Size::ZERO, tag); @@ -1587,7 +1706,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { ) -> Result<Layout<'tcx>, LayoutError<'tcx>> { use SavedLocalEligibility::*; let tcx = self.tcx; - let subst_field = |ty: Ty<'tcx>| ty.subst(tcx, substs); + let subst_field = |ty: Ty<'tcx>| EarlyBinder(ty).subst(tcx, substs); let Some(info) = tcx.generator_layout(def_id) else { return Err(LayoutError::Unknown(ty)); @@ -2631,7 +2750,7 @@ impl<'tcx> ty::Instance<'tcx> { // track of a polymorphization `ParamEnv` to allow normalizing later. let mut sig = match *ty.kind() { ty::FnDef(def_id, substs) => tcx - .normalize_erasing_regions(tcx.param_env(def_id), tcx.fn_sig(def_id)) + .normalize_erasing_regions(tcx.param_env(def_id), tcx.bound_fn_sig(def_id)) .subst(tcx, substs), _ => unreachable!(), }; @@ -2762,14 +2881,30 @@ impl<'tcx> ty::Instance<'tcx> { /// with `-Cpanic=abort` will look like they can't unwind when in fact they /// might (from a foreign exception or similar). #[inline] -pub fn fn_can_unwind<'tcx>( - tcx: TyCtxt<'tcx>, - codegen_fn_attr_flags: CodegenFnAttrFlags, - abi: SpecAbi, -) -> bool { - // Special attribute for functions which can't unwind. - if codegen_fn_attr_flags.contains(CodegenFnAttrFlags::NEVER_UNWIND) { - return false; +pub fn fn_can_unwind<'tcx>(tcx: TyCtxt<'tcx>, fn_def_id: Option<DefId>, abi: SpecAbi) -> bool { + if let Some(did) = fn_def_id { + // Special attribute for functions which can't unwind. + if tcx.codegen_fn_attrs(did).flags.contains(CodegenFnAttrFlags::NEVER_UNWIND) { + return false; + } + + // With `-C panic=abort`, all non-FFI functions are required to not unwind. + // + // Note that this is true regardless ABI specified on the function -- a `extern "C-unwind"` + // function defined in Rust is also required to abort. + if tcx.sess.panic_strategy() == PanicStrategy::Abort && !tcx.is_foreign_item(did) { + return false; + } + + // With -Z panic-in-drop=abort, drop_in_place never unwinds. + // + // This is not part of `codegen_fn_attrs` as it can differ between crates + // and therefore cannot be computed in core. + if tcx.sess.opts.debugging_opts.panic_in_drop == PanicStrategy::Abort { + if Some(did) == tcx.lang_items().drop_in_place_fn() { + return false; + } + } } // Otherwise if this isn't special then unwinding is generally determined by @@ -2991,13 +3126,7 @@ fn fn_abi_of_fn_ptr<'tcx>( ) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, FnAbiError<'tcx>> { let (param_env, (sig, extra_args)) = query.into_parts(); - LayoutCx { tcx, param_env }.fn_abi_new_uncached( - sig, - extra_args, - None, - CodegenFnAttrFlags::empty(), - false, - ) + LayoutCx { tcx, param_env }.fn_abi_new_uncached(sig, extra_args, None, None, false) } fn fn_abi_of_instance<'tcx>( @@ -3014,13 +3143,11 @@ fn fn_abi_of_instance<'tcx>( None }; - let attrs = tcx.codegen_fn_attrs(instance.def_id()).flags; - LayoutCx { tcx, param_env }.fn_abi_new_uncached( sig, extra_args, caller_location, - attrs, + Some(instance.def_id()), matches!(instance.def, ty::InstanceDef::Virtual(..)), ) } @@ -3033,7 +3160,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { sig: ty::PolyFnSig<'tcx>, extra_args: &[Ty<'tcx>], caller_location: Option<Ty<'tcx>>, - codegen_fn_attr_flags: CodegenFnAttrFlags, + fn_def_id: Option<DefId>, // FIXME(eddyb) replace this with something typed, like an `enum`. force_thin_self_ptr: bool, ) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, FnAbiError<'tcx>> { @@ -3205,7 +3332,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { c_variadic: sig.c_variadic, fixed_count: inputs.len(), conv, - can_unwind: fn_can_unwind(self.tcx(), codegen_fn_attr_flags, sig.abi), + can_unwind: fn_can_unwind(self.tcx(), fn_def_id, sig.abi), }; self.fn_abi_adjust_for_abi(&mut fn_abi, sig.abi)?; debug!("fn_abi_new_uncached = {:?}", fn_abi); diff --git a/compiler/rustc_middle/src/ty/list.rs b/compiler/rustc_middle/src/ty/list.rs index 197dc9205b4..3c9e96df59a 100644 --- a/compiler/rustc_middle/src/ty/list.rs +++ b/compiler/rustc_middle/src/ty/list.rs @@ -122,13 +122,6 @@ impl<S: Encoder, T: Encodable<S>> Encodable<S> for List<T> { } } -impl<S: Encoder, T: Encodable<S>> Encodable<S> for &List<T> { - #[inline] - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - (**self).encode(s) - } -} - impl<T: PartialEq> PartialEq for List<T> { #[inline] fn eq(&self, other: &List<T>) -> bool { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index af9216a990a..775b06c6c38 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -14,12 +14,6 @@ pub use self::AssocItemContainer::*; pub use self::BorrowKind::*; pub use self::IntVarValue::*; pub use self::Variance::*; -pub use adt::*; -pub use assoc::*; -pub use generics::*; -use rustc_data_structures::fingerprint::Fingerprint; -pub use vtable::*; - use crate::metadata::ModChild; use crate::middle::privacy::AccessLevels; use crate::mir::{Body, GeneratorLayout}; @@ -28,8 +22,12 @@ use crate::ty; use crate::ty::fast_reject::SimplifiedType; use crate::ty::subst::{GenericArg, InternalSubsts, Subst, SubstsRef}; use crate::ty::util::Discr; +pub use adt::*; +pub use assoc::*; +pub use generics::*; use rustc_ast as ast; use rustc_attr as attr; +use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::intern::{Interned, WithStableHash}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; @@ -44,6 +42,7 @@ use rustc_session::cstore::CrateStoreDyn; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; use rustc_target::abi::Align; +pub use vtable::*; use std::fmt::Debug; use std::hash::Hash; @@ -73,12 +72,13 @@ pub use self::context::{ }; pub use self::instance::{Instance, InstanceDef}; pub use self::list::List; +pub use self::rvalue_scopes::RvalueScopes; pub use self::sty::BoundRegionKind::*; pub use self::sty::RegionKind::*; pub use self::sty::TyKind::*; pub use self::sty::{ Binder, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, BoundVar, BoundVariableKind, - CanonicalPolyFnSig, ClosureSubsts, ClosureSubstsParts, ConstVid, EarlyBoundRegion, + CanonicalPolyFnSig, ClosureSubsts, ClosureSubstsParts, ConstVid, EarlyBinder, EarlyBoundRegion, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FnSig, FreeRegion, GenSig, GeneratorSubsts, GeneratorSubstsParts, InlineConstSubsts, InlineConstSubstsParts, ParamConst, ParamTy, PolyExistentialProjection, PolyExistentialTraitRef, PolyFnSig, PolyGenSig, @@ -119,6 +119,7 @@ mod generics; mod impls_ty; mod instance; mod list; +mod rvalue_scopes; mod structural_impls; mod sty; @@ -228,7 +229,7 @@ impl fmt::Display for ImplPolarity { } } -#[derive(Clone, Debug, PartialEq, Eq, Copy, Hash, TyEncodable, TyDecodable, HashStable)] +#[derive(Clone, Debug, PartialEq, Eq, Copy, Hash, Encodable, Decodable, HashStable)] pub enum Visibility { /// Visible everywhere (including in other crates). Public, @@ -410,7 +411,7 @@ pub struct CReaderCacheKey { /// of the relevant methods. #[derive(PartialEq, Eq, PartialOrd, Ord)] #[allow(rustc::usage_of_ty_tykind)] -crate struct TyS<'tcx> { +pub(crate) struct TyS<'tcx> { /// This field shouldn't be used directly and may be removed in the future. /// Use `Ty::kind()` instead. kind: TyKind<'tcx>, @@ -501,7 +502,7 @@ impl ty::EarlyBoundRegion { /// See comments on `TyS`, which apply here too (albeit for /// `PredicateS`/`Predicate` rather than `TyS`/`Ty`). #[derive(Debug)] -crate struct PredicateS<'tcx> { +pub(crate) struct PredicateS<'tcx> { kind: Binder<'tcx, PredicateKind<'tcx>>, flags: TypeFlags, /// See the comment for the corresponding field of [TyS]. @@ -736,7 +737,7 @@ impl<'tcx> Predicate<'tcx> { let shifted_pred = tcx.shift_bound_var_indices(trait_bound_vars.len(), bound_pred.skip_binder()); // 2) Self: Bar1<'a, '^0.1> -> T: Bar1<'^0.0, '^0.1> - let new = shifted_pred.subst(tcx, trait_ref.skip_binder().substs); + let new = EarlyBinder(shifted_pred).subst(tcx, trait_ref.skip_binder().substs); // 3) ['x] + ['b] -> ['x, 'b] let bound_vars = tcx.mk_bound_variable_kinds(trait_bound_vars.iter().chain(pred_bound_vars)); @@ -1030,9 +1031,9 @@ impl<'tcx> Predicate<'tcx> { /// their values. /// /// Example: -/// -/// struct Foo<T, U: Bar<T>> { ... } -/// +/// ```ignore (illustrative) +/// struct Foo<T, U: Bar<T>> { ... } +/// ``` /// Here, the `GenericPredicates` for `Foo` would contain a list of bounds like /// `[[], [U:Bar<T>]]`. Now if there were some particular reference /// like `Foo<isize,usize>`, then the `InstantiatedPredicates` would be `[[], @@ -1090,9 +1091,9 @@ pub struct OpaqueHiddenType<'tcx> { /// The type variable that represents the value of the opaque type /// that we require. In other words, after we compile this function, /// we will be created a constraint like: - /// - /// Foo<'a, T> = ?C - /// + /// ```ignore (pseudo-rust) + /// Foo<'a, T> = ?C + /// ``` /// where `?C` is the value of this type variable. =) It may /// naturally refer to the type and lifetime parameters in scope /// in this function, though ultimately it should only reference @@ -1133,7 +1134,7 @@ rustc_index::newtype_index! { /// /// To make this more concrete, consider this program: /// - /// ``` + /// ```ignore (illustrative) /// struct Foo { } /// fn bar<T>(x: T) { /// let y: for<'a> fn(&'a u8, Foo) = ...; @@ -1172,7 +1173,7 @@ impl UniverseIndex { /// corresponds to entering a `forall` quantifier. So, for /// example, suppose we have this type in universe `U`: /// - /// ``` + /// ```ignore (illustrative) /// for<'a> fn(&'a u32) /// ``` /// @@ -1629,7 +1630,7 @@ where } } -#[derive(Copy, Clone, Debug, HashStable)] +#[derive(Copy, Clone, Debug, HashStable, Encodable, Decodable)] pub struct Destructor { /// The `DefId` of the destructor method pub did: DefId, @@ -1818,8 +1819,8 @@ impl ReprOptions { field_shuffle_seed ^= user_seed; } - for attr in tcx.get_attrs(did).iter() { - for r in attr::find_repr_attrs(&tcx.sess, attr) { + for attr in tcx.get_attrs(did, sym::repr) { + for r in attr::parse_repr_attr(&tcx.sess, attr) { flags.insert(match r { attr::ReprC => ReprFlags::IS_C, attr::ReprPacked(pack) => { @@ -1932,7 +1933,7 @@ impl<'tcx> FieldDef { /// Returns the type of this field. The resulting type is not normalized. The `subst` is /// typically obtained via the second field of [`TyKind::Adt`]. pub fn ty(&self, tcx: TyCtxt<'tcx>, subst: SubstsRef<'tcx>) -> Ty<'tcx> { - tcx.type_of(self.did).subst(tcx, subst) + tcx.bound_type_of(self.did).subst(tcx, subst) } /// Computes the `Ident` of this variant by looking up the `Span` @@ -1941,8 +1942,7 @@ impl<'tcx> FieldDef { } } -pub type Attributes<'tcx> = &'tcx [ast::Attribute]; - +pub type Attributes<'tcx> = impl Iterator<Item = &'tcx ast::Attribute>; #[derive(Debug, PartialEq, Eq)] pub enum ImplOverlapKind { /// These impls are always allowed to overlap. @@ -1959,7 +1959,7 @@ pub enum ImplOverlapKind { /// The widely-used version 0.1.0 of the crate `traitobject` had accidentally relied /// that difference, making what reduces to the following set of impls: /// - /// ``` + /// ```compile_fail,(E0119) /// trait Trait {} /// impl Trait for dyn Send + Sync {} /// impl Trait for dyn Sync + Send {} @@ -1996,7 +1996,8 @@ impl<'tcx> TyCtxt<'tcx> { .filter(|item| item.kind == AssocKind::Fn && item.defaultness.has_value()) } - fn opt_item_name(self, def_id: DefId) -> Option<Symbol> { + /// Look up the name of a definition across crates. This does not look at HIR. + pub fn opt_item_name(self, def_id: DefId) -> Option<Symbol> { if let Some(cnum) = def_id.as_crate_root() { Some(self.crate_name(cnum)) } else { @@ -2016,16 +2017,11 @@ impl<'tcx> TyCtxt<'tcx> { /// Look up the name of a definition across crates. This does not look at HIR. /// - /// When possible, this function should be used for cross-crate lookups over - /// [`opt_item_name`] to avoid invalidating the incremental cache. If you - /// need to handle items without a name, or HIR items that will not be - /// serialized cross-crate, or if you need the span of the item, use + /// This method will ICE if the corresponding item does not have a name. In these cases, use /// [`opt_item_name`] instead. /// /// [`opt_item_name`]: Self::opt_item_name pub fn item_name(self, id: DefId) -> Symbol { - // Look at cross-crate items first to avoid invalidating the incremental cache - // unless we have to. self.opt_item_name(id).unwrap_or_else(|| { bug!("item_name: no name for {:?}", self.def_path(id)); }) @@ -2186,8 +2182,8 @@ impl<'tcx> TyCtxt<'tcx> { } } - /// Gets the attributes of a definition. - pub fn get_attrs(self, did: DefId) -> Attributes<'tcx> { + // FIXME(@lcnr): Remove this function. + pub fn get_attrs_unchecked(self, did: DefId) -> &'tcx [ast::Attribute] { if let Some(did) = did.as_local() { self.hir().attrs(self.hir().local_def_id_to_hir_id(did)) } else { @@ -2195,9 +2191,29 @@ impl<'tcx> TyCtxt<'tcx> { } } + /// Gets all attributes with the given name. + pub fn get_attrs(self, did: DefId, attr: Symbol) -> ty::Attributes<'tcx> { + let filter_fn = move |a: &&ast::Attribute| a.has_name(attr); + if let Some(did) = did.as_local() { + self.hir().attrs(self.hir().local_def_id_to_hir_id(did)).iter().filter(filter_fn) + } else if cfg!(debug_assertions) && rustc_feature::is_builtin_only_local(attr) { + bug!("tried to access the `only_local` attribute `{}` from an extern crate", attr); + } else { + self.item_attrs(did).iter().filter(filter_fn) + } + } + + pub fn get_attr(self, did: DefId, attr: Symbol) -> Option<&'tcx ast::Attribute> { + self.get_attrs(did, attr).next() + } + /// Determines whether an item is annotated with an attribute. pub fn has_attr(self, did: DefId, attr: Symbol) -> bool { - self.sess.contains_name(&self.get_attrs(did), attr) + if cfg!(debug_assertions) && !did.is_local() && rustc_feature::is_builtin_only_local(attr) { + bug!("tried to access the `only_local` attribute `{}` from an extern crate", attr); + } else { + self.get_attrs(did, attr).next().is_some() + } } /// Returns `true` if this is an `auto trait`. diff --git a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs index 808be446b2a..9bbbd7e2f7c 100644 --- a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs +++ b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs @@ -11,7 +11,7 @@ use crate::mir; use crate::traits::query::NoSolution; use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder}; use crate::ty::subst::{Subst, SubstsRef}; -use crate::ty::{self, Ty, TyCtxt}; +use crate::ty::{self, EarlyBinder, Ty, TyCtxt}; #[derive(Debug, Copy, Clone, HashStable, TyEncodable, TyDecodable)] pub enum NormalizationError<'tcx> { @@ -133,7 +133,7 @@ impl<'tcx> TyCtxt<'tcx> { param_env={:?})", param_substs, value, param_env, ); - let substituted = value.subst(self, param_substs); + let substituted = EarlyBinder(value).subst(self, param_substs); self.normalize_erasing_regions(param_env, substituted) } @@ -157,7 +157,7 @@ impl<'tcx> TyCtxt<'tcx> { param_env={:?})", param_substs, value, param_env, ); - let substituted = value.subst(self, param_substs); + let substituted = EarlyBinder(value).subst(self, param_substs); self.try_normalize_erasing_regions(param_env, substituted) } } diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index 3fe68f723ec..9d8124eb25d 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -115,12 +115,16 @@ pub trait Printer<'tcx>: Sized { DefPathData::Impl => { let generics = self.tcx().generics_of(def_id); - let mut self_ty = self.tcx().type_of(def_id); - let mut impl_trait_ref = self.tcx().impl_trait_ref(def_id); - if substs.len() >= generics.count() { - self_ty = self_ty.subst(self.tcx(), substs); - impl_trait_ref = impl_trait_ref.subst(self.tcx(), substs); - } + let self_ty = self.tcx().bound_type_of(def_id); + let impl_trait_ref = self.tcx().bound_impl_trait_ref(def_id); + let (self_ty, impl_trait_ref) = if substs.len() >= generics.count() { + ( + self_ty.subst(self.tcx(), substs), + impl_trait_ref.map(|i| i.subst(self.tcx(), substs)), + ) + } else { + (self_ty.0, impl_trait_ref.map(|i| i.0)) + }; self.print_impl_path(def_id, substs, self_ty, impl_trait_ref) } @@ -136,6 +140,10 @@ pub trait Printer<'tcx>: Sized { match key.disambiguated_data.data { // Closures' own generics are only captures, don't print them. DefPathData::ClosureExpr => {} + // This covers both `DefKind::AnonConst` and `DefKind::InlineConst`. + // Anon consts doesn't have their own generics, and inline consts' own + // generics are their inferred types, so don't print them. + DefPathData::AnonConst => {} // If we have any generic arguments to print, we do that // on top of the same path, but without its own generics. @@ -199,7 +207,7 @@ pub trait Printer<'tcx>: Sized { has_default && substs[param.index as usize] == GenericArg::from( - self.tcx().type_of(param.def_id).subst(self.tcx(), substs), + self.tcx().bound_type_of(param.def_id).subst(self.tcx(), substs), ) } ty::GenericParamDefKind::Const { has_default } => { diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 991666908f9..4c0bc2e4337 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -587,7 +587,7 @@ pub trait PrettyPrinter<'tcx>: p!(")") } ty::FnDef(def_id, substs) => { - let sig = self.tcx().fn_sig(def_id).subst(self.tcx(), substs); + let sig = self.tcx().bound_fn_sig(def_id).subst(self.tcx(), substs); p!(print(sig), " {{", print_value_path(def_id, substs), "}}"); } ty::FnPtr(ref bare_fn) => p!(print(bare_fn)), @@ -774,13 +774,13 @@ pub trait PrettyPrinter<'tcx>: // Grab the "TraitA + TraitB" from `impl TraitA + TraitB`, // by looking up the projections associated with the def_id. - let bounds = self.tcx().explicit_item_bounds(def_id); + let bounds = self.tcx().bound_explicit_item_bounds(def_id); let mut traits = BTreeMap::new(); let mut fn_traits = BTreeMap::new(); let mut is_sized = false; - for (predicate, _) in bounds { + for predicate in bounds.transpose_iter().map(|e| e.map_bound(|(p, _)| *p)) { let predicate = predicate.subst(self.tcx(), substs); let bound_predicate = predicate.kind(); @@ -2676,7 +2676,7 @@ fn for_each_def(tcx: TyCtxt<'_>, mut collect_fn: impl for<'b> FnMut(&'b Ident, N // Iterate all local crate items no matter where they are defined. let hir = tcx.hir(); for id in hir.items() { - if matches!(hir.def_kind(id.def_id), DefKind::Use) { + if matches!(tcx.def_kind(id.def_id), DefKind::Use) { continue; } diff --git a/compiler/rustc_middle/src/ty/query.rs b/compiler/rustc_middle/src/ty/query.rs index fb937ded65a..65f41c5266d 100644 --- a/compiler/rustc_middle/src/ty/query.rs +++ b/compiler/rustc_middle/src/ty/query.rs @@ -6,15 +6,16 @@ use crate::middle::codegen_fn_attrs::CodegenFnAttrs; use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; use crate::middle::lib_features::LibFeatures; use crate::middle::privacy::AccessLevels; -use crate::middle::region; use crate::middle::resolve_lifetime::{ LifetimeScopeForPath, ObjectLifetimeDefault, Region, ResolveLifetimes, }; use crate::middle::stability::{self, DeprecationEntry}; use crate::mir; use crate::mir::interpret::GlobalId; -use crate::mir::interpret::{ConstAlloc, LitToConstError, LitToConstInput}; -use crate::mir::interpret::{ConstValue, EvalToAllocationRawResult, EvalToConstValueResult}; +use crate::mir::interpret::{ + ConstValue, EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult, +}; +use crate::mir::interpret::{LitToConstError, LitToConstInput}; use crate::mir::mono::CodegenUnit; use crate::thir; use crate::traits::query::{ diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 4c1160e21fe..8677405eebe 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -159,7 +159,8 @@ pub fn relate_substs_with_variances<'tcx, R: TypeRelation<'tcx>>( let params = iter::zip(a_subst, b_subst).enumerate().map(|(i, (a, b))| { let variance = variances[i]; let variance_info = if variance == ty::Invariant { - let ty = *cached_ty.get_or_insert_with(|| tcx.type_of(ty_def_id).subst(tcx, a_subst)); + let ty = + *cached_ty.get_or_insert_with(|| tcx.bound_type_of(ty_def_id).subst(tcx, a_subst)); ty::VarianceDiagInfo::Invariant { ty, param_index: i.try_into().unwrap() } } else { ty::VarianceDiagInfo::default() diff --git a/compiler/rustc_middle/src/ty/rvalue_scopes.rs b/compiler/rustc_middle/src/ty/rvalue_scopes.rs new file mode 100644 index 00000000000..e86dafae338 --- /dev/null +++ b/compiler/rustc_middle/src/ty/rvalue_scopes.rs @@ -0,0 +1,57 @@ +use crate::middle::region::{Scope, ScopeData, ScopeTree}; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir as hir; + +/// `RvalueScopes` is a mapping from sub-expressions to _extended_ lifetime as determined by +/// rules laid out in `rustc_typeck::check::rvalue_scopes`. +#[derive(TyEncodable, TyDecodable, Clone, Debug, Default, Eq, PartialEq, HashStable)] +pub struct RvalueScopes { + map: FxHashMap<hir::ItemLocalId, Option<Scope>>, +} + +impl RvalueScopes { + pub fn new() -> Self { + Self { map: <_>::default() } + } + + /// Returns the scope when the temp created by `expr_id` will be cleaned up. + pub fn temporary_scope( + &self, + region_scope_tree: &ScopeTree, + expr_id: hir::ItemLocalId, + ) -> Option<Scope> { + // Check for a designated rvalue scope. + if let Some(&s) = self.map.get(&expr_id) { + debug!("temporary_scope({expr_id:?}) = {s:?} [custom]"); + return s; + } + + // Otherwise, locate the innermost terminating scope + // if there's one. Static items, for instance, won't + // have an enclosing scope, hence no scope will be + // returned. + let mut id = Scope { id: expr_id, data: ScopeData::Node }; + + while let Some(&(p, _)) = region_scope_tree.parent_map.get(&id) { + match p.data { + ScopeData::Destruction => { + debug!("temporary_scope({expr_id:?}) = {id:?} [enclosing]"); + return Some(id); + } + _ => id = p, + } + } + + debug!("temporary_scope({expr_id:?}) = None"); + None + } + + /// Make an association between a sub-expression and an extended lifetime + pub fn record_rvalue_scope(&mut self, var: hir::ItemLocalId, lifetime: Option<Scope>) { + debug!("record_rvalue_scope(var={var:?}, lifetime={lifetime:?})"); + if let Some(lifetime) = lifetime { + assert!(var != lifetime.item_local_id()); + } + self.map.insert(var, lifetime); + } +} diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 4ef6ff1835f..2c8cd4f933d 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -860,6 +860,27 @@ impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Box<[T]> { } } +impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for ty::EarlyBinder<T> { + fn try_super_fold_with<F: FallibleTypeFolder<'tcx>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error> { + self.try_map_bound(|ty| ty.try_fold_with(folder)) + } + + fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> { + self.try_map_bound(|ty| ty.try_fold_with(folder)) + } + + fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { + self.as_ref().0.visit_with(visitor) + } + + fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { + self.as_ref().0.visit_with(visitor) + } +} + impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for ty::Binder<'tcx, T> { fn try_super_fold_with<F: FallibleTypeFolder<'tcx>>( self, diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 0d55fe3a392..a973a5c9b50 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -187,12 +187,14 @@ pub enum TyKind<'tcx> { /// Looking at the following example, the witness for this generator /// may end up as something like `for<'a> [Vec<i32>, &'a Vec<i32>]`: /// - /// ```rust + /// ```ignore UNSOLVED (ask @compiler-errors, should this error? can we just swap the yields?) + /// #![feature(generators)] /// |a| { /// let x = &vec![3]; /// yield a; /// yield x[0]; /// } + /// # ; /// ``` GeneratorWitness(Binder<'tcx, &'tcx List<Ty<'tcx>>>), @@ -276,9 +278,9 @@ impl<'tcx> TyKind<'tcx> { static_assert_size!(TyKind<'_>, 32); /// A closure can be modeled as a struct that looks like: -/// -/// struct Closure<'l0...'li, T0...Tj, CK, CS, U>(...U); -/// +/// ```ignore (illustrative) +/// struct Closure<'l0...'li, T0...Tj, CK, CS, U>(...U); +/// ``` /// where: /// /// - 'l0...'li and T0...Tj are the generic parameters @@ -295,25 +297,25 @@ static_assert_size!(TyKind<'_>, 32); /// and the up-var has the type `Foo`, then that field of U will be `&Foo`). /// /// So, for example, given this function: -/// -/// fn foo<'a, T>(data: &'a mut T) { -/// do(|| data.count += 1) -/// } -/// +/// ```ignore (illustrative) +/// fn foo<'a, T>(data: &'a mut T) { +/// do(|| data.count += 1) +/// } +/// ``` /// the type of the closure would be something like: -/// -/// struct Closure<'a, T, U>(...U); -/// +/// ```ignore (illustrative) +/// struct Closure<'a, T, U>(...U); +/// ``` /// Note that the type of the upvar is not specified in the struct. /// You may wonder how the impl would then be able to use the upvar, /// if it doesn't know it's type? The answer is that the impl is /// (conceptually) not fully generic over Closure but rather tied to /// instances with the expected upvar types: -/// -/// impl<'b, 'a, T> FnMut() for Closure<'a, T, (&'b mut &'a mut T,)> { -/// ... -/// } -/// +/// ```ignore (illustrative) +/// impl<'b, 'a, T> FnMut() for Closure<'a, T, (&'b mut &'a mut T,)> { +/// ... +/// } +/// ``` /// You can see that the *impl* fully specified the type of the upvar /// and thus knows full well that `data` has type `&'b mut &'a mut T`. /// (Here, I am assuming that `data` is mut-borrowed.) @@ -711,7 +713,9 @@ impl<'tcx> GeneratorSubsts<'tcx> { ) -> impl Iterator<Item = impl Iterator<Item = Ty<'tcx>> + Captures<'tcx>> { let layout = tcx.generator_layout(def_id).unwrap(); layout.variant_fields.iter().map(move |variant| { - variant.iter().map(move |field| layout.field_tys[*field].subst(tcx, self.substs)) + variant + .iter() + .map(move |field| EarlyBinder(layout.field_tys[*field]).subst(tcx, self.substs)) }) } @@ -760,9 +764,9 @@ impl<'tcx> UpvarSubsts<'tcx> { } /// An inline const is modeled like -/// -/// const InlineConst<'l0...'li, T0...Tj, R>: R; -/// +/// ```ignore (illustrative) +/// const InlineConst<'l0...'li, T0...Tj, R>: R; +/// ``` /// where: /// /// - 'l0...'li and T0...Tj are the generic parameters @@ -936,9 +940,9 @@ impl<'tcx> List<ty::Binder<'tcx, ExistentialPredicate<'tcx>>> { /// A complete reference to a trait. These take numerous guises in syntax, /// but perhaps the most recognizable form is in a where-clause: -/// -/// T: Foo<U> -/// +/// ```ignore (illustrative) +/// T: Foo<U> +/// ``` /// This would be represented by a trait-reference where the `DefId` is the /// `DefId` for the trait `Foo` and the substs define `T` as parameter 0, /// and `U` as parameter 1. @@ -1012,9 +1016,9 @@ impl<'tcx> PolyTraitRef<'tcx> { /// An existential reference to a trait, where `Self` is erased. /// For example, the trait object `Trait<'a, 'b, X, Y>` is: -/// -/// exists T. T: Trait<'a, 'b, X, Y> -/// +/// ```ignore (illustrative) +/// exists T. T: Trait<'a, 'b, X, Y> +/// ``` /// The substitutions don't include the erased `Self`, only trait /// type and lifetime parameters (`[X, Y]` and `['a, 'b]` above). #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] @@ -1066,6 +1070,69 @@ impl<'tcx> PolyExistentialTraitRef<'tcx> { } } +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[derive(Encodable, Decodable, HashStable)] +pub struct EarlyBinder<T>(pub T); + +impl<T> EarlyBinder<T> { + pub fn as_ref(&self) -> EarlyBinder<&T> { + EarlyBinder(&self.0) + } + + pub fn map_bound_ref<F, U>(&self, f: F) -> EarlyBinder<U> + where + F: FnOnce(&T) -> U, + { + self.as_ref().map_bound(f) + } + + pub fn map_bound<F, U>(self, f: F) -> EarlyBinder<U> + where + F: FnOnce(T) -> U, + { + let value = f(self.0); + EarlyBinder(value) + } + + pub fn try_map_bound<F, U, E>(self, f: F) -> Result<EarlyBinder<U>, E> + where + F: FnOnce(T) -> Result<U, E>, + { + let value = f(self.0)?; + Ok(EarlyBinder(value)) + } +} + +impl<T> EarlyBinder<Option<T>> { + pub fn transpose(self) -> Option<EarlyBinder<T>> { + self.0.map(|v| EarlyBinder(v)) + } +} + +impl<T, U> EarlyBinder<(T, U)> { + pub fn transpose_tuple2(self) -> (EarlyBinder<T>, EarlyBinder<U>) { + (EarlyBinder(self.0.0), EarlyBinder(self.0.1)) + } +} + +pub struct EarlyBinderIter<T> { + t: T, +} + +impl<T: IntoIterator> EarlyBinder<T> { + pub fn transpose_iter(self) -> EarlyBinderIter<T::IntoIter> { + EarlyBinderIter { t: self.0.into_iter() } + } +} + +impl<T: Iterator> Iterator for EarlyBinderIter<T> { + type Item = EarlyBinder<T::Item>; + + fn next(&mut self) -> Option<Self::Item> { + self.t.next().map(|i| EarlyBinder(i)) + } +} + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] #[derive(HashStable)] pub enum BoundVariableKind { @@ -1434,7 +1501,7 @@ impl<'tcx> fmt::Debug for Region<'tcx> { /// /// In general, the region lattice looks like /// -/// ``` +/// ```text /// static ----------+-----...------+ (greatest) /// | | | /// early-bound and | | @@ -1780,14 +1847,14 @@ impl<'tcx> Region<'tcx> { /// Given an early-bound or free region, returns the `DefId` where it was bound. /// For example, consider the regions in this snippet of code: /// - /// ``` + /// ```ignore (illustrative) /// impl<'a> Foo { - /// ^^ -- early bound, declared on an impl + /// // ^^ -- early bound, declared on an impl /// /// fn bar<'b, 'c>(x: &self, y: &'b u32, z: &'c u64) where 'static: 'c - /// ^^ ^^ ^ anonymous, late-bound - /// | early-bound, appears in where-clauses - /// late-bound, appears only in fn args + /// // ^^ ^^ ^ anonymous, late-bound + /// // | early-bound, appears in where-clauses + /// // late-bound, appears only in fn args /// {..} /// } /// ``` @@ -2137,7 +2204,7 @@ impl<'tcx> Ty<'tcx> { pub fn fn_sig(self, tcx: TyCtxt<'tcx>) -> PolyFnSig<'tcx> { match self.kind() { - FnDef(def_id, substs) => tcx.fn_sig(*def_id).subst(tcx, substs), + FnDef(def_id, substs) => tcx.bound_fn_sig(*def_id).subst(tcx, substs), FnPtr(f) => *f, Error(_) => { // ignore errors (#54954) @@ -2304,7 +2371,7 @@ impl<'tcx> Ty<'tcx> { ty::Str | ty::Slice(_) => (tcx.types.usize, false), ty::Dynamic(..) => { let dyn_metadata = tcx.lang_items().dyn_metadata().unwrap(); - (tcx.type_of(dyn_metadata).subst(tcx, &[tail.into()]), false) + (tcx.bound_type_of(dyn_metadata).subst(tcx, &[tail.into()]), false) }, // type parameters only have unit metadata if they're sized, so return true diff --git a/compiler/rustc_middle/src/ty/subst.rs b/compiler/rustc_middle/src/ty/subst.rs index 46b938ea93b..48c71113d50 100644 --- a/compiler/rustc_middle/src/ty/subst.rs +++ b/compiler/rustc_middle/src/ty/subst.rs @@ -4,13 +4,13 @@ use crate::mir; use crate::ty::codec::{TyDecoder, TyEncoder}; use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder, TypeVisitor}; use crate::ty::sty::{ClosureSubsts, GeneratorSubsts, InlineConstSubsts}; -use crate::ty::{self, Lift, List, ParamConst, Ty, TyCtxt}; +use crate::ty::{self, EarlyBinder, Lift, List, ParamConst, Ty, TyCtxt}; use rustc_data_structures::intern::{Interned, WithStableHash}; use rustc_hir::def_id::DefId; use rustc_macros::HashStable; use rustc_serialize::{self, Decodable, Encodable}; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::DUMMY_SP; use smallvec::SmallVec; use core::intrinsics; @@ -498,35 +498,20 @@ impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List<Ty<'tcx>> { } } -/////////////////////////////////////////////////////////////////////////// -// Public trait `Subst` -// -// Just call `foo.subst(tcx, substs)` to perform a substitution across -// `foo`. Or use `foo.subst_spanned(tcx, substs, Some(span))` when -// there is more information available (for better errors). - +// Just call `foo.subst(tcx, substs)` to perform a substitution across `foo`. +#[rustc_on_unimplemented(message = "Calling `subst` must now be done through an `EarlyBinder`")] pub trait Subst<'tcx>: Sized { - fn subst(self, tcx: TyCtxt<'tcx>, substs: &[GenericArg<'tcx>]) -> Self { - self.subst_spanned(tcx, substs, None) - } + type Inner; - fn subst_spanned( - self, - tcx: TyCtxt<'tcx>, - substs: &[GenericArg<'tcx>], - span: Option<Span>, - ) -> Self; + fn subst(self, tcx: TyCtxt<'tcx>, substs: &[GenericArg<'tcx>]) -> Self::Inner; } -impl<'tcx, T: TypeFoldable<'tcx>> Subst<'tcx> for T { - fn subst_spanned( - self, - tcx: TyCtxt<'tcx>, - substs: &[GenericArg<'tcx>], - span: Option<Span>, - ) -> T { - let mut folder = SubstFolder { tcx, substs, span, binders_passed: 0 }; - self.fold_with(&mut folder) +impl<'tcx, T: TypeFoldable<'tcx>> Subst<'tcx> for EarlyBinder<T> { + type Inner = T; + + fn subst(self, tcx: TyCtxt<'tcx>, substs: &[GenericArg<'tcx>]) -> Self::Inner { + let mut folder = SubstFolder { tcx, substs, binders_passed: 0 }; + self.0.fold_with(&mut folder) } } @@ -537,9 +522,6 @@ struct SubstFolder<'a, 'tcx> { tcx: TyCtxt<'tcx>, substs: &'a [GenericArg<'tcx>], - /// The location for which the substitution is performed, if available. - span: Option<Span>, - /// Number of region binders we have passed through while doing the substitution binders_passed: u32, } @@ -571,13 +553,12 @@ impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> { match rk { Some(GenericArgKind::Lifetime(lt)) => self.shift_region_through_binders(lt), _ => { - let span = self.span.unwrap_or(DUMMY_SP); let msg = format!( "Region parameter out of range \ when substituting in region {} (index={})", data.name, data.index ); - span_bug!(span, "{}", msg); + span_bug!(DUMMY_SP, "{}", msg); } } } @@ -617,9 +598,8 @@ impl<'a, 'tcx> SubstFolder<'a, 'tcx> { let ty = match opt_ty { Some(GenericArgKind::Type(ty)) => ty, Some(kind) => { - let span = self.span.unwrap_or(DUMMY_SP); span_bug!( - span, + DUMMY_SP, "expected type for `{:?}` ({:?}/{}) but found {:?} \ when substituting, substs={:?}", p, @@ -630,9 +610,8 @@ impl<'a, 'tcx> SubstFolder<'a, 'tcx> { ); } None => { - let span = self.span.unwrap_or(DUMMY_SP); span_bug!( - span, + DUMMY_SP, "type parameter `{:?}` ({:?}/{}) out of range \ when substituting, substs={:?}", p, @@ -652,9 +631,8 @@ impl<'a, 'tcx> SubstFolder<'a, 'tcx> { let ct = match opt_ct { Some(GenericArgKind::Const(ct)) => ct, Some(kind) => { - let span = self.span.unwrap_or(DUMMY_SP); span_bug!( - span, + DUMMY_SP, "expected const for `{:?}` ({:?}/{}) but found {:?} \ when substituting substs={:?}", p, @@ -665,9 +643,8 @@ impl<'a, 'tcx> SubstFolder<'a, 'tcx> { ); } None => { - let span = self.span.unwrap_or(DUMMY_SP); span_bug!( - span, + DUMMY_SP, "const parameter `{:?}` ({:?}/{}) out of range \ when substituting substs={:?}", p, @@ -687,17 +664,17 @@ impl<'a, 'tcx> SubstFolder<'a, 'tcx> { /// /// ``` /// type Func<A> = fn(A); - /// type MetaFunc = for<'a> fn(Func<&'a i32>) + /// type MetaFunc = for<'a> fn(Func<&'a i32>); /// ``` /// /// The type `MetaFunc`, when fully expanded, will be - /// - /// for<'a> fn(fn(&'a i32)) - /// ^~ ^~ ^~~ - /// | | | - /// | | DebruijnIndex of 2 - /// Binders - /// + /// ```ignore (illustrative) + /// for<'a> fn(fn(&'a i32)) + /// // ^~ ^~ ^~~ + /// // | | | + /// // | | DebruijnIndex of 2 + /// // Binders + /// ``` /// Here the `'a` lifetime is bound in the outer function, but appears as an argument of the /// inner one. Therefore, that appearance will have a DebruijnIndex of 2, because we must skip /// over the inner binder (remember that we count De Bruijn indices from 1). However, in the @@ -709,17 +686,17 @@ impl<'a, 'tcx> SubstFolder<'a, 'tcx> { /// /// ``` /// type FuncTuple<A> = (A,fn(A)); - /// type MetaFuncTuple = for<'a> fn(FuncTuple<&'a i32>) + /// type MetaFuncTuple = for<'a> fn(FuncTuple<&'a i32>); /// ``` /// /// Here the final type will be: - /// - /// for<'a> fn((&'a i32, fn(&'a i32))) - /// ^~~ ^~~ - /// | | - /// DebruijnIndex of 1 | - /// DebruijnIndex of 2 - /// + /// ```ignore (illustrative) + /// for<'a> fn((&'a i32, fn(&'a i32))) + /// // ^~~ ^~~ + /// // | | + /// // DebruijnIndex of 1 | + /// // DebruijnIndex of 2 + /// ``` /// As indicated in the diagram, here the same type `&'a i32` is substituted once, but in the /// first case we do not increase the De Bruijn index and in the second case we do. The reason /// is that only in the second case have we passed through a fn binder. @@ -767,7 +744,7 @@ pub struct UserSubsts<'tcx> { /// sometimes needed to constrain the type parameters on the impl. For /// example, in this code: /// -/// ``` +/// ```ignore (illustrative) /// struct Foo<T> { } /// impl<A> Foo<A> { fn method() { } } /// ``` diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index ca6fabf7f40..cb34b64660d 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -143,7 +143,7 @@ impl<'tcx> TyCtxt<'tcx> { self_ty: Ty<'tcx>, ) -> impl Iterator<Item = DefId> + 'tcx { let impls = self.trait_impls_of(def_id); - if let Some(simp) = fast_reject::simplify_type(self, self_ty, TreatParams::AsPlaceholders) { + if let Some(simp) = fast_reject::simplify_type(self, self_ty, TreatParams::AsInfer) { if let Some(impls) = impls.non_blanket_impls.get(&simp) { return impls.iter().copied(); } @@ -173,14 +173,14 @@ impl<'tcx> TyCtxt<'tcx> { } } - // Note that we're using `TreatParams::AsBoundTypes` to query `non_blanket_impls` while using - // `TreatParams::AsPlaceholders` while actually adding them. + // Note that we're using `TreatParams::AsPlaceholder` to query `non_blanket_impls` while using + // `TreatParams::AsInfer` while actually adding them. // // This way, when searching for some impl for `T: Trait`, we do not look at any impls // whose outer level is not a parameter or projection. Especially for things like // `T: Clone` this is incredibly useful as we would otherwise look at all the impls // of `Clone` for `Option<T>`, `Vec<T>`, `ConcreteType` and so on. - if let Some(simp) = fast_reject::simplify_type(self, self_ty, TreatParams::AsBoundTypes) { + if let Some(simp) = fast_reject::simplify_type(self, self_ty, TreatParams::AsPlaceholder) { if let Some(impls) = impls.non_blanket_impls.get(&simp) { for &impl_def_id in impls { if let result @ Some(_) = f(impl_def_id) { @@ -240,7 +240,7 @@ pub(super) fn trait_impls_of_provider(tcx: TyCtxt<'_>, trait_id: DefId) -> Trait } if let Some(simplified_self_ty) = - fast_reject::simplify_type(tcx, impl_self_ty, TreatParams::AsPlaceholders) + fast_reject::simplify_type(tcx, impl_self_ty, TreatParams::AsInfer) { impls.non_blanket_impls.entry(simplified_self_ty).or_default().push(impl_def_id); } else { diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index c190eec7e5a..809e7ce2e74 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -5,9 +5,7 @@ use crate::ty::fold::{FallibleTypeFolder, TypeFolder}; use crate::ty::layout::IntegerExt; use crate::ty::query::TyCtxtAt; use crate::ty::subst::{GenericArgKind, Subst, SubstsRef}; -use crate::ty::{ - self, Const, DebruijnIndex, DefIdTree, List, ReEarlyBound, Ty, TyCtxt, TyKind::*, TypeFoldable, -}; +use crate::ty::{self, DefIdTree, Ty, TyCtxt, TypeFoldable}; use rustc_apfloat::Float as _; use rustc_ast as ast; use rustc_attr::{self as attr, SignedInt, UnsignedInt}; @@ -17,9 +15,11 @@ use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; +use rustc_index::bit_set::GrowableBitSet; use rustc_macros::HashStable; use rustc_span::{sym, DUMMY_SP}; use rustc_target::abi::{Integer, Size, TargetDataLayout}; +use rustc_target::spec::abi::Abi; use smallvec::SmallVec; use std::{fmt, iter}; @@ -30,6 +30,19 @@ pub struct Discr<'tcx> { pub ty: Ty<'tcx>, } +/// Used as an input to [`TyCtxt::uses_unique_generic_params`]. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum IgnoreRegions { + Yes, + No, +} + +#[derive(Copy, Clone, Debug)] +pub enum NotUniqueParam<'tcx> { + DuplicateParam(ty::GenericArg<'tcx>), + NotParam(ty::GenericArg<'tcx>), +} + impl<'tcx> fmt::Display for Discr<'tcx> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { match *self.ty.kind() { @@ -47,8 +60,8 @@ impl<'tcx> fmt::Display for Discr<'tcx> { fn int_size_and_signed<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (Size, bool) { let (int, signed) = match *ty.kind() { - Int(ity) => (Integer::from_int_ty(&tcx, ity), true), - Uint(uty) => (Integer::from_uint_ty(&tcx, uty), false), + ty::Int(ity) => (Integer::from_int_ty(&tcx, ity), true), + ty::Uint(uty) => (Integer::from_uint_ty(&tcx, uty), false), _ => bug!("non integer discriminant"), }; (int.size(), signed) @@ -174,7 +187,7 @@ impl<'tcx> TyCtxt<'tcx> { if let ty::Adt(def, substs) = *ty.kind() { for field in def.all_fields() { let field_ty = field.ty(self, substs); - if let Error(_) = field_ty.kind() { + if let ty::Error(_) = field_ty.kind() { return true; } } @@ -309,7 +322,7 @@ impl<'tcx> TyCtxt<'tcx> { let (mut a, mut b) = (source, target); loop { match (&a.kind(), &b.kind()) { - (&Adt(a_def, a_substs), &Adt(b_def, b_substs)) + (&ty::Adt(a_def, a_substs), &ty::Adt(b_def, b_substs)) if a_def == b_def && a_def.is_struct() => { if let Some(f) = a_def.non_enum_variant().fields.last() { @@ -319,7 +332,7 @@ impl<'tcx> TyCtxt<'tcx> { break; } } - (&Tuple(a_tys), &Tuple(b_tys)) if a_tys.len() == b_tys.len() => { + (&ty::Tuple(a_tys), &ty::Tuple(b_tys)) if a_tys.len() == b_tys.len() => { if let Some(&a_last) = a_tys.last() { a = a_last; b = *b_tys.last().unwrap(); @@ -425,7 +438,7 @@ impl<'tcx> TyCtxt<'tcx> { .filter(|&(_, k)| { match k.unpack() { GenericArgKind::Lifetime(region) => match region.kind() { - ReEarlyBound(ref ebr) => { + ty::ReEarlyBound(ref ebr) => { !impl_generics.region_param(ebr, self).pure_wrt_drop } // Error: not a region param @@ -451,6 +464,47 @@ impl<'tcx> TyCtxt<'tcx> { result } + /// Checks whether each generic argument is simply a unique generic parameter. + pub fn uses_unique_generic_params( + self, + substs: SubstsRef<'tcx>, + ignore_regions: IgnoreRegions, + ) -> Result<(), NotUniqueParam<'tcx>> { + let mut seen = GrowableBitSet::default(); + for arg in substs { + match arg.unpack() { + GenericArgKind::Lifetime(lt) => { + if ignore_regions == IgnoreRegions::No { + let ty::ReEarlyBound(p) = lt.kind() else { + return Err(NotUniqueParam::NotParam(lt.into())) + }; + if !seen.insert(p.index) { + return Err(NotUniqueParam::DuplicateParam(lt.into())); + } + } + } + GenericArgKind::Type(t) => match t.kind() { + ty::Param(p) => { + if !seen.insert(p.index) { + return Err(NotUniqueParam::DuplicateParam(t.into())); + } + } + _ => return Err(NotUniqueParam::NotParam(t.into())), + }, + GenericArgKind::Const(c) => match c.val() { + ty::ConstKind::Param(p) => { + if !seen.insert(p.index) { + return Err(NotUniqueParam::DuplicateParam(c.into())); + } + } + _ => return Err(NotUniqueParam::NotParam(c.into())), + }, + } + } + + Ok(()) + } + /// Returns `true` if `def_id` refers to a closure (e.g., `|x| x * 2`). Note /// that closures have a `DefId`, but the closure *expression* also /// has a `HirId` that is located within the context where the @@ -591,6 +645,35 @@ impl<'tcx> TyCtxt<'tcx> { trace!(?expanded_type); if visitor.found_recursion { Err(expanded_type) } else { Ok(expanded_type) } } + + pub fn bound_type_of(self, def_id: DefId) -> ty::EarlyBinder<Ty<'tcx>> { + ty::EarlyBinder(self.type_of(def_id)) + } + + pub fn bound_fn_sig(self, def_id: DefId) -> ty::EarlyBinder<ty::PolyFnSig<'tcx>> { + ty::EarlyBinder(self.fn_sig(def_id)) + } + + pub fn bound_impl_trait_ref( + self, + def_id: DefId, + ) -> Option<ty::EarlyBinder<ty::TraitRef<'tcx>>> { + self.impl_trait_ref(def_id).map(|i| ty::EarlyBinder(i)) + } + + pub fn bound_explicit_item_bounds( + self, + def_id: DefId, + ) -> ty::EarlyBinder<&'tcx [(ty::Predicate<'tcx>, rustc_span::Span)]> { + ty::EarlyBinder(self.explicit_item_bounds(def_id)) + } + + pub fn bound_item_bounds( + self, + def_id: DefId, + ) -> ty::EarlyBinder<&'tcx ty::List<ty::Predicate<'tcx>>> { + ty::EarlyBinder(self.item_bounds(def_id)) + } } struct OpaqueTypeExpander<'tcx> { @@ -622,7 +705,7 @@ impl<'tcx> OpaqueTypeExpander<'tcx> { let expanded_ty = match self.expanded_cache.get(&(def_id, substs)) { Some(expanded_ty) => *expanded_ty, None => { - let generic_ty = self.tcx.type_of(def_id); + let generic_ty = self.tcx.bound_type_of(def_id); let concrete_ty = generic_ty.subst(self.tcx, substs); let expanded_ty = self.fold_ty(concrete_ty); self.expanded_cache.insert((def_id, substs), expanded_ty); @@ -662,7 +745,7 @@ impl<'tcx> TypeFolder<'tcx> for OpaqueTypeExpander<'tcx> { impl<'tcx> Ty<'tcx> { /// Returns the maximum value for the given numeric type (including `char`s) /// or returns `None` if the type is not numeric. - pub fn numeric_max_val(self, tcx: TyCtxt<'tcx>) -> Option<Const<'tcx>> { + pub fn numeric_max_val(self, tcx: TyCtxt<'tcx>) -> Option<ty::Const<'tcx>> { let val = match self.kind() { ty::Int(_) | ty::Uint(_) => { let (size, signed) = int_size_and_signed(tcx, self); @@ -677,12 +760,13 @@ impl<'tcx> Ty<'tcx> { }), _ => None, }; - val.map(|v| Const::from_bits(tcx, v, ty::ParamEnv::empty().and(self))) + + val.map(|v| ty::Const::from_bits(tcx, v, ty::ParamEnv::empty().and(self))) } /// Returns the minimum value for the given numeric type (including `char`s) /// or returns `None` if the type is not numeric. - pub fn numeric_min_val(self, tcx: TyCtxt<'tcx>) -> Option<Const<'tcx>> { + pub fn numeric_min_val(self, tcx: TyCtxt<'tcx>) -> Option<ty::Const<'tcx>> { let val = match self.kind() { ty::Int(_) | ty::Uint(_) => { let (size, signed) = int_size_and_signed(tcx, self); @@ -696,7 +780,8 @@ impl<'tcx> Ty<'tcx> { }), _ => None, }; - val.map(|v| Const::from_bits(tcx, v, ty::ParamEnv::empty().and(self))) + + val.map(|v| ty::Const::from_bits(tcx, v, ty::ParamEnv::empty().and(self))) } /// Checks whether values of this type `T` are *moved* or *copied* @@ -900,35 +985,40 @@ impl<'tcx> Ty<'tcx> { pub fn is_structural_eq_shallow(self, tcx: TyCtxt<'tcx>) -> bool { match self.kind() { // Look for an impl of both `PartialStructuralEq` and `StructuralEq`. - Adt(..) => tcx.has_structural_eq_impls(self), + ty::Adt(..) => tcx.has_structural_eq_impls(self), // Primitive types that satisfy `Eq`. - Bool | Char | Int(_) | Uint(_) | Str | Never => true, + ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Str | ty::Never => true, // Composite types that satisfy `Eq` when all of their fields do. // // Because this function is "shallow", we return `true` for these composites regardless // of the type(s) contained within. - Ref(..) | Array(..) | Slice(_) | Tuple(..) => true, + ty::Ref(..) | ty::Array(..) | ty::Slice(_) | ty::Tuple(..) => true, // Raw pointers use bitwise comparison. - RawPtr(_) | FnPtr(_) => true, + ty::RawPtr(_) | ty::FnPtr(_) => true, // Floating point numbers are not `Eq`. - Float(_) => false, + ty::Float(_) => false, // Conservatively return `false` for all others... // Anonymous function types - FnDef(..) | Closure(..) | Dynamic(..) | Generator(..) => false, + ty::FnDef(..) | ty::Closure(..) | ty::Dynamic(..) | ty::Generator(..) => false, // Generic or inferred types // // FIXME(ecstaticmorse): Maybe we should `bug` here? This should probably only be // called for known, fully-monomorphized types. - Projection(_) | Opaque(..) | Param(_) | Bound(..) | Placeholder(_) | Infer(_) => false, + ty::Projection(_) + | ty::Opaque(..) + | ty::Param(_) + | ty::Bound(..) + | ty::Placeholder(_) + | ty::Infer(_) => false, - Foreign(_) | GeneratorWitness(..) | Error(_) => false, + ty::Foreign(_) | ty::GeneratorWitness(..) | ty::Error(_) => false, } } @@ -944,13 +1034,13 @@ impl<'tcx> Ty<'tcx> { /// - `&'a *const &'b u8 -> *const &'b u8` pub fn peel_refs(self) -> Ty<'tcx> { let mut ty = self; - while let Ref(_, inner_ty, _) = ty.kind() { + while let ty::Ref(_, inner_ty, _) = ty.kind() { ty = *inner_ty; } ty } - pub fn outer_exclusive_binder(self) -> DebruijnIndex { + pub fn outer_exclusive_binder(self) -> ty::DebruijnIndex { self.0.outer_exclusive_binder } } @@ -973,7 +1063,7 @@ impl<'tcx> ExplicitSelf<'tcx> { /// /// Examples: /// - /// ``` + /// ```ignore (illustrative) /// impl<'a> Foo for &'a T { /// // Legal declarations: /// fn method1(self: &&'a T); // ExplicitSelf::ByReference @@ -1147,8 +1237,8 @@ pub struct AlwaysRequiresDrop; /// with their underlying types. pub fn normalize_opaque_types<'tcx>( tcx: TyCtxt<'tcx>, - val: &'tcx List<ty::Predicate<'tcx>>, -) -> &'tcx List<ty::Predicate<'tcx>> { + val: &'tcx ty::List<ty::Predicate<'tcx>>, +) -> &'tcx ty::List<ty::Predicate<'tcx>> { let mut visitor = OpaqueTypeExpander { seen_opaque_tys: FxHashSet::default(), expanded_cache: FxHashMap::default(), @@ -1163,12 +1253,17 @@ pub fn normalize_opaque_types<'tcx>( /// Determines whether an item is annotated with `doc(hidden)`. pub fn is_doc_hidden(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - tcx.get_attrs(def_id) - .iter() - .filter_map(|attr| if attr.has_name(sym::doc) { attr.meta_item_list() } else { None }) + tcx.get_attrs(def_id, sym::doc) + .filter_map(|attr| attr.meta_item_list()) .any(|items| items.iter().any(|item| item.has_name(sym::hidden))) } +/// Determines whether an item is an intrinsic by Abi. +pub fn is_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + matches!(tcx.fn_sig(def_id).abi(), Abi::RustIntrinsic | Abi::PlatformIntrinsic) +} + pub fn provide(providers: &mut ty::query::Providers) { - *providers = ty::query::Providers { normalize_opaque_types, is_doc_hidden, ..*providers } + *providers = + ty::query::Providers { normalize_opaque_types, is_doc_hidden, is_intrinsic, ..*providers } } diff --git a/compiler/rustc_middle/src/ty/walk.rs b/compiler/rustc_middle/src/ty/walk.rs index 9f57e1a977a..09946f02448 100644 --- a/compiler/rustc_middle/src/ty/walk.rs +++ b/compiler/rustc_middle/src/ty/walk.rs @@ -34,7 +34,7 @@ impl<'tcx> TypeWalker<'tcx> { /// /// Example: Imagine you are walking `Foo<Bar<i32>, usize>`. /// - /// ``` + /// ```ignore (illustrative) /// let mut iter: TypeWalker = ...; /// iter.next(); // yields Foo /// iter.next(); // yields Bar<i32> diff --git a/compiler/rustc_mir_build/src/build/block.rs b/compiler/rustc_mir_build/src/build/block.rs index 679043bdc30..e8b939fb51d 100644 --- a/compiler/rustc_mir_build/src/build/block.rs +++ b/compiler/rustc_mir_build/src/build/block.rs @@ -6,7 +6,7 @@ use rustc_middle::{mir::*, ty}; use rustc_span::Span; impl<'a, 'tcx> Builder<'a, 'tcx> { - crate fn ast_block( + pub(crate) fn ast_block( &mut self, destination: Place<'tcx>, block: BasicBlock, @@ -108,7 +108,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let_scope_stack.push(remainder_scope); // Declare the bindings, which may create a source scope. - let remainder_span = remainder_scope.span(this.tcx, this.region_scope_tree); + let remainder_span = + remainder_scope.span(this.tcx, &this.typeck_results.region_scope_tree); let visibility_scope = Some(this.new_source_scope(remainder_span, LintLevel::Inherited, None)); diff --git a/compiler/rustc_mir_build/src/build/cfg.rs b/compiler/rustc_mir_build/src/build/cfg.rs index ac92b03e5f3..d7b4b1f731a 100644 --- a/compiler/rustc_mir_build/src/build/cfg.rs +++ b/compiler/rustc_mir_build/src/build/cfg.rs @@ -5,33 +5,33 @@ use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; impl<'tcx> CFG<'tcx> { - crate fn block_data(&self, blk: BasicBlock) -> &BasicBlockData<'tcx> { + pub(crate) fn block_data(&self, blk: BasicBlock) -> &BasicBlockData<'tcx> { &self.basic_blocks[blk] } - crate fn block_data_mut(&mut self, blk: BasicBlock) -> &mut BasicBlockData<'tcx> { + pub(crate) fn block_data_mut(&mut self, blk: BasicBlock) -> &mut BasicBlockData<'tcx> { &mut self.basic_blocks[blk] } // llvm.org/PR32488 makes this function use an excess of stack space. Mark // it as #[inline(never)] to keep rustc's stack use in check. #[inline(never)] - crate fn start_new_block(&mut self) -> BasicBlock { + pub(crate) fn start_new_block(&mut self) -> BasicBlock { self.basic_blocks.push(BasicBlockData::new(None)) } - crate fn start_new_cleanup_block(&mut self) -> BasicBlock { + pub(crate) fn start_new_cleanup_block(&mut self) -> BasicBlock { let bb = self.start_new_block(); self.block_data_mut(bb).is_cleanup = true; bb } - crate fn push(&mut self, block: BasicBlock, statement: Statement<'tcx>) { + pub(crate) fn push(&mut self, block: BasicBlock, statement: Statement<'tcx>) { debug!("push({:?}, {:?})", block, statement); self.block_data_mut(block).statements.push(statement); } - crate fn push_assign( + pub(crate) fn push_assign( &mut self, block: BasicBlock, source_info: SourceInfo, @@ -44,7 +44,7 @@ impl<'tcx> CFG<'tcx> { ); } - crate fn push_assign_constant( + pub(crate) fn push_assign_constant( &mut self, block: BasicBlock, source_info: SourceInfo, @@ -59,7 +59,7 @@ impl<'tcx> CFG<'tcx> { ); } - crate fn push_assign_unit( + pub(crate) fn push_assign_unit( &mut self, block: BasicBlock, source_info: SourceInfo, @@ -78,7 +78,7 @@ impl<'tcx> CFG<'tcx> { ); } - crate fn push_fake_read( + pub(crate) fn push_fake_read( &mut self, block: BasicBlock, source_info: SourceInfo, @@ -90,7 +90,7 @@ impl<'tcx> CFG<'tcx> { self.push(block, stmt); } - crate fn terminate( + pub(crate) fn terminate( &mut self, block: BasicBlock, source_info: SourceInfo, @@ -107,7 +107,7 @@ impl<'tcx> CFG<'tcx> { } /// In the `origin` block, push a `goto -> target` terminator. - crate fn goto(&mut self, origin: BasicBlock, source_info: SourceInfo, target: BasicBlock) { + pub(crate) fn goto(&mut self, origin: BasicBlock, source_info: SourceInfo, target: BasicBlock) { self.terminate(origin, source_info, TerminatorKind::Goto { target }) } } diff --git a/compiler/rustc_mir_build/src/build/expr/as_constant.rs b/compiler/rustc_mir_build/src/build/expr/as_constant.rs index 3a6e59db90b..035e94eecee 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_constant.rs @@ -1,21 +1,17 @@ //! See docs in build/expr/mod.rs -use crate::build::Builder; -use crate::thir::constant::parse_float; -use rustc_ast as ast; +use crate::build::{lit_to_mir_constant, Builder}; use rustc_hir::def_id::DefId; -use rustc_middle::mir::interpret::Allocation; use rustc_middle::mir::interpret::{ConstValue, LitToConstError, LitToConstInput, Scalar}; use rustc_middle::mir::*; use rustc_middle::thir::*; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt}; -use rustc_target::abi::Size; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Compile `expr`, yielding a compile-time constant. Assumes that /// `expr` is a valid compile-time constant! - crate fn as_constant(&mut self, expr: &Expr<'tcx>) -> Constant<'tcx> { + pub(crate) fn as_constant(&mut self, expr: &Expr<'tcx>) -> Constant<'tcx> { let create_uneval_from_def_id = |tcx: TyCtxt<'tcx>, def_id: DefId, ty: Ty<'tcx>, substs: SubstsRef<'tcx>| { let uneval = ty::Unevaluated::new(ty::WithOptConstParam::unknown(def_id), substs); @@ -88,54 +84,3 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } } - -#[instrument(skip(tcx, lit_input))] -fn lit_to_mir_constant<'tcx>( - tcx: TyCtxt<'tcx>, - lit_input: LitToConstInput<'tcx>, -) -> Result<ConstantKind<'tcx>, LitToConstError> { - let LitToConstInput { lit, ty, neg } = lit_input; - let trunc = |n| { - let param_ty = ty::ParamEnv::reveal_all().and(ty); - let width = tcx.layout_of(param_ty).map_err(|_| LitToConstError::Reported)?.size; - trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits()); - let result = width.truncate(n); - trace!("trunc result: {}", result); - Ok(ConstValue::Scalar(Scalar::from_uint(result, width))) - }; - - let value = match (lit, &ty.kind()) { - (ast::LitKind::Str(s, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_str() => { - let s = s.as_str(); - let allocation = Allocation::from_bytes_byte_aligned_immutable(s.as_bytes()); - let allocation = tcx.intern_const_alloc(allocation); - ConstValue::Slice { data: allocation, start: 0, end: s.len() } - } - (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _)) - if matches!(inner_ty.kind(), ty::Slice(_)) => - { - let allocation = Allocation::from_bytes_byte_aligned_immutable(data as &[u8]); - let allocation = tcx.intern_const_alloc(allocation); - ConstValue::Slice { data: allocation, start: 0, end: data.len() } - } - (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => { - let id = tcx.allocate_bytes(data); - ConstValue::Scalar(Scalar::from_pointer(id.into(), &tcx)) - } - (ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => { - ConstValue::Scalar(Scalar::from_uint(*n, Size::from_bytes(1))) - } - (ast::LitKind::Int(n, _), ty::Uint(_)) | (ast::LitKind::Int(n, _), ty::Int(_)) => { - trunc(if neg { (*n as i128).overflowing_neg().0 as u128 } else { *n })? - } - (ast::LitKind::Float(n, _), ty::Float(fty)) => { - parse_float(*n, *fty, neg).ok_or(LitToConstError::Reported)? - } - (ast::LitKind::Bool(b), ty::Bool) => ConstValue::Scalar(Scalar::from_bool(*b)), - (ast::LitKind::Char(c), ty::Char) => ConstValue::Scalar(Scalar::from_char(*c)), - (ast::LitKind::Err(_), _) => return Err(LitToConstError::Reported), - _ => return Err(LitToConstError::TypeError), - }; - - Ok(ConstantKind::Val(value, ty)) -} diff --git a/compiler/rustc_mir_build/src/build/expr/as_operand.rs b/compiler/rustc_mir_build/src/build/expr/as_operand.rs index c413b8ff82b..e707c373f0d 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_operand.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_operand.rs @@ -14,7 +14,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// after the current enclosing `ExprKind::Scope` has ended, so /// please do *not* return it from functions to avoid bad /// miscompiles. - crate fn as_local_operand( + pub(crate) fn as_local_operand( &mut self, block: BasicBlock, expr: &Expr<'tcx>, @@ -42,15 +42,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// We tweak the handling of parameters of unsized type slightly to avoid the need to create a /// local variable of unsized type. For example, consider this program: /// - /// ```rust - /// fn foo(p: dyn Debug) { ... } + /// ``` + /// #![feature(unsized_locals, unsized_fn_params)] + /// # use core::fmt::Debug; + /// fn foo(p: dyn Debug) { dbg!(p); } /// - /// fn bar(box_p: Box<dyn Debug>) { foo(*p); } + /// fn bar(box_p: Box<dyn Debug>) { foo(*box_p); } /// ``` /// /// Ordinarily, for sized types, we would compile the call `foo(*p)` like so: /// - /// ```rust + /// ```ignore (illustrative) /// let tmp0 = *box_p; // tmp0 would be the operand returned by this function call /// foo(tmp0) /// ``` @@ -60,7 +62,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// that we create *stores the entire box*, and the parameter to the call itself will be /// `*tmp0`: /// - /// ```rust + /// ```ignore (illustrative) /// let tmp0 = box_p; call foo(*tmp0) /// ``` /// @@ -71,7 +73,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// value to the stack. /// /// See #68034 for more details. - crate fn as_local_call_operand( + pub(crate) fn as_local_call_operand( &mut self, block: BasicBlock, expr: &Expr<'tcx>, @@ -95,7 +97,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Like `as_local_call_operand`, except that the argument will /// not be valid once `scope` ends. #[instrument(level = "debug", skip(self, scope))] - crate fn as_operand( + pub(crate) fn as_operand( &mut self, mut block: BasicBlock, scope: Option<region::Scope>, @@ -130,7 +132,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - crate fn as_call_operand( + pub(crate) fn as_call_operand( &mut self, mut block: BasicBlock, scope: Option<region::Scope>, diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs index 3b3120cf04b..045d6eb1c30 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -21,7 +21,7 @@ use std::iter; /// The "outermost" place that holds this value. #[derive(Copy, Clone, Debug, PartialEq)] -crate enum PlaceBase { +pub(crate) enum PlaceBase { /// Denotes the start of a `Place`. Local(Local), @@ -37,7 +37,7 @@ crate enum PlaceBase { /// /// Consider the following example /// ```rust - /// let t = (10, (10, (10, 10))); + /// let t = (((10, 10), 10), 10); /// /// let c = || { /// println!("{}", t.0.0.0); @@ -45,7 +45,7 @@ crate enum PlaceBase { /// ``` /// Here the THIR expression for `t.0.0.0` will be something like /// - /// ``` + /// ```ignore (illustrative) /// * Field(0) /// * Field(0) /// * Field(0) @@ -71,7 +71,7 @@ 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)] -crate struct PlaceBuilder<'tcx> { +pub(crate) struct PlaceBuilder<'tcx> { base: PlaceBase, projection: Vec<PlaceElem<'tcx>>, } @@ -283,7 +283,7 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>( } impl<'tcx> PlaceBuilder<'tcx> { - crate fn into_place<'a>( + pub(crate) fn into_place<'a>( self, tcx: TyCtxt<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>, @@ -314,7 +314,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. - crate fn try_upvars_resolved<'a>( + pub(crate) fn try_upvars_resolved<'a>( self, tcx: TyCtxt<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>, @@ -322,19 +322,19 @@ impl<'tcx> PlaceBuilder<'tcx> { to_upvars_resolved_place_builder(self, tcx, typeck_results) } - crate fn base(&self) -> PlaceBase { + pub(crate) fn base(&self) -> PlaceBase { self.base } - crate fn field(self, f: Field, ty: Ty<'tcx>) -> Self { + pub(crate) fn field(self, f: Field, ty: Ty<'tcx>) -> Self { self.project(PlaceElem::Field(f, ty)) } - crate fn deref(self) -> Self { + pub(crate) fn deref(self) -> Self { self.project(PlaceElem::Deref) } - crate fn downcast(self, adt_def: AdtDef<'tcx>, variant_index: VariantIdx) -> Self { + pub(crate) fn downcast(self, adt_def: AdtDef<'tcx>, variant_index: VariantIdx) -> Self { self.project(PlaceElem::Downcast(Some(adt_def.variant(variant_index).name), variant_index)) } @@ -342,7 +342,7 @@ impl<'tcx> PlaceBuilder<'tcx> { self.project(PlaceElem::Index(index)) } - crate fn project(mut self, elem: PlaceElem<'tcx>) -> Self { + pub(crate) fn project(mut self, elem: PlaceElem<'tcx>) -> Self { self.projection.push(elem); self } @@ -373,7 +373,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Extra care is needed if any user code is allowed to run between calling /// this method and using it, as is the case for `match` and index /// expressions. - crate fn as_place( + pub(crate) fn as_place( &mut self, mut block: BasicBlock, expr: &Expr<'tcx>, @@ -384,7 +384,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// This is used when constructing a compound `Place`, so that we can avoid creating /// intermediate `Place` values until we know the full set of projections. - crate fn as_place_builder( + pub(crate) fn as_place_builder( &mut self, block: BasicBlock, expr: &Expr<'tcx>, @@ -397,7 +397,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// place. The place itself may or may not be mutable: /// * If this expr is a place expr like a.b, then we will return that place. /// * Otherwise, a temporary is created: in that event, it will be an immutable temporary. - crate fn as_read_only_place( + pub(crate) fn as_read_only_place( &mut self, mut block: BasicBlock, expr: &Expr<'tcx>, diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs index d807500f1fb..0fd67f15b75 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -21,7 +21,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// The operand returned from this function will *not be valid* after /// an ExprKind::Scope is passed, so please do *not* return it from /// functions to avoid bad miscompiles. - crate fn as_local_rvalue( + pub(crate) fn as_local_rvalue( &mut self, block: BasicBlock, expr: &Expr<'tcx>, @@ -31,7 +31,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } /// Compile `expr`, yielding an rvalue. - crate fn as_rvalue( + pub(crate) fn as_rvalue( &mut self, mut block: BasicBlock, scope: Option<region::Scope>, @@ -416,7 +416,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - crate fn build_binary_op( + pub(crate) fn build_binary_op( &mut self, mut block: BasicBlock, op: BinOp, diff --git a/compiler/rustc_mir_build/src/build/expr/as_temp.rs b/compiler/rustc_mir_build/src/build/expr/as_temp.rs index 6067da2f69b..724b72f8769 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_temp.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_temp.rs @@ -10,7 +10,7 @@ use rustc_middle::thir::*; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Compile `expr` into a fresh temporary. This is used when building /// up rvalues so as to freeze the value that will be consumed. - crate fn as_temp( + pub(crate) fn as_temp( &mut self, block: BasicBlock, temp_lifetime: Option<region::Scope>, diff --git a/compiler/rustc_mir_build/src/build/expr/category.rs b/compiler/rustc_mir_build/src/build/expr/category.rs index bcece39c620..b1a70643934 100644 --- a/compiler/rustc_mir_build/src/build/expr/category.rs +++ b/compiler/rustc_mir_build/src/build/expr/category.rs @@ -1,7 +1,7 @@ use rustc_middle::thir::*; #[derive(Debug, PartialEq)] -crate enum Category { +pub(crate) enum Category { // An assignable memory location like `x`, `x.f`, `foo()[3]`, that // sort of thing. Something that could appear on the LHS of an `=` // sign. @@ -19,7 +19,7 @@ crate enum Category { // Rvalues fall into different "styles" that will determine which fn // is best suited to generate them. #[derive(Debug, PartialEq)] -crate enum RvalueFunc { +pub(crate) enum RvalueFunc { // Best generated by `into`. This is generally exprs that // cause branching, like `match`, but also includes calls. Into, @@ -31,7 +31,7 @@ crate enum RvalueFunc { /// Determines the category for a given expression. Note that scope /// and paren expressions have no category. impl Category { - crate fn of(ek: &ExprKind<'_>) -> Option<Category> { + pub(crate) fn of(ek: &ExprKind<'_>) -> Option<Category> { match *ek { ExprKind::Scope { .. } => None, diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index 4399fdf8520..e912501d55f 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -15,7 +15,7 @@ use std::iter; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Compile `expr`, storing the result into `destination`, which /// is assumed to be uninitialized. - crate fn expr_into_dest( + pub(crate) fn expr_into_dest( &mut self, destination: Place<'tcx>, mut block: BasicBlock, diff --git a/compiler/rustc_mir_build/src/build/expr/mod.rs b/compiler/rustc_mir_build/src/build/expr/mod.rs index 7be435cda7d..f5ae060d603 100644 --- a/compiler/rustc_mir_build/src/build/expr/mod.rs +++ b/compiler/rustc_mir_build/src/build/expr/mod.rs @@ -60,7 +60,7 @@ //! basically the point where the "by value" operations are bridged //! over to the "by reference" mode (`as_place`). -crate mod as_constant; +pub(crate) mod as_constant; mod as_operand; pub mod as_place; mod as_rvalue; diff --git a/compiler/rustc_mir_build/src/build/expr/stmt.rs b/compiler/rustc_mir_build/src/build/expr/stmt.rs index 46c616ff362..a7e1331aabc 100644 --- a/compiler/rustc_mir_build/src/build/expr/stmt.rs +++ b/compiler/rustc_mir_build/src/build/expr/stmt.rs @@ -10,7 +10,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// (e.g., `some().code(&here());`) then `opt_stmt_span` is the /// span of that statement (including its semicolon, if any). /// The scope is used if a statement temporary must be dropped. - crate fn stmt_expr( + pub(crate) fn stmt_expr( &mut self, mut block: BasicBlock, expr: &Expr<'tcx>, diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index d45ae19752e..9df86c6ada1 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -11,7 +11,7 @@ use crate::build::ForGuard::{self, OutsideGuard, RefWithinGuard}; use crate::build::{BlockAnd, BlockAndExtension, Builder}; use crate::build::{GuardFrame, GuardFrameLocal, LocalsForNode}; use rustc_data_structures::{ - fx::{FxHashSet, FxIndexMap}, + fx::{FxHashSet, FxIndexMap, FxIndexSet}, stack::ensure_sufficient_stack, }; use rustc_hir::HirId; @@ -151,7 +151,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// /// * From each pre-binding block to the next pre-binding block. /// * From each otherwise block to the next pre-binding block. - crate fn match_expr( + #[tracing::instrument(level = "debug", skip(self, arms))] + pub(crate) fn match_expr( &mut self, destination: Place<'tcx>, span: Span, @@ -264,7 +265,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // The set of places that we are creating fake borrows of. If there are // no match guards then we don't need any fake borrows, so don't track // them. - let mut fake_borrows = match_has_guard.then(FxHashSet::default); + let mut fake_borrows = match_has_guard.then(FxIndexSet::default); let mut otherwise = None; @@ -523,8 +524,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }, .. }, - ascription: - thir::Ascription { user_ty: pat_ascription_ty, variance: _, user_ty_span }, + ascription: thir::Ascription { annotation, variance: _ }, } => { let place = self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true); @@ -535,18 +535,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let cause_let = FakeReadCause::ForLet(None); self.cfg.push_fake_read(block, pattern_source_info, cause_let, place); - let ty_source_info = self.source_info(user_ty_span); - let user_ty = pat_ascription_ty.user_ty( - &mut self.canonical_user_type_annotations, - place.ty(&self.local_decls, self.tcx).ty, - ty_source_info.span, - ); + let ty_source_info = self.source_info(annotation.span); + + let base = self.canonical_user_type_annotations.push(annotation); self.cfg.push( block, Statement { source_info: ty_source_info, kind: StatementKind::AscribeUserType( - Box::new((place, user_ty)), + Box::new((place, UserTypeProjection { base, projs: Vec::new() })), // We always use invariant as the variance here. This is because the // variance field from the ascription refers to the variance to use // when applying the type to the value being matched, but this @@ -577,7 +574,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - crate fn place_into_pattern( + pub(crate) fn place_into_pattern( &mut self, block: BasicBlock, irrefutable_pat: Pat<'tcx>, @@ -653,7 +650,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// scope for the bindings in these patterns, if such a scope had to be /// created. NOTE: Declaring the bindings should always be done in their /// drop scope. - crate fn declare_bindings( + pub(crate) fn declare_bindings( &mut self, mut visibility_scope: Option<SourceScope>, scope_span: Span, @@ -690,7 +687,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { visibility_scope } - crate fn storage_live_binding( + pub(crate) fn storage_live_binding( &mut self, block: BasicBlock, var: HirId, @@ -703,15 +700,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(local_id) }); // Altough there is almost always scope for given variable in corner cases // like #92893 we might get variable with no scope. - if let Some(region_scope) = self.region_scope_tree.var_scope(var.local_id) && schedule_drop{ + if let Some(region_scope) = self.typeck_results.region_scope_tree.var_scope(var.local_id) && schedule_drop{ self.schedule_drop(span, region_scope, local_id, DropKind::Storage); } Place::from(local_id) } - crate fn schedule_drop_for_binding(&mut self, var: HirId, span: Span, for_guard: ForGuard) { + pub(crate) fn schedule_drop_for_binding( + &mut self, + var: HirId, + span: Span, + for_guard: ForGuard, + ) { let local_id = self.var_local_id(var, for_guard); - if let Some(region_scope) = self.region_scope_tree.var_scope(var.local_id) { + if let Some(region_scope) = self.typeck_results.region_scope_tree.var_scope(var.local_id) { self.schedule_drop(span, region_scope, local_id, DropKind::Value); } } @@ -784,7 +786,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { PatKind::AscribeUserType { ref subpattern, - ascription: thir::Ascription { ref user_ty, user_ty_span, variance: _ }, + ascription: thir::Ascription { ref annotation, variance: _ }, } => { // This corresponds to something like // @@ -794,16 +796,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // // Note that the variance doesn't apply here, as we are tracking the effect // of `user_ty` on any bindings contained with subpattern. - let annotation = CanonicalUserTypeAnnotation { - span: user_ty_span, - user_ty: user_ty.user_ty, - inferred_ty: subpattern.ty, - }; + let projection = UserTypeProjection { - base: self.canonical_user_type_annotations.push(annotation), + base: self.canonical_user_type_annotations.push(annotation.clone()), projs: Vec::new(), }; - let subpattern_user_ty = pattern_user_ty.push_projection(&projection, user_ty_span); + let subpattern_user_ty = + pattern_user_ty.push_projection(&projection, annotation.span); self.visit_primary_bindings(subpattern, subpattern_user_ty, f) } @@ -927,14 +926,13 @@ struct Binding<'tcx> { /// influence region inference. #[derive(Clone, Debug)] struct Ascription<'tcx> { - span: Span, source: Place<'tcx>, - user_ty: PatTyProj<'tcx>, + annotation: CanonicalUserTypeAnnotation<'tcx>, variance: ty::Variance, } #[derive(Clone, Debug)] -crate struct MatchPair<'pat, 'tcx> { +pub(crate) struct MatchPair<'pat, 'tcx> { // this place... place: PlaceBuilder<'tcx>, @@ -966,13 +964,13 @@ enum TestKind<'tcx> { /// /// For `bool` we always generate two edges, one for `true` and one for /// `false`. - options: FxIndexMap<ty::Const<'tcx>, u128>, + options: FxIndexMap<ConstantKind<'tcx>, u128>, }, /// Test for equality with value, possibly after an unsizing coercion to /// `ty`, Eq { - value: ty::Const<'tcx>, + value: ConstantKind<'tcx>, // Integer types are handled by `SwitchInt`, and constants with ADT // types are converted back into patterns, so this can only be `&str`, // `&[T]`, `f32` or `f64`. @@ -991,7 +989,7 @@ enum TestKind<'tcx> { /// [`Test`] is just the test to perform; it does not include the value /// to be tested. #[derive(Debug)] -crate struct Test<'tcx> { +pub(crate) struct Test<'tcx> { span: Span, kind: TestKind<'tcx>, } @@ -999,7 +997,7 @@ crate struct Test<'tcx> { /// `ArmHasGuard` is a wrapper around a boolean flag. It indicates whether /// a match arm has a guard expression attached to it. #[derive(Copy, Clone, Debug)] -crate struct ArmHasGuard(crate bool); +pub(crate) struct ArmHasGuard(pub(crate) bool); /////////////////////////////////////////////////////////////////////////// // Main matching algorithm @@ -1032,11 +1030,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// exhaustive match, consider: /// /// ``` + /// # fn foo(x: (bool, bool)) { /// match x { /// (true, true) => (), /// (_, false) => (), /// (false, true) => (), /// } + /// # } /// ``` /// /// For this match, we check if `x.0` matches `true` (for the first @@ -1051,7 +1051,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { start_block: BasicBlock, otherwise_block: &mut Option<BasicBlock>, candidates: &mut [&mut Candidate<'pat, 'tcx>], - fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>, + fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>, ) { debug!( "matched_candidate(span={:?}, candidates={:?}, start_block={:?}, otherwise_block={:?})", @@ -1103,7 +1103,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { start_block: BasicBlock, otherwise_block: &mut Option<BasicBlock>, candidates: &mut [&mut Candidate<'_, 'tcx>], - fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>, + fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>, ) { // The candidates are sorted by priority. Check to see whether the // higher priority candidates (and hence at the front of the slice) @@ -1157,7 +1157,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// /// For example, if we have something like this: /// - /// ```rust + /// ```ignore (illustrative) /// ... /// Some(x) if cond1 => ... /// Some(x) => ... @@ -1182,7 +1182,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, matched_candidates: &mut [&mut Candidate<'_, 'tcx>], start_block: BasicBlock, - fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>, + fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>, ) -> Option<BasicBlock> { debug_assert!( !matched_candidates.is_empty(), @@ -1320,7 +1320,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { candidates: &mut [&mut Candidate<'_, 'tcx>], block: BasicBlock, otherwise_block: &mut Option<BasicBlock>, - fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>, + fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>, ) { let (first_candidate, remaining_candidates) = candidates.split_first_mut().unwrap(); @@ -1383,7 +1383,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { pats: &'pat [Pat<'tcx>], or_span: Span, place: PlaceBuilder<'tcx>, - fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>, + fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>, ) { debug!("test_or_pattern:\ncandidate={:#?}\npats={:#?}", candidate, pats); let mut or_candidates: Vec<_> = pats @@ -1481,11 +1481,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// ``` /// # let (x, y, z) = (true, true, true); /// match (x, y, z) { - /// (true, _, true) => true, // (0) - /// (_, true, _) => true, // (1) - /// (false, false, _) => false, // (2) - /// (true, _, false) => false, // (3) + /// (true , _ , true ) => true, // (0) + /// (_ , true , _ ) => true, // (1) + /// (false, false, _ ) => false, // (2) + /// (true , _ , false) => false, // (3) /// } + /// # ; /// ``` /// /// In that case, after we test on `x`, there are 2 overlapping candidate @@ -1502,14 +1503,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// with precisely the reachable arms being reachable - but that problem /// is trivially NP-complete: /// - /// ```rust - /// match (var0, var1, var2, var3, ...) { - /// (true, _, _, false, true, ...) => false, - /// (_, true, true, false, _, ...) => false, - /// (false, _, false, false, _, ...) => false, - /// ... - /// _ => true - /// } + /// ```ignore (illustrative) + /// match (var0, var1, var2, var3, ...) { + /// (true , _ , _ , false, true, ...) => false, + /// (_ , true, true , false, _ , ...) => false, + /// (false, _ , false, false, _ , ...) => false, + /// ... + /// _ => true + /// } /// ``` /// /// Here the last arm is reachable only if there is an assignment to @@ -1520,7 +1521,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// our simplistic treatment of constants and guards would make it occur /// in very common situations - for example [#29740]: /// - /// ```rust + /// ```ignore (illustrative) /// match x { /// "foo" if foo_guard => ..., /// "bar" if bar_guard => ..., @@ -1569,7 +1570,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { mut candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>], block: BasicBlock, otherwise_block: &mut Option<BasicBlock>, - fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>, + fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>, ) { // extract the match-pair from the highest priority candidate let match_pair = &candidates.first().unwrap().match_pairs[0]; @@ -1712,7 +1713,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// by a MIR pass run after borrow checking. fn calculate_fake_borrows<'b>( &mut self, - fake_borrows: &'b FxHashSet<Place<'tcx>>, + fake_borrows: &'b FxIndexSet<Place<'tcx>>, temp_span: Span, ) -> Vec<(Place<'tcx>, Local)> { let tcx = self.tcx; @@ -1738,9 +1739,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { all_fake_borrows.push(place.as_ref()); } - // Deduplicate and ensure a deterministic order. - all_fake_borrows.sort(); - all_fake_borrows.dedup(); + // Deduplicate + let mut dedup = FxHashSet::default(); + all_fake_borrows.retain(|b| dedup.insert(*b)); debug!("add_fake_borrows all_fake_borrows = {:?}", all_fake_borrows); @@ -1766,7 +1767,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Pat binding - used for `let` and function parameters as well. impl<'a, 'tcx> Builder<'a, 'tcx> { - crate fn lower_let_expr( + pub(crate) fn lower_let_expr( &mut self, mut block: BasicBlock, expr: &Expr<'tcx>, @@ -1855,7 +1856,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { parent_bindings .iter() .flat_map(|(_, ascriptions)| ascriptions) - .chain(&candidate.ascriptions), + .cloned() + .chain(candidate.ascriptions), ); // rust-lang/rust#27282: The `autoref` business deserves some @@ -2059,32 +2061,24 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Append `AscribeUserType` statements onto the end of `block` /// for each ascription - fn ascribe_types<'b>( + fn ascribe_types( &mut self, block: BasicBlock, - ascriptions: impl IntoIterator<Item = &'b Ascription<'tcx>>, - ) where - 'tcx: 'b, - { + ascriptions: impl IntoIterator<Item = Ascription<'tcx>>, + ) { for ascription in ascriptions { - let source_info = self.source_info(ascription.span); - - debug!( - "adding user ascription at span {:?} of place {:?} and {:?}", - source_info.span, ascription.source, ascription.user_ty, - ); + let source_info = self.source_info(ascription.annotation.span); - let user_ty = ascription.user_ty.user_ty( - &mut self.canonical_user_type_annotations, - ascription.source.ty(&self.local_decls, self.tcx).ty, - source_info.span, - ); + let base = self.canonical_user_type_annotations.push(ascription.annotation); self.cfg.push( block, Statement { source_info, kind: StatementKind::AscribeUserType( - Box::new((ascription.source, user_ty)), + Box::new(( + ascription.source, + UserTypeProjection { base, projs: Vec::new() }, + )), ascription.variance, ), }, diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/build/matches/simplify.rs index 7f53d9dd705..b4a0c965d6b 100644 --- a/compiler/rustc_mir_build/src/build/matches/simplify.rs +++ b/compiler/rustc_mir_build/src/build/matches/simplify.rs @@ -152,15 +152,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { match *match_pair.pattern.kind { PatKind::AscribeUserType { ref subpattern, - ascription: thir::Ascription { variance, user_ty, user_ty_span }, + ascription: thir::Ascription { ref annotation, variance }, } => { // Apply the type ascription to the value at `match_pair.place`, which is the if let Ok(place_resolved) = match_pair.place.clone().try_upvars_resolved(self.tcx, self.typeck_results) { candidate.ascriptions.push(Ascription { - span: user_ty_span, - user_ty, + annotation: annotation.clone(), source: place_resolved.into_place(self.tcx, self.typeck_results), variance, }); @@ -228,9 +227,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { _ => (None, 0), }; if let Some((min, max, sz)) = range { - if let (Some(lo), Some(hi)) = - (lo.val().try_to_bits(sz), hi.val().try_to_bits(sz)) - { + if let (Some(lo), Some(hi)) = (lo.try_to_bits(sz), hi.try_to_bits(sz)) { // We want to compare ranges numerically, but the order of the bitwise // representation of signed integers does not match their numeric order. // Thus, to correct the ordering, we need to shift the range of signed diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index 0e9e9869376..565345595d5 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -86,7 +86,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { test_place: &PlaceBuilder<'tcx>, candidate: &Candidate<'pat, 'tcx>, switch_ty: Ty<'tcx>, - options: &mut FxIndexMap<ty::Const<'tcx>, u128>, + options: &mut FxIndexMap<ConstantKind<'tcx>, u128>, ) -> bool { let Some(match_pair) = candidate.match_pairs.iter().find(|mp| mp.place == *test_place) else { return false; @@ -366,7 +366,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block: BasicBlock, make_target_blocks: impl FnOnce(&mut Self) -> Vec<BasicBlock>, source_info: SourceInfo, - value: ty::Const<'tcx>, + value: ConstantKind<'tcx>, place: Place<'tcx>, mut ty: Ty<'tcx>, ) { @@ -760,7 +760,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { span_bug!(match_pair.pattern.span, "simplifyable pattern found: {:?}", match_pair.pattern) } - fn const_range_contains(&self, range: PatRange<'tcx>, value: ty::Const<'tcx>) -> Option<bool> { + fn const_range_contains( + &self, + range: PatRange<'tcx>, + value: ConstantKind<'tcx>, + ) -> Option<bool> { use std::cmp::Ordering::*; let tcx = self.tcx; @@ -777,7 +781,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn values_not_contained_in_range( &self, range: PatRange<'tcx>, - options: &FxIndexMap<ty::Const<'tcx>, u128>, + options: &FxIndexMap<ConstantKind<'tcx>, u128>, ) -> Option<bool> { for &val in options.keys() { if self.const_range_contains(range, val)? { @@ -834,7 +838,7 @@ fn trait_method<'tcx>( .find(|item| item.kind == ty::AssocKind::Fn) .expect("trait method not found"); - let method_ty = tcx.type_of(item.def_id); + let method_ty = tcx.bound_type_of(item.def_id); let method_ty = method_ty.subst(tcx, substs); ConstantKind::zero_sized(method_ty) diff --git a/compiler/rustc_mir_build/src/build/matches/util.rs b/compiler/rustc_mir_build/src/build/matches/util.rs index 88dd76e37c1..9a1e98d3bb1 100644 --- a/compiler/rustc_mir_build/src/build/matches/util.rs +++ b/compiler/rustc_mir_build/src/build/matches/util.rs @@ -8,7 +8,7 @@ use smallvec::SmallVec; use std::convert::TryInto; impl<'a, 'tcx> Builder<'a, 'tcx> { - crate fn field_match_pairs<'pat>( + pub(crate) fn field_match_pairs<'pat>( &mut self, place: PlaceBuilder<'tcx>, subpatterns: &'pat [FieldPat<'tcx>], @@ -22,7 +22,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { .collect() } - crate fn prefix_slice_suffix<'pat>( + pub(crate) fn prefix_slice_suffix<'pat>( &mut self, match_pairs: &mut SmallVec<[MatchPair<'pat, 'tcx>; 1]>, place: &PlaceBuilder<'tcx>, @@ -79,7 +79,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Creates a false edge to `imaginary_target` and a real edge to /// real_target. If `imaginary_target` is none, or is the same as the real /// target, a Goto is generated instead to simplify the generated MIR. - crate fn false_edges( + pub(crate) fn false_edges( &mut self, from_block: BasicBlock, real_target: BasicBlock, @@ -100,7 +100,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } impl<'pat, 'tcx> MatchPair<'pat, 'tcx> { - crate fn new(place: PlaceBuilder<'tcx>, pattern: &'pat Pat<'tcx>) -> MatchPair<'pat, 'tcx> { + pub(crate) fn new( + place: PlaceBuilder<'tcx>, + pattern: &'pat Pat<'tcx>, + ) -> MatchPair<'pat, 'tcx> { MatchPair { place, pattern } } } diff --git a/compiler/rustc_mir_build/src/build/misc.rs b/compiler/rustc_mir_build/src/build/misc.rs index 84762d602f8..86f466ff767 100644 --- a/compiler/rustc_mir_build/src/build/misc.rs +++ b/compiler/rustc_mir_build/src/build/misc.rs @@ -3,7 +3,6 @@ use crate::build::Builder; -use rustc_middle::mir; use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty}; use rustc_span::{Span, DUMMY_SP}; @@ -15,7 +14,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// /// N.B., **No cleanup is scheduled for this temporary.** You should /// call `schedule_drop` once the temporary is initialized. - crate fn temp(&mut self, ty: Ty<'tcx>, span: Span) -> Place<'tcx> { + pub(crate) fn temp(&mut self, ty: Ty<'tcx>, span: Span) -> Place<'tcx> { // Mark this local as internal to avoid temporaries with types not present in the // user's code resulting in ICEs from the generator transform. let temp = self.local_decls.push(LocalDecl::new(ty, span).internal()); @@ -26,10 +25,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Convenience function for creating a literal operand, one /// without any user type annotation. - crate fn literal_operand( + pub(crate) fn literal_operand( &mut self, span: Span, - literal: mir::ConstantKind<'tcx>, + literal: ConstantKind<'tcx>, ) -> Operand<'tcx> { let constant = Box::new(Constant { span, user_ty: None, literal }); Operand::Constant(constant) @@ -37,13 +36,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Returns a zero literal operand for the appropriate type, works for // bool, char and integers. - crate fn zero_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> { + pub(crate) fn zero_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> { let literal = ConstantKind::from_bits(self.tcx, 0, ty::ParamEnv::empty().and(ty)); self.literal_operand(span, literal) } - crate fn push_usize( + pub(crate) fn push_usize( &mut self, block: BasicBlock, source_info: SourceInfo, @@ -64,7 +63,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { temp } - crate fn consume_by_copy_or_move(&self, place: Place<'tcx>) -> Operand<'tcx> { + pub(crate) fn consume_by_copy_or_move(&self, place: Place<'tcx>) -> Operand<'tcx> { let tcx = self.tcx; let ty = place.ty(&self.local_decls, tcx).ty; if !self.infcx.type_is_copy_modulo_regions(self.param_env, ty, DUMMY_SP) { diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index ce57e5fe846..c42f2b3a67a 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -1,7 +1,9 @@ use crate::build; use crate::build::expr::as_place::PlaceBuilder; use crate::build::scope::DropKind; +use crate::thir::constant::parse_float; use crate::thir::pattern::pat_from_hir; +use rustc_ast as ast; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -11,17 +13,20 @@ use rustc_index::vec::{Idx, IndexVec}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_middle::hir::place::PlaceBase as HirPlaceBase; use rustc_middle::middle::region; +use rustc_middle::mir::interpret::Allocation; +use rustc_middle::mir::interpret::{ConstValue, LitToConstError, LitToConstInput, Scalar}; use rustc_middle::mir::*; use rustc_middle::thir::{BindingMode, Expr, ExprId, LintLevel, PatKind, Thir}; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeckResults}; use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_target::abi::Size; use rustc_target::spec::abi::Abi; use super::lints; -crate fn mir_built<'tcx>( +pub(crate) fn mir_built<'tcx>( tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalDefId>, ) -> &'tcx rustc_data_structures::steal::Steal<Body<'tcx>> { @@ -177,7 +182,7 @@ fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -> Body<'_ let ty = if fn_sig.c_variadic && index == fn_sig.inputs().len() { let va_list_did = tcx.require_lang_item(LangItem::VaList, Some(arg.span)); - tcx.type_of(va_list_did).subst(tcx, &[tcx.lifetimes.re_erased.into()]) + tcx.bound_type_of(va_list_did).subst(tcx, &[tcx.lifetimes.re_erased.into()]) } else { fn_sig.inputs()[index] }; @@ -260,6 +265,57 @@ fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -> Body<'_ }) } +#[instrument(skip(tcx, lit_input))] +pub(crate) fn lit_to_mir_constant<'tcx>( + tcx: TyCtxt<'tcx>, + lit_input: LitToConstInput<'tcx>, +) -> Result<ConstantKind<'tcx>, LitToConstError> { + let LitToConstInput { lit, ty, neg } = lit_input; + let trunc = |n| { + let param_ty = ty::ParamEnv::reveal_all().and(ty); + let width = tcx.layout_of(param_ty).map_err(|_| LitToConstError::Reported)?.size; + trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits()); + let result = width.truncate(n); + trace!("trunc result: {}", result); + Ok(ConstValue::Scalar(Scalar::from_uint(result, width))) + }; + + let value = match (lit, &ty.kind()) { + (ast::LitKind::Str(s, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_str() => { + let s = s.as_str(); + let allocation = Allocation::from_bytes_byte_aligned_immutable(s.as_bytes()); + let allocation = tcx.intern_const_alloc(allocation); + ConstValue::Slice { data: allocation, start: 0, end: s.len() } + } + (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _)) + if matches!(inner_ty.kind(), ty::Slice(_)) => + { + let allocation = Allocation::from_bytes_byte_aligned_immutable(data as &[u8]); + let allocation = tcx.intern_const_alloc(allocation); + ConstValue::Slice { data: allocation, start: 0, end: data.len() } + } + (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => { + let id = tcx.allocate_bytes(data); + ConstValue::Scalar(Scalar::from_pointer(id.into(), &tcx)) + } + (ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => { + ConstValue::Scalar(Scalar::from_uint(*n, Size::from_bytes(1))) + } + (ast::LitKind::Int(n, _), ty::Uint(_)) | (ast::LitKind::Int(n, _), ty::Int(_)) => { + trunc(if neg { (*n as i128).overflowing_neg().0 as u128 } else { *n })? + } + (ast::LitKind::Float(n, _), ty::Float(fty)) => { + parse_float(*n, *fty, neg).ok_or(LitToConstError::Reported)? + } + (ast::LitKind::Bool(b), ty::Bool) => ConstValue::Scalar(Scalar::from_bool(*b)), + (ast::LitKind::Char(c), ty::Char) => ConstValue::Scalar(Scalar::from_char(*c)), + (ast::LitKind::Err(_), _) => return Err(LitToConstError::Reported), + _ => return Err(LitToConstError::TypeError), + }; + + Ok(ConstantKind::Val(value, ty)) +} + /////////////////////////////////////////////////////////////////////////// // BuildMir -- walks a crate, looking for fn items and methods to build MIR from @@ -342,7 +398,6 @@ struct Builder<'a, 'tcx> { tcx: TyCtxt<'tcx>, infcx: &'a InferCtxt<'a, 'tcx>, typeck_results: &'tcx TypeckResults<'tcx>, - region_scope_tree: &'tcx region::ScopeTree, param_env: ty::ParamEnv<'tcx>, thir: &'a Thir<'tcx>, @@ -679,7 +734,6 @@ where } else { None }; - debug!("fn_id {:?} has attrs {:?}", fn_def, tcx.get_attrs(fn_def.did.to_def_id())); let mut body = builder.finish(); body.spread_arg = spread_arg; @@ -826,7 +880,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { tcx, infcx, typeck_results: tcx.typeck_opt_const_arg(def), - region_scope_tree: tcx.region_scope_tree(def.did), param_env, def_id: def.did.to_def_id(), hir_id, diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index d96534fe3e0..53f9706f021 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -177,7 +177,7 @@ struct IfThenScope { /// The target of an expression that breaks out of a scope #[derive(Clone, Copy, Debug)] -crate enum BreakableTarget { +pub(crate) enum BreakableTarget { Continue(region::Scope), Break(region::Scope), Return, @@ -445,7 +445,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // ========================== // Start a breakable scope, which tracks where `continue`, `break` and // `return` should branch to. - crate fn in_breakable_scope<F>( + pub(crate) fn in_breakable_scope<F>( &mut self, loop_block: Option<BasicBlock>, break_destination: Place<'tcx>, @@ -507,7 +507,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// - We don't need to keep a stack of scopes in the `Builder` because the /// 'else' paths will only leave the innermost scope. /// - This is also used for match guards. - crate fn in_if_then_scope<F>( + pub(crate) fn in_if_then_scope<F>( &mut self, region_scope: region::Scope, f: F, @@ -530,7 +530,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { (then_block, else_block) } - crate fn in_opt_scope<F, R>( + pub(crate) fn in_opt_scope<F, R>( &mut self, opt_scope: Option<(region::Scope, SourceInfo)>, f: F, @@ -553,7 +553,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Convenience wrapper that pushes a scope and then executes `f` /// to build its contents, popping the scope afterwards. - crate fn in_scope<F, R>( + pub(crate) fn in_scope<F, R>( &mut self, region_scope: (region::Scope, SourceInfo), lint_level: LintLevel, @@ -597,14 +597,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// scope and call `pop_scope` afterwards. Note that these two /// calls must be paired; using `in_scope` as a convenience /// wrapper maybe preferable. - crate fn push_scope(&mut self, region_scope: (region::Scope, SourceInfo)) { + pub(crate) fn push_scope(&mut self, region_scope: (region::Scope, SourceInfo)) { self.scopes.push_scope(region_scope, self.source_scope); } /// Pops a scope, which should have region scope `region_scope`, /// adding any drops onto the end of `block` that are needed. /// This must match 1-to-1 with `push_scope`. - crate fn pop_scope( + pub(crate) fn pop_scope( &mut self, region_scope: (region::Scope, SourceInfo), mut block: BasicBlock, @@ -619,7 +619,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } /// Sets up the drops for breaking from `block` to `target`. - crate fn break_scope( + pub(crate) fn break_scope( &mut self, mut block: BasicBlock, value: Option<&Expr<'tcx>>, @@ -698,7 +698,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.start_new_block().unit() } - crate fn break_for_else( + pub(crate) fn break_for_else( &mut self, block: BasicBlock, target: region::Scope, @@ -756,7 +756,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } /// Creates a new source scope, nested in the current one. - crate fn new_source_scope( + pub(crate) fn new_source_scope( &mut self, span: Span, lint_level: LintLevel, @@ -791,7 +791,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } /// Given a span and the current source scope, make a SourceInfo. - crate fn source_info(&self, span: Span) -> SourceInfo { + pub(crate) fn source_info(&self, span: Span) -> SourceInfo { SourceInfo { span, scope: self.source_scope } } @@ -803,26 +803,26 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// scope (which can be larger or smaller). /// /// Consider: - /// - /// let x = foo(bar(X, Y)); - /// + /// ```ignore (illustrative) + /// let x = foo(bar(X, Y)); + /// ``` /// We wish to pop the storage for X and Y after `bar()` is /// called, not after the whole `let` is completed. /// /// As another example, if the second argument diverges: - /// - /// foo(Box::new(2), panic!()) - /// + /// ```ignore (illustrative) + /// foo(Box::new(2), panic!()) + /// ``` /// We would allocate the box but then free it on the unwinding /// path; we would also emit a free on the 'success' path from /// panic, but that will turn out to be removed as dead-code. - crate fn local_scope(&self) -> region::Scope { + pub(crate) fn local_scope(&self) -> region::Scope { self.scopes.topmost() } // Scheduling drops // ================ - crate fn schedule_drop_storage_and_value( + pub(crate) fn schedule_drop_storage_and_value( &mut self, span: Span, region_scope: region::Scope, @@ -836,7 +836,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// /// When called with `DropKind::Storage`, `place` shouldn't be the return /// place, or a function parameter. - crate fn schedule_drop( + pub(crate) fn schedule_drop( &mut self, span: Span, region_scope: region::Scope, @@ -916,7 +916,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } if scope.region_scope == region_scope { - let region_scope_span = region_scope.span(self.tcx, &self.region_scope_tree); + let region_scope_span = + region_scope.span(self.tcx, &self.typeck_results.region_scope_tree); // Attribute scope exit drops to scope's closing brace. let scope_end = self.tcx.sess.source_map().end_point(region_scope_span); @@ -944,7 +945,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// /// Example: when compiling the call to `foo` here: /// - /// ```rust + /// ```ignore (illustrative) /// foo(bar(), ...) /// ``` /// @@ -955,7 +956,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// dropped). However, if no unwind occurs, then `_X` will be /// unconditionally consumed by the `call`: /// - /// ``` + /// ```ignore (illustrative) /// bb { /// ... /// _R = CALL(foo, _X, ...) @@ -969,7 +970,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// spurious borrow-check errors -- the problem, ironically, is /// not the `DROP(_X)` itself, but the (spurious) unwind pathways /// that it creates. See #64391 for an example. - crate fn record_operands_moved(&mut self, operands: &[Operand<'tcx>]) { + pub(crate) fn record_operands_moved(&mut self, operands: &[Operand<'tcx>]) { let local_scope = self.local_scope(); let scope = self.scopes.scopes.last_mut().unwrap(); @@ -1026,7 +1027,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// /// This path terminates in Resume. The path isn't created until after all /// of the non-unwind paths in this item have been lowered. - crate fn diverge_from(&mut self, start: BasicBlock) { + pub(crate) fn diverge_from(&mut self, start: BasicBlock) { debug_assert!( matches!( self.cfg.block_data(start).terminator().kind, @@ -1048,7 +1049,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// [TerminatorKind::Yield]. /// /// This path terminates in GeneratorDrop. - crate fn generator_drop_cleanup(&mut self, yield_block: BasicBlock) { + pub(crate) fn generator_drop_cleanup(&mut self, yield_block: BasicBlock) { debug_assert!( matches!( self.cfg.block_data(yield_block).terminator().kind, @@ -1078,7 +1079,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } /// Utility function for *non*-scope code to build their own drops - crate fn build_drop_and_replace( + pub(crate) fn build_drop_and_replace( &mut self, block: BasicBlock, span: Span, @@ -1101,7 +1102,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Creates an `Assert` terminator and return the success block. /// If the boolean condition operand is not the expected value, /// a runtime panic will be caused with the given message. - crate fn assert( + pub(crate) fn assert( &mut self, block: BasicBlock, cond: Operand<'tcx>, @@ -1126,7 +1127,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// /// This is only needed for `match` arm scopes, because they have one /// entrance per pattern, but only one exit. - crate fn clear_top_scope(&mut self, region_scope: region::Scope) { + pub(crate) fn clear_top_scope(&mut self, region_scope: region::Scope) { let top_scope = self.scopes.scopes.last_mut().unwrap(); assert_eq!(top_scope.region_scope, region_scope); @@ -1262,7 +1263,7 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> { } /// Build the unwind and generator drop trees. - crate fn build_drop_trees(&mut self) { + pub(crate) fn build_drop_trees(&mut self) { if self.generator_kind.is_some() { self.build_generator_drop_trees(); } else { diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index a841cce23de..94b2722dca8 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -27,7 +27,7 @@ struct UnsafetyVisitor<'a, 'tcx> { body_unsafety: BodyUnsafety, /// The `#[target_feature]` attributes of the body. Used for checking /// calls to functions with `#[target_feature]` (RFC 2396). - body_target_features: &'tcx Vec<Symbol>, + body_target_features: &'tcx [Symbol], /// When inside the LHS of an assignment to a field, this is the type /// of the LHS and the span of the assignment expression. assignment_info: Option<(Ty<'tcx>, Span)>, @@ -643,9 +643,8 @@ pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalD return; } - let (thir, expr) = match tcx.thir_body(def) { - Ok(body) => body, - Err(_) => return, + let Ok((thir, expr)) = tcx.thir_body(def) else { + return }; let thir = &thir.borrow(); // If `thir` is empty, a type error occurred, skip this body. @@ -661,7 +660,7 @@ pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalD BodyUnsafety::Safe } }); - let body_target_features = &tcx.codegen_fn_attrs(def.did).target_features; + let body_target_features = &tcx.body_codegen_attrs(def.did.to_def_id()).target_features; let safety_context = if body_unsafety.is_unsafe() { SafetyContext::UnsafeFn } else { SafetyContext::Safe }; let mut visitor = UnsafetyVisitor { @@ -679,7 +678,7 @@ pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalD visitor.visit_expr(&thir[expr]); } -crate fn thir_check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) { +pub(crate) fn thir_check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) { if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) { tcx.thir_check_unsafety_for_const_arg(def) } else { @@ -687,7 +686,7 @@ crate fn thir_check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) { } } -crate fn thir_check_unsafety_for_const_arg<'tcx>( +pub(crate) fn thir_check_unsafety_for_const_arg<'tcx>( tcx: TyCtxt<'tcx>, (did, param_did): (LocalDefId, DefId), ) { diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs index 4ffee59a962..11cd2a9aa4d 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs @@ -4,7 +4,6 @@ #![allow(rustc::potential_query_instability)] #![feature(box_patterns)] #![feature(control_flow_enum)] -#![feature(crate_visibility_modifier)] #![feature(if_let_guard)] #![feature(let_chains)] #![feature(let_else)] @@ -27,6 +26,7 @@ use rustc_middle::ty::query::Providers; pub fn provide(providers: &mut Providers) { providers.check_match = thir::pattern::check_match; providers.lit_to_const = thir::constant::lit_to_const; + providers.lit_to_mir_constant = build::lit_to_mir_constant; providers.mir_built = build::mir_built; providers.thir_check_unsafety = check_unsafety::thir_check_unsafety; providers.thir_check_unsafety_for_const_arg = check_unsafety::thir_check_unsafety_for_const_arg; diff --git a/compiler/rustc_mir_build/src/lints.rs b/compiler/rustc_mir_build/src/lints.rs index bccff379873..5470cc1262e 100644 --- a/compiler/rustc_mir_build/src/lints.rs +++ b/compiler/rustc_mir_build/src/lints.rs @@ -9,7 +9,7 @@ use rustc_session::lint::builtin::UNCONDITIONAL_RECURSION; use rustc_span::Span; use std::ops::ControlFlow; -crate fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { +pub(crate) fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { let def_id = body.source.def_id().expect_local(); if let Some(fn_kind) = tcx.hir().get_by_def_id(def_id).fn_kind() { diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index 30d7fdb7fec..d82e6688633 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -8,7 +8,7 @@ use rustc_span::symbol::Symbol; use rustc_target::abi::Size; // FIXME Once valtrees are available, get rid of this function and the query -crate fn lit_to_const<'tcx>( +pub(crate) fn lit_to_const<'tcx>( tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx>, ) -> Result<ty::Const<'tcx>, LitToConstError> { diff --git a/compiler/rustc_mir_build/src/thir/cx/block.rs b/compiler/rustc_mir_build/src/thir/cx/block.rs index 2d9b5c1d98a..59750d5d0b8 100644 --- a/compiler/rustc_mir_build/src/thir/cx/block.rs +++ b/compiler/rustc_mir_build/src/thir/cx/block.rs @@ -6,9 +6,10 @@ use rustc_middle::thir::*; use rustc_middle::ty; use rustc_index::vec::Idx; +use rustc_middle::ty::CanonicalUserTypeAnnotation; impl<'tcx> Cx<'tcx> { - crate fn mirror_block(&mut self, block: &'tcx hir::Block<'tcx>) -> Block { + pub(crate) fn mirror_block(&mut self, block: &'tcx hir::Block<'tcx>) -> Block { // We have to eagerly lower the "spine" of the statements // in order to get the lexical scoping correctly. let stmts = self.mirror_stmts(block.hir_id.local_id, block.stmts); @@ -74,19 +75,24 @@ impl<'tcx> Cx<'tcx> { }; let mut pattern = self.pattern_from_hir(local.pat); + debug!(?pattern); if let Some(ty) = &local.ty { if let Some(&user_ty) = self.typeck_results.user_provided_types().get(ty.hir_id) { debug!("mirror_stmts: user_ty={:?}", user_ty); + let annotation = CanonicalUserTypeAnnotation { + user_ty, + span: ty.span, + inferred_ty: self.typeck_results.node_type(ty.hir_id), + }; pattern = Pat { ty: pattern.ty, span: pattern.span, kind: Box::new(PatKind::AscribeUserType { ascription: Ascription { - user_ty: PatTyProj::from_user_type(user_ty), - user_ty_span: ty.span, + annotation, variance: ty::Variance::Covariant, }, subpattern: pattern, diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index f382f79af29..480c5b195cc 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -22,17 +22,18 @@ use rustc_span::Span; use rustc_target::abi::VariantIdx; impl<'tcx> Cx<'tcx> { - crate fn mirror_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) -> ExprId { + 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)) } - crate fn mirror_exprs(&mut self, exprs: &'tcx [hir::Expr<'tcx>]) -> Box<[ExprId]> { + pub(crate) fn mirror_exprs(&mut self, exprs: &'tcx [hir::Expr<'tcx>]) -> Box<[ExprId]> { exprs.iter().map(|expr| self.mirror_expr_inner(expr)).collect() } pub(super) fn mirror_expr_inner(&mut self, hir_expr: &'tcx hir::Expr<'tcx>) -> ExprId { - let temp_lifetime = self.region_scope_tree.temporary_scope(hir_expr.hir_id.local_id); + let temp_lifetime = + self.rvalue_scopes.temporary_scope(self.region_scope_tree, hir_expr.hir_id.local_id); let expr_scope = region::Scope { id: hir_expr.hir_id.local_id, data: region::ScopeData::Node }; @@ -158,9 +159,11 @@ impl<'tcx> Cx<'tcx> { } fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> { + let tcx = self.tcx; let expr_ty = self.typeck_results().expr_ty(expr); let expr_span = expr.span; - let temp_lifetime = self.region_scope_tree.temporary_scope(expr.hir_id.local_id); + let temp_lifetime = + self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id); let kind = match expr.kind { // Here comes the interesting stuff: @@ -196,7 +199,7 @@ impl<'tcx> Cx<'tcx> { let arg_tys = args.iter().map(|e| self.typeck_results().expr_ty_adjusted(e)); let tupled_args = Expr { - ty: self.tcx.mk_tup(arg_tys), + ty: tcx.mk_tup(arg_tys), temp_lifetime, span: expr.span, kind: ExprKind::Tuple { fields: self.mirror_exprs(args) }, @@ -488,24 +491,24 @@ impl<'tcx> Cx<'tcx> { out_expr: out_expr.as_ref().map(|expr| self.mirror_expr(expr)), }, hir::InlineAsmOperand::Const { ref anon_const } => { - let anon_const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id); + let anon_const_def_id = tcx.hir().local_def_id(anon_const.hir_id); let value = mir::ConstantKind::from_anon_const( - self.tcx, + tcx, anon_const_def_id, self.param_env, ); - let span = self.tcx.hir().span(anon_const.hir_id); + let span = tcx.hir().span(anon_const.hir_id); InlineAsmOperand::Const { value, span } } hir::InlineAsmOperand::SymFn { ref anon_const } => { - let anon_const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id); + let anon_const_def_id = tcx.hir().local_def_id(anon_const.hir_id); let value = mir::ConstantKind::from_anon_const( - self.tcx, + tcx, anon_const_def_id, self.param_env, ); - let span = self.tcx.hir().span(anon_const.hir_id); + let span = tcx.hir().span(anon_const.hir_id); InlineAsmOperand::SymFn { value, span } } @@ -519,21 +522,16 @@ impl<'tcx> Cx<'tcx> { }, hir::ExprKind::ConstBlock(ref anon_const) => { - let tcx = self.tcx; - let local_def_id = tcx.hir().local_def_id(anon_const.hir_id); - let anon_const_def_id = local_def_id.to_def_id(); - - // Need to include the parent substs - let hir_id = tcx.hir().local_def_id_to_hir_id(local_def_id); - let ty = tcx.typeck(local_def_id).node_type(hir_id); - let typeck_root_def_id = tcx.typeck_root_def_id(anon_const_def_id); + let ty = self.typeck_results().node_type(anon_const.hir_id); + let did = tcx.hir().local_def_id(anon_const.hir_id).to_def_id(); + let typeck_root_def_id = tcx.typeck_root_def_id(did); let parent_substs = tcx.erase_regions(InternalSubsts::identity_for_item(tcx, typeck_root_def_id)); let substs = InlineConstSubsts::new(tcx, InlineConstSubstsParts { parent_substs, ty }) .substs; - ExprKind::ConstBlock { did: anon_const_def_id, substs } + ExprKind::ConstBlock { did, substs } } // Now comes the rote stuff: hir::ExprKind::Repeat(ref v, _) => { @@ -579,7 +577,9 @@ impl<'tcx> Cx<'tcx> { }, hir::ExprKind::Loop(ref body, ..) => { let block_ty = self.typeck_results().node_type(body.hir_id); - let temp_lifetime = self.region_scope_tree.temporary_scope(body.hir_id.local_id); + let temp_lifetime = self + .rvalue_scopes + .temporary_scope(self.region_scope_tree, body.hir_id.local_id); let block = self.mirror_block(body); let body = self.thir.exprs.push(Expr { ty: block_ty, @@ -591,7 +591,7 @@ impl<'tcx> Cx<'tcx> { } hir::ExprKind::Field(ref source, ..) => ExprKind::Field { lhs: self.mirror_expr(source), - name: Field::new(self.tcx.field_index(expr.hir_id, self.typeck_results)), + name: Field::new(tcx.field_index(expr.hir_id, self.typeck_results)), }, hir::ExprKind::Cast(ref source, ref cast_ty) => { // Check for a user-given type annotation on this `cast` @@ -640,7 +640,7 @@ impl<'tcx> Cx<'tcx> { let (d, o) = adt_def.discriminant_def_for_variant(idx); use rustc_middle::ty::util::IntTypeExt; let ty = adt_def.repr().discr_type(); - let ty = ty.to_ty(self.tcx()); + let ty = ty.to_ty(tcx); Some((d, o, ty)) } _ => None, @@ -652,8 +652,7 @@ impl<'tcx> Cx<'tcx> { let source = if let Some((did, offset, var_ty)) = var { let param_env_ty = self.param_env.and(var_ty); - let size = self - .tcx + let size = tcx .layout_of(param_env_ty) .unwrap_or_else(|e| { panic!("could not compute layout for {:?}: {:?}", param_env_ty, e) @@ -671,7 +670,7 @@ impl<'tcx> Cx<'tcx> { Some(did) => { // in case we are offsetting from a computed discriminant // and not the beginning of discriminants (which is always `0`) - let substs = InternalSubsts::identity_for_item(self.tcx(), did); + let substs = InternalSubsts::identity_for_item(tcx, did); let kind = ExprKind::NamedConst { def_id: did, substs, user_ty: None }; let lhs = self.thir.exprs.push(Expr { @@ -781,7 +780,8 @@ impl<'tcx> Cx<'tcx> { span: Span, overloaded_callee: Option<(DefId, SubstsRef<'tcx>)>, ) -> Expr<'tcx> { - let temp_lifetime = self.region_scope_tree.temporary_scope(expr.hir_id.local_id); + let temp_lifetime = + self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id); let (def_id, substs, user_ty) = match overloaded_callee { Some((def_id, substs)) => (def_id, substs, None), None => { @@ -803,8 +803,8 @@ impl<'tcx> Cx<'tcx> { pattern: self.pattern_from_hir(&arm.pat), guard: arm.guard.as_ref().map(|g| match g { hir::Guard::If(ref e) => Guard::If(self.mirror_expr(e)), - hir::Guard::IfLet(ref pat, ref e) => { - Guard::IfLet(self.pattern_from_hir(pat), self.mirror_expr(e)) + hir::Guard::IfLet(ref l) => { + Guard::IfLet(self.pattern_from_hir(l.pat), self.mirror_expr(l.init)) } }), body: self.mirror_expr(arm.body), @@ -868,7 +868,9 @@ impl<'tcx> Cx<'tcx> { // a constant reference (or constant raw pointer for `static mut`) in MIR Res::Def(DefKind::Static(_), id) => { let ty = self.tcx.static_ptr_ty(id); - let temp_lifetime = self.region_scope_tree.temporary_scope(expr.hir_id.local_id); + let temp_lifetime = self + .rvalue_scopes + .temporary_scope(self.region_scope_tree, expr.hir_id.local_id); let kind = if self.tcx.is_thread_local_static(id) { ExprKind::ThreadLocalRef(id) } else { @@ -944,7 +946,8 @@ impl<'tcx> Cx<'tcx> { // construct the complete expression `foo()` for the overloaded call, // which will yield the &T type - let temp_lifetime = self.region_scope_tree.temporary_scope(expr.hir_id.local_id); + let temp_lifetime = + self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id); let fun = self.method_callee(expr, span, overloaded_callee); let fun = self.thir.exprs.push(fun); let fun_ty = self.thir[fun].ty; @@ -964,7 +967,9 @@ impl<'tcx> Cx<'tcx> { closure_expr: &'tcx hir::Expr<'tcx>, place: HirPlace<'tcx>, ) -> Expr<'tcx> { - let temp_lifetime = self.region_scope_tree.temporary_scope(closure_expr.hir_id.local_id); + let temp_lifetime = self + .rvalue_scopes + .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_typeck/check/upvar.rs`represents a captured path @@ -1019,7 +1024,9 @@ impl<'tcx> Cx<'tcx> { let upvar_capture = captured_place.info.capture_kind; let captured_place_expr = self.convert_captured_hir_place(closure_expr, captured_place.place.clone()); - let temp_lifetime = self.region_scope_tree.temporary_scope(closure_expr.hir_id.local_id); + let temp_lifetime = self + .rvalue_scopes + .temporary_scope(self.region_scope_tree, closure_expr.hir_id.local_id); match upvar_capture { ty::UpvarCapture::ByValue => captured_place_expr, diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index f17fe38b292..bd17df60cd7 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -5,6 +5,7 @@ use crate::thir::pattern::pat_from_hir; use crate::thir::util::UserAnnotatedTyHelpers; +use rustc_ast as ast; use rustc_data_structures::steal::Steal; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; @@ -12,11 +13,13 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::HirId; use rustc_hir::Node; use rustc_middle::middle::region; +use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; +use rustc_middle::mir::ConstantKind; use rustc_middle::thir::*; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, RvalueScopes, Ty, TyCtxt}; use rustc_span::Span; -crate fn thir_body<'tcx>( +pub(crate) fn thir_body<'tcx>( tcx: TyCtxt<'tcx>, owner_def: ty::WithOptConstParam<LocalDefId>, ) -> Result<(&'tcx Steal<Thir<'tcx>>, ExprId), ErrorGuaranteed> { @@ -30,7 +33,7 @@ crate fn thir_body<'tcx>( Ok((tcx.alloc_steal_thir(cx.thir), expr)) } -crate fn thir_tree<'tcx>( +pub(crate) fn thir_tree<'tcx>( tcx: TyCtxt<'tcx>, owner_def: ty::WithOptConstParam<LocalDefId>, ) -> String { @@ -44,10 +47,11 @@ struct Cx<'tcx> { tcx: TyCtxt<'tcx>, thir: Thir<'tcx>, - crate param_env: ty::ParamEnv<'tcx>, + pub(crate) param_env: ty::ParamEnv<'tcx>, - crate region_scope_tree: &'tcx region::ScopeTree, - crate typeck_results: &'tcx ty::TypeckResults<'tcx>, + pub(crate) region_scope_tree: &'tcx region::ScopeTree, + pub(crate) typeck_results: &'tcx ty::TypeckResults<'tcx>, + pub(crate) rvalue_scopes: &'tcx RvalueScopes, /// When applying adjustments to the expression /// with the given `HirId`, use the given `Span`, @@ -68,14 +72,34 @@ impl<'tcx> Cx<'tcx> { tcx, thir: Thir::new(), param_env: tcx.param_env(def.did), - region_scope_tree: tcx.region_scope_tree(def.did), + region_scope_tree: &typeck_results.region_scope_tree, typeck_results, + rvalue_scopes: &typeck_results.rvalue_scopes, body_owner: def.did.to_def_id(), adjustment_span: None, } } - crate fn pattern_from_hir(&mut self, p: &hir::Pat<'_>) -> Pat<'tcx> { + #[instrument(skip(self), level = "debug")] + pub(crate) fn const_eval_literal( + &mut self, + lit: &'tcx ast::LitKind, + ty: Ty<'tcx>, + sp: Span, + neg: bool, + ) -> ConstantKind<'tcx> { + match self.tcx.at(sp).lit_to_mir_constant(LitToConstInput { lit, ty, neg }) { + Ok(c) => c, + Err(LitToConstError::Reported) => { + // create a dummy value and continue compiling + ConstantKind::Ty(self.tcx.const_error(ty)) + } + Err(LitToConstError::TypeError) => bug!("const_eval_literal: had type error"), + } + } + + #[tracing::instrument(level = "debug", skip(self))] + pub(crate) fn pattern_from_hir(&mut self, p: &hir::Pat<'_>) -> Pat<'tcx> { let p = match self.tcx.hir().get(p.hir_id) { Node::Pat(p) | Node::Binding(p) => p, node => bug!("pattern became {:?}", node), diff --git a/compiler/rustc_mir_build/src/thir/mod.rs b/compiler/rustc_mir_build/src/thir/mod.rs index ddbe1b0b69c..e0e6ac26654 100644 --- a/compiler/rustc_mir_build/src/thir/mod.rs +++ b/compiler/rustc_mir_build/src/thir/mod.rs @@ -4,10 +4,10 @@ //! unit-tested and separated from the Rust source and compiler data //! structures. -crate mod constant; +pub(crate) mod constant; -crate mod cx; +pub(crate) mod cx; -crate mod pattern; +pub(crate) mod pattern; mod util; diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 58e484e413d..dc204eb47ae 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -23,7 +23,7 @@ use rustc_session::Session; use rustc_span::source_map::Spanned; use rustc_span::{BytePos, DesugaringKind, ExpnKind, Span}; -crate fn check_match(tcx: TyCtxt<'_>, def_id: DefId) { +pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: DefId) { let body_id = match def_id.as_local() { None => return, Some(id) => tcx.hir().body_owned_by(tcx.hir().local_def_id_to_hir_id(id)), @@ -173,10 +173,10 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { for arm in hir_arms { // Check the arm for some things unrelated to exhaustiveness. self.check_patterns(&arm.pat, Refutable); - if let Some(hir::Guard::IfLet(ref pat, _)) = arm.guard { - self.check_patterns(pat, Refutable); - let tpat = self.lower_pattern(&mut cx, pat, &mut false); - self.check_let_reachability(&mut cx, pat.hir_id, tpat, tpat.span()); + if let Some(hir::Guard::IfLet(ref let_expr)) = arm.guard { + self.check_patterns(let_expr.pat, Refutable); + let tpat = self.lower_pattern(&mut cx, let_expr.pat, &mut false); + self.check_let_reachability(&mut cx, let_expr.pat.hir_id, tpat, tpat.span()); } } @@ -880,7 +880,7 @@ fn non_exhaustive_match<'p, 'tcx>( err.emit(); } -crate fn joined_uncovered_patterns<'p, 'tcx>( +pub(crate) fn joined_uncovered_patterns<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, witnesses: &[DeconstructedPat<'p, 'tcx>], ) -> String { @@ -901,7 +901,7 @@ crate fn joined_uncovered_patterns<'p, 'tcx>( } } -crate fn pattern_not_covered_label( +pub(crate) fn pattern_not_covered_label( witnesses: &[DeconstructedPat<'_, '_>], joined_patterns: &str, ) -> String { @@ -1108,9 +1108,9 @@ fn let_source_parent(tcx: TyCtxt<'_>, parent: HirId, pat_id: Option<HirId>) -> L match parent_node { hir::Node::Arm(hir::Arm { - guard: Some(hir::Guard::IfLet(&hir::Pat { hir_id, .. }, _)), + guard: Some(hir::Guard::IfLet(&hir::Let { pat: hir::Pat { hir_id, .. }, .. })), .. - }) if Some(hir_id) == pat_id => { + }) if Some(*hir_id) == pat_id => { return LetSource::IfLetGuard; } hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Let(..), span, .. }) => { diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 2298cc7cddf..880f86aff5d 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -1,7 +1,7 @@ use rustc_hir as hir; use rustc_index::vec::Idx; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; -use rustc_middle::mir::Field; +use rustc_middle::mir::{self, Field}; use rustc_middle::thir::{FieldPat, Pat, PatKind}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt}; @@ -22,7 +22,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { #[instrument(level = "debug", skip(self))] pub(super) fn const_to_pat( &self, - cv: ty::Const<'tcx>, + cv: mir::ConstantKind<'tcx>, id: hir::HirId, span: Span, mir_structural_match_violation: bool, @@ -152,7 +152,11 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { ty.is_structural_eq_shallow(self.infcx.tcx) } - fn to_pat(&mut self, cv: ty::Const<'tcx>, mir_structural_match_violation: bool) -> Pat<'tcx> { + fn to_pat( + &mut self, + cv: mir::ConstantKind<'tcx>, + mir_structural_match_violation: bool, + ) -> Pat<'tcx> { trace!(self.treat_byte_string_as_slice); // This method is just a wrapper handling a validity check; the heavy lifting is // performed by the recursive `recur` method, which is not meant to be @@ -246,7 +250,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { fn field_pats( &self, - vals: impl Iterator<Item = ty::Const<'tcx>>, + vals: impl Iterator<Item = mir::ConstantKind<'tcx>>, ) -> Result<Vec<FieldPat<'tcx>>, FallbackToConstRef> { vals.enumerate() .map(|(idx, val)| { @@ -257,9 +261,10 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { } // Recursive helper for `to_pat`; invoke that (instead of calling this directly). + #[instrument(skip(self), level = "debug")] fn recur( &self, - cv: ty::Const<'tcx>, + cv: mir::ConstantKind<'tcx>, mir_structural_match_violation: bool, ) -> Result<Pat<'tcx>, FallbackToConstRef> { let id = self.id; @@ -365,7 +370,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { PatKind::Wild } ty::Adt(adt_def, substs) if adt_def.is_enum() => { - let destructured = tcx.destructure_const(param_env.and(cv)); + let destructured = tcx.destructure_mir_constant(param_env, cv); PatKind::Variant { adt_def: *adt_def, substs, @@ -376,12 +381,12 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { } } ty::Tuple(_) | ty::Adt(_, _) => { - let destructured = tcx.destructure_const(param_env.and(cv)); + let destructured = tcx.destructure_mir_constant(param_env, cv); PatKind::Leaf { subpatterns: self.field_pats(destructured.fields.iter().copied())? } } ty::Array(..) => PatKind::Array { prefix: tcx - .destructure_const(param_env.and(cv)) + .destructure_mir_constant(param_env, cv) .fields .iter() .map(|val| self.recur(*val, false)) @@ -412,12 +417,12 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { // arrays. ty::Array(..) if !self.treat_byte_string_as_slice => { let old = self.behind_reference.replace(true); - let array = tcx.deref_const(self.param_env.and(cv)); + let array = tcx.deref_mir_constant(self.param_env.and(cv)); let val = PatKind::Deref { subpattern: Pat { kind: Box::new(PatKind::Array { prefix: tcx - .destructure_const(param_env.and(array)) + .destructure_mir_constant(param_env, array) .fields .iter() .map(|val| self.recur(*val, false)) @@ -438,12 +443,12 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { // pattern. ty::Slice(elem_ty) => { let old = self.behind_reference.replace(true); - let array = tcx.deref_const(self.param_env.and(cv)); + let array = tcx.deref_mir_constant(self.param_env.and(cv)); let val = PatKind::Deref { subpattern: Pat { kind: Box::new(PatKind::Slice { prefix: tcx - .destructure_const(param_env.and(array)) + .destructure_mir_constant(param_env, array) .fields .iter() .map(|val| self.recur(*val, false)) @@ -512,7 +517,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { // we fall back to a const pattern. If we do not do this, we may end up with // a !structural-match constant that is not of reference type, which makes it // very hard to invoke `PartialEq::eq` on it as a fallback. - let val = match self.recur(tcx.deref_const(self.param_env.and(cv)), false) { + let val = match self.recur(tcx.deref_mir_constant(self.param_env.and(cv)), false) { Ok(subpattern) => PatKind::Deref { subpattern }, Err(_) => PatKind::Constant { value: cv }, }; diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index 72f0d07c260..b7de3f28872 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -13,7 +13,7 @@ //! Instead of listing all those constructors (which is intractable), we group those value //! constructors together as much as possible. Example: //! -//! ``` +//! ```compile_fail,E0004 //! match (0, false) { //! (0 ..=100, true) => {} // `p_1` //! (50..=150, false) => {} // `p_2` @@ -52,7 +52,7 @@ use rustc_data_structures::captures::Captures; use rustc_index::vec::Idx; use rustc_hir::{HirId, RangeEnd}; -use rustc_middle::mir::Field; +use rustc_middle::mir::{self, Field}; use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange}; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef}; @@ -133,23 +133,35 @@ impl IntRange { } #[inline] - fn from_const<'tcx>( + fn from_constant<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, - value: ty::Const<'tcx>, + value: mir::ConstantKind<'tcx>, ) -> Option<IntRange> { let ty = value.ty(); if let Some((target_size, bias)) = Self::integral_size_and_signed_bias(tcx, ty) { let val = (|| { - if let ty::ConstKind::Value(ConstValue::Scalar(scalar)) = value.val() { - // For this specific pattern we can skip a lot of effort and go - // straight to the result, after doing a bit of checking. (We - // could remove this branch and just fall through, which - // is more general but much slower.) - if let Ok(bits) = scalar.to_bits_or_ptr_internal(target_size).unwrap() { - return Some(bits); + match value { + mir::ConstantKind::Val(ConstValue::Scalar(scalar), _) => { + // For this specific pattern we can skip a lot of effort and go + // straight to the result, after doing a bit of checking. (We + // could remove this branch and just fall through, which + // is more general but much slower.) + if let Ok(Ok(bits)) = scalar.to_bits_or_ptr_internal(target_size) { + return Some(bits); + } else { + return None; + } } + mir::ConstantKind::Ty(c) => match c.val() { + ty::ConstKind::Value(_) => bug!( + "encountered ConstValue in mir::ConstantKind::Ty, whereas this is expected to be in ConstantKind::Val" + ), + _ => {} + }, + _ => {} } + // This is a more general form of the previous case. value.try_eval_bits(tcx, param_env, ty) })()?; @@ -234,8 +246,8 @@ impl IntRange { let (lo, hi) = (lo ^ bias, hi ^ bias); let env = ty::ParamEnv::empty().and(ty); - let lo_const = ty::Const::from_bits(tcx, lo, env); - let hi_const = ty::Const::from_bits(tcx, hi, env); + let lo_const = mir::ConstantKind::from_bits(tcx, lo, env); + let hi_const = mir::ConstantKind::from_bits(tcx, hi, env); let kind = if lo == hi { PatKind::Constant { value: lo_const } @@ -344,13 +356,13 @@ enum IntBorder { /// straddles the boundary of one of the inputs. /// /// The following input: -/// ``` +/// ```text /// |-------------------------| // `self` /// |------| |----------| |----| /// |-------| |-------| /// ``` /// would be iterated over as follows: -/// ``` +/// ```text /// ||---|--||-|---|---|---|--| /// ``` #[derive(Debug, Clone)] @@ -492,14 +504,17 @@ impl Slice { /// /// Let's look at an example, where we are trying to split the last pattern: /// ``` +/// # fn foo(x: &[bool]) { /// match x { /// [true, true, ..] => {} /// [.., false, false] => {} /// [..] => {} /// } +/// # } /// ``` /// Here are the results of specialization for the first few lengths: /// ``` +/// # fn foo(x: &[bool]) { match x { /// // length 0 /// [] => {} /// // length 1 @@ -520,6 +535,8 @@ impl Slice { /// [true, true, _, _, _ ] => {} /// [_, _, _, false, false] => {} /// [_, _, _, _, _ ] => {} +/// # _ => {} +/// # }} /// ``` /// /// If we went above length 5, we would simply be inserting more columns full of wildcards in the @@ -630,9 +647,9 @@ pub(super) enum Constructor<'tcx> { /// Ranges of integer literal values (`2`, `2..=5` or `2..5`). IntRange(IntRange), /// Ranges of floating-point literal values (`2.0..=5.2`). - FloatRange(ty::Const<'tcx>, ty::Const<'tcx>, RangeEnd), + FloatRange(mir::ConstantKind<'tcx>, mir::ConstantKind<'tcx>, RangeEnd), /// String literals. Strings are not quite the same as `&[u8]` so we treat them separately. - Str(ty::Const<'tcx>), + Str(mir::ConstantKind<'tcx>), /// Array and slice patterns. Slice(Slice), /// Constants that must not be matched structurally. They are treated as black @@ -1128,7 +1145,8 @@ impl<'tcx> SplitWildcard<'tcx> { /// In the following example `Fields::wildcards` returns `[_, _, _, _]`. Then in /// `extract_pattern_arguments` we fill some of the entries, and the result is /// `[Some(0), _, _, _]`. -/// ```rust +/// ```compile_fail,E0004 +/// # fn foo() -> [Option<u8>; 4] { [None; 4] } /// let x: [Option<u8>; 4] = foo(); /// match x { /// [Some(0), ..] => {} @@ -1370,7 +1388,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { } } PatKind::Constant { value } => { - if let Some(int_range) = IntRange::from_const(cx.tcx, cx.param_env, *value) { + if let Some(int_range) = IntRange::from_constant(cx.tcx, cx.param_env, *value) { ctor = IntRange(int_range); fields = Fields::empty(); } else { diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 5b0aa4309a8..e0dec1daf63 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -17,32 +17,33 @@ use rustc_hir::RangeEnd; use rustc_index::vec::Idx; use rustc_middle::mir::interpret::{get_slice_bytes, ConstValue}; use rustc_middle::mir::interpret::{ErrorHandled, LitToConstError, LitToConstInput}; -use rustc_middle::mir::UserTypeProjection; +use rustc_middle::mir::{self, UserTypeProjection}; use rustc_middle::mir::{BorrowKind, Field, Mutability}; -use rustc_middle::thir::{Ascription, BindingMode, FieldPat, Pat, PatKind, PatRange, PatTyProj}; +use rustc_middle::thir::{Ascription, BindingMode, FieldPat, Pat, PatKind, PatRange}; use rustc_middle::ty::subst::{GenericArg, SubstsRef}; +use rustc_middle::ty::CanonicalUserTypeAnnotation; use rustc_middle::ty::{self, AdtDef, ConstKind, DefIdTree, Region, Ty, TyCtxt, UserType}; use rustc_span::{Span, Symbol}; use std::cmp::Ordering; #[derive(Clone, Debug)] -crate enum PatternError { +pub(crate) enum PatternError { AssocConstInPattern(Span), ConstParamInPattern(Span), StaticInPattern(Span), NonConstPath(Span), } -crate struct PatCtxt<'a, 'tcx> { - crate tcx: TyCtxt<'tcx>, - crate param_env: ty::ParamEnv<'tcx>, - crate typeck_results: &'a ty::TypeckResults<'tcx>, - crate errors: Vec<PatternError>, +pub(crate) struct PatCtxt<'a, 'tcx> { + pub(crate) tcx: TyCtxt<'tcx>, + pub(crate) param_env: ty::ParamEnv<'tcx>, + pub(crate) typeck_results: &'a ty::TypeckResults<'tcx>, + pub(crate) errors: Vec<PatternError>, include_lint_checks: bool, } -crate fn pat_from_hir<'a, 'tcx>( +pub(crate) fn pat_from_hir<'a, 'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>, @@ -59,7 +60,7 @@ crate fn pat_from_hir<'a, 'tcx>( } impl<'a, 'tcx> PatCtxt<'a, 'tcx> { - crate fn new( + pub(crate) fn new( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>, @@ -67,12 +68,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { PatCtxt { tcx, param_env, typeck_results, errors: vec![], include_lint_checks: false } } - crate fn include_lint_checks(&mut self) -> &mut Self { + pub(crate) fn include_lint_checks(&mut self) -> &mut Self { self.include_lint_checks = true; self } - crate fn lower_pattern(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Pat<'tcx> { + pub(crate) fn lower_pattern(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Pat<'tcx> { // When implicit dereferences have been inserted in this pattern, the unadjusted lowered // pattern has the type that results *after* dereferencing. For example, in this code: // @@ -121,8 +122,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { fn lower_pattern_range( &mut self, ty: Ty<'tcx>, - lo: ty::Const<'tcx>, - hi: ty::Const<'tcx>, + lo: mir::ConstantKind<'tcx>, + hi: mir::ConstantKind<'tcx>, end: RangeEnd, span: Span, ) -> PatKind<'tcx> { @@ -177,16 +178,18 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { ty: Ty<'tcx>, lo: Option<&PatKind<'tcx>>, hi: Option<&PatKind<'tcx>>, - ) -> Option<(ty::Const<'tcx>, ty::Const<'tcx>)> { + ) -> Option<(mir::ConstantKind<'tcx>, mir::ConstantKind<'tcx>)> { match (lo, hi) { (Some(PatKind::Constant { value: lo }), Some(PatKind::Constant { value: hi })) => { Some((*lo, *hi)) } (Some(PatKind::Constant { value: lo }), None) => { - Some((*lo, ty.numeric_max_val(self.tcx)?)) + let hi = ty.numeric_max_val(self.tcx)?; + Some((*lo, hi.into())) } (None, Some(PatKind::Constant { value: hi })) => { - Some((ty.numeric_min_val(self.tcx)?, *hi)) + let lo = ty.numeric_min_val(self.tcx)?; + Some((lo.into(), *hi)) } _ => None, } @@ -225,7 +228,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { for end in &[lo, hi] { if let Some((_, Some(ascription))) = end { let subpattern = Pat { span: pat.span, ty, kind: Box::new(kind) }; - kind = PatKind::AscribeUserType { ascription: *ascription, subpattern }; + kind = + PatKind::AscribeUserType { ascription: ascription.clone(), subpattern }; } } @@ -430,13 +434,14 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { if let Some(user_ty) = self.user_substs_applied_to_ty_of_hir_id(hir_id) { debug!("lower_variant_or_leaf: kind={:?} user_ty={:?} span={:?}", kind, user_ty, span); + let annotation = CanonicalUserTypeAnnotation { + user_ty, + span, + inferred_ty: self.typeck_results.node_type(hir_id), + }; kind = PatKind::AscribeUserType { subpattern: Pat { span, ty, kind: Box::new(kind) }, - ascription: Ascription { - user_ty: PatTyProj::from_user_type(user_ty), - user_ty_span: span, - variance: ty::Variance::Covariant, - }, + ascription: Ascription { annotation, variance: ty::Variance::Covariant }, }; } @@ -446,6 +451,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { /// Takes a HIR Path. If the path is a constant, evaluates it and feeds /// it to `const_to_pat`. Any other path (like enum variants without fields) /// is converted to the corresponding pattern via `lower_variant_or_leaf`. + #[instrument(skip(self), level = "debug")] fn lower_path(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) -> Pat<'tcx> { let ty = self.typeck_results.node_type(id); let res = self.typeck_results.qpath_res(qpath, id); @@ -487,8 +493,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { debug!("mir_structural_match_violation({:?}) -> {}", qpath, mir_structural_match_violation); match self.tcx.const_eval_instance(param_env_reveal_all, instance, Some(span)) { - Ok(value) => { - let const_ = ty::Const::from_value(self.tcx, value, ty); + Ok(literal) => { + let const_ = mir::ConstantKind::Val(literal, ty); let pattern = self.const_to_pat(const_, id, span, mir_structural_match_violation); if !is_associated_const { @@ -496,18 +502,21 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } let user_provided_types = self.typeck_results().user_provided_types(); - if let Some(u_ty) = user_provided_types.get(id) { - let user_ty = PatTyProj::from_user_type(*u_ty); + if let Some(&user_ty) = user_provided_types.get(id) { + let annotation = CanonicalUserTypeAnnotation { + user_ty, + span, + inferred_ty: self.typeck_results().node_type(id), + }; Pat { span, kind: Box::new(PatKind::AscribeUserType { subpattern: pattern, ascription: Ascription { + annotation, /// Note that use `Contravariant` here. See the /// `variance` field documentation for details. variance: ty::Variance::Contravariant, - user_ty, - user_ty_span: span, }, }), ty: const_.ty(), @@ -537,25 +546,30 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { span: Span, ) -> PatKind<'tcx> { let anon_const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id); - let value = ty::Const::from_inline_const(self.tcx, anon_const_def_id); + let value = mir::ConstantKind::from_inline_const(self.tcx, anon_const_def_id); // Evaluate early like we do in `lower_path`. let value = value.eval(self.tcx, self.param_env); - match value.val() { - ConstKind::Param(_) => { - self.errors.push(PatternError::ConstParamInPattern(span)); - return PatKind::Wild; - } - ConstKind::Unevaluated(_) => { - // If we land here it means the const can't be evaluated because it's `TooGeneric`. - self.tcx.sess.span_err(span, "constant pattern depends on a generic parameter"); - return PatKind::Wild; + match value { + mir::ConstantKind::Ty(c) => { + match c.val() { + ConstKind::Param(_) => { + self.errors.push(PatternError::ConstParamInPattern(span)); + return PatKind::Wild; + } + ConstKind::Unevaluated(_) => { + // If we land here it means the const can't be evaluated because it's `TooGeneric`. + self.tcx + .sess + .span_err(span, "constant pattern depends on a generic parameter"); + return PatKind::Wild; + } + _ => bug!("Expected either ConstKind::Param or ConstKind::Unevaluated"), + } } - _ => (), + mir::ConstantKind::Val(_, _) => *self.const_to_pat(value, id, span, false).kind, } - - *self.const_to_pat(value, id, span, false).kind } /// Converts literals, paths and negation of literals to patterns. @@ -582,7 +596,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let lit_input = LitToConstInput { lit: &lit.node, ty: self.typeck_results.expr_ty(expr), neg }; - match self.tcx.at(expr.span).lit_to_const(lit_input) { + match self.tcx.at(expr.span).lit_to_mir_constant(lit_input) { Ok(constant) => *self.const_to_pat(constant, expr.hir_id, lit.span, false).kind, Err(LitToConstError::Reported) => PatKind::Wild, Err(LitToConstError::TypeError) => bug!("lower_lit: had type error"), @@ -600,7 +614,7 @@ impl<'tcx> UserAnnotatedTyHelpers<'tcx> for PatCtxt<'_, 'tcx> { } } -crate trait PatternFoldable<'tcx>: Sized { +pub(crate) trait PatternFoldable<'tcx>: Sized { fn fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self { self.super_fold_with(folder) } @@ -608,7 +622,7 @@ crate trait PatternFoldable<'tcx>: Sized { fn super_fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self; } -crate trait PatternFolder<'tcx>: Sized { +pub(crate) trait PatternFolder<'tcx>: Sized { fn fold_pattern(&mut self, pattern: &Pat<'tcx>) -> Pat<'tcx> { pattern.super_fold_with(self) } @@ -637,7 +651,7 @@ impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Option<T> { } } -macro_rules! CloneImpls { +macro_rules! ClonePatternFoldableImpls { (<$lt_tcx:tt> $($ty:ty),+) => { $( impl<$lt_tcx> PatternFoldable<$lt_tcx> for $ty { @@ -649,11 +663,11 @@ macro_rules! CloneImpls { } } -CloneImpls! { <'tcx> +ClonePatternFoldableImpls! { <'tcx> Span, Field, Mutability, Symbol, hir::HirId, usize, ty::Const<'tcx>, Region<'tcx>, Ty<'tcx>, BindingMode, AdtDef<'tcx>, SubstsRef<'tcx>, &'tcx GenericArg<'tcx>, UserType<'tcx>, - UserTypeProjection, PatTyProj<'tcx> + UserTypeProjection, CanonicalUserTypeAnnotation<'tcx> } impl<'tcx> PatternFoldable<'tcx> for FieldPat<'tcx> { @@ -686,14 +700,10 @@ impl<'tcx> PatternFoldable<'tcx> for PatKind<'tcx> { PatKind::Wild => PatKind::Wild, PatKind::AscribeUserType { ref subpattern, - ascription: Ascription { variance, ref user_ty, user_ty_span }, + ascription: Ascription { ref annotation, variance }, } => PatKind::AscribeUserType { subpattern: subpattern.fold_with(folder), - ascription: Ascription { - user_ty: user_ty.fold_with(folder), - variance, - user_ty_span, - }, + ascription: Ascription { annotation: annotation.fold_with(folder), variance }, }, PatKind::Binding { mutability, name, mode, var, ty, ref subpattern, is_primary } => { PatKind::Binding { @@ -738,10 +748,10 @@ impl<'tcx> PatternFoldable<'tcx> for PatKind<'tcx> { } #[instrument(skip(tcx), level = "debug")] -crate fn compare_const_vals<'tcx>( +pub(crate) fn compare_const_vals<'tcx>( tcx: TyCtxt<'tcx>, - a: ty::Const<'tcx>, - b: ty::Const<'tcx>, + a: mir::ConstantKind<'tcx>, + b: mir::ConstantKind<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, ) -> Option<Ordering> { @@ -754,9 +764,7 @@ crate fn compare_const_vals<'tcx>( return fallback(); } - // Early return for equal constants (so e.g. references to ZSTs can be compared, even if they - // are just integer addresses). - if a.val() == b.val() { + if a == b { return from_bool(true); } @@ -788,9 +796,9 @@ crate fn compare_const_vals<'tcx>( } if let ty::Str = ty.kind() && let ( - ty::ConstKind::Value(a_val @ ConstValue::Slice { .. }), - ty::ConstKind::Value(b_val @ ConstValue::Slice { .. }), - ) = (a.val(), b.val()) + Some(a_val @ ConstValue::Slice { .. }), + Some(b_val @ ConstValue::Slice { .. }), + ) = (a.try_val(), b.try_val()) { let a_bytes = get_slice_bytes(&tcx, a_val); let b_bytes = get_slice_bytes(&tcx, b_val); diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index 687b2e23c9f..9e7a267ecbd 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -35,23 +35,27 @@ //! This is enough to compute reachability: a pattern in a `match` expression is reachable iff it //! is useful w.r.t. the patterns above it: //! ```rust +//! # fn foo(x: Option<i32>) { //! match x { -//! Some(_) => ..., -//! None => ..., // reachable: `None` is matched by this but not the branch above -//! Some(0) => ..., // unreachable: all the values this matches are already matched by -//! // `Some(_)` above +//! Some(_) => {}, +//! None => {}, // reachable: `None` is matched by this but not the branch above +//! Some(0) => {}, // unreachable: all the values this matches are already matched by +//! // `Some(_)` above //! } +//! # } //! ``` //! //! This is also enough to compute exhaustiveness: a match is exhaustive iff the wildcard `_` //! pattern is _not_ useful w.r.t. the patterns in the match. The values returned by `usefulness` //! are used to tell the user which values are missing. -//! ```rust +//! ```compile_fail,E0004 +//! # fn foo(x: Option<i32>) { //! match x { -//! Some(0) => ..., -//! None => ..., +//! Some(0) => {}, +//! None => {}, //! // not exhaustive: `_` is useful because it matches `Some(1)` //! } +//! # } //! ``` //! //! The entrypoint of this file is the [`compute_match_usefulness`] function, which computes @@ -120,12 +124,15 @@ //! say from knowing only the first constructor of our candidate value. //! //! Let's take the following example: -//! ``` +//! ```compile_fail,E0004 +//! # enum Enum { Variant1(()), Variant2(Option<bool>, u32)} +//! # fn foo(x: Enum) { //! match x { //! Enum::Variant1(_) => {} // `p1` //! Enum::Variant2(None, 0) => {} // `p2` //! Enum::Variant2(Some(_), 0) => {} // `q` //! } +//! # } //! ``` //! //! We can easily see that if our candidate value `v` starts with `Variant1` it will not match `q`. @@ -133,11 +140,13 @@ //! and `v1`. In fact, such a `v` will be a witness of usefulness of `q` exactly when the tuple //! `(v0, v1)` is a witness of usefulness of `q'` in the following reduced match: //! -//! ``` +//! ```compile_fail,E0004 +//! # fn foo(x: (Option<bool>, u32)) { //! match x { //! (None, 0) => {} // `p2'` //! (Some(_), 0) => {} // `q'` //! } +//! # } //! ``` //! //! This motivates a new step in computing usefulness, that we call _specialization_. @@ -150,7 +159,7 @@ //! like a stack. We note a pattern-stack simply with `[p_1 ... p_n]`. //! Here's a sequence of specializations of a list of pattern-stacks, to illustrate what's //! happening: -//! ``` +//! ```ignore (illustrative) //! [Enum::Variant1(_)] //! [Enum::Variant2(None, 0)] //! [Enum::Variant2(Some(_), 0)] @@ -234,7 +243,7 @@ //! - We return the concatenation of all the witnesses found, if any. //! //! Example: -//! ``` +//! ```ignore (illustrative) //! [Some(true)] // p_1 //! [None] // p_2 //! [Some(_)] // q @@ -300,16 +309,16 @@ use smallvec::{smallvec, SmallVec}; use std::fmt; use std::iter::once; -crate struct MatchCheckCtxt<'p, 'tcx> { - crate tcx: TyCtxt<'tcx>, +pub(crate) struct MatchCheckCtxt<'p, 'tcx> { + pub(crate) tcx: TyCtxt<'tcx>, /// The module in which the match occurs. This is necessary for /// checking inhabited-ness of types because whether a type is (visibly) /// inhabited can depend on whether it was defined in the current module or /// not. E.g., `struct Foo { _private: ! }` cannot be seen to be empty /// outside its module and should not be matchable with an empty match statement. - crate module: DefId, - crate param_env: ty::ParamEnv<'tcx>, - crate pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>, + pub(crate) module: DefId, + pub(crate) param_env: ty::ParamEnv<'tcx>, + pub(crate) pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>, } impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { @@ -659,13 +668,15 @@ enum ArmType { /// /// For example, if we are constructing a witness for the match against /// -/// ``` +/// ```compile_fail,E0004 +/// # #![feature(type_ascription)] /// struct Pair(Option<(u32, u32)>, bool); -/// +/// # fn foo(p: Pair) { /// match (p: Pair) { /// Pair(None, _) => {} /// Pair(_, false) => {} /// } +/// # } /// ``` /// /// We'll perform the following steps: @@ -680,7 +691,7 @@ enum ArmType { /// /// The final `Pair(Some(_), true)` is then the resulting witness. #[derive(Debug)] -crate struct Witness<'p, 'tcx>(Vec<DeconstructedPat<'p, 'tcx>>); +pub(crate) struct Witness<'p, 'tcx>(Vec<DeconstructedPat<'p, 'tcx>>); impl<'p, 'tcx> Witness<'p, 'tcx> { /// Asserts that the witness contains a single pattern, and returns it. @@ -897,16 +908,16 @@ fn is_useful<'p, 'tcx>( /// The arm of a match expression. #[derive(Clone, Copy, Debug)] -crate struct MatchArm<'p, 'tcx> { +pub(crate) struct MatchArm<'p, 'tcx> { /// The pattern must have been lowered through `check_match::MatchVisitor::lower_pattern`. - crate pat: &'p DeconstructedPat<'p, 'tcx>, - crate hir_id: HirId, - crate has_guard: bool, + pub(crate) pat: &'p DeconstructedPat<'p, 'tcx>, + pub(crate) hir_id: HirId, + pub(crate) has_guard: bool, } /// Indicates whether or not a given arm is reachable. #[derive(Clone, Debug)] -crate enum Reachability { +pub(crate) enum Reachability { /// The arm is reachable. This additionally carries a set of or-pattern branches that have been /// found to be unreachable despite the overall arm being reachable. Used only in the presence /// of or-patterns, otherwise it stays empty. @@ -916,12 +927,12 @@ crate enum Reachability { } /// The output of checking a match for exhaustiveness and arm reachability. -crate struct UsefulnessReport<'p, 'tcx> { +pub(crate) struct UsefulnessReport<'p, 'tcx> { /// For each arm of the input, whether that arm is reachable after the arms above it. - crate arm_usefulness: Vec<(MatchArm<'p, 'tcx>, Reachability)>, + pub(crate) arm_usefulness: Vec<(MatchArm<'p, 'tcx>, Reachability)>, /// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of /// exhaustiveness. - crate non_exhaustiveness_witnesses: Vec<DeconstructedPat<'p, 'tcx>>, + pub(crate) non_exhaustiveness_witnesses: Vec<DeconstructedPat<'p, 'tcx>>, } /// The entrypoint for the usefulness algorithm. Computes whether a match is exhaustive and which @@ -930,7 +941,7 @@ crate struct UsefulnessReport<'p, 'tcx> { /// Note: the input patterns must have been lowered through /// `check_match::MatchVisitor::lower_pattern`. #[instrument(skip(cx, arms), level = "debug")] -crate fn compute_match_usefulness<'p, 'tcx>( +pub(crate) fn compute_match_usefulness<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, arms: &[MatchArm<'p, 'tcx>], scrut_hir_id: HirId, diff --git a/compiler/rustc_mir_build/src/thir/util.rs b/compiler/rustc_mir_build/src/thir/util.rs index 82f97f22cce..c58ed1ac0b8 100644 --- a/compiler/rustc_mir_build/src/thir/util.rs +++ b/compiler/rustc_mir_build/src/thir/util.rs @@ -1,7 +1,7 @@ use rustc_hir as hir; use rustc_middle::ty::{self, CanonicalUserType, TyCtxt, UserType}; -crate trait UserAnnotatedTyHelpers<'tcx> { +pub(crate) trait UserAnnotatedTyHelpers<'tcx> { fn tcx(&self) -> TyCtxt<'tcx>; fn typeck_results(&self) -> &ty::TypeckResults<'tcx>; diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs index 93118dfeb77..18795128b85 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -269,9 +269,9 @@ impl Direction for Backward { mir::TerminatorKind::SwitchInt { targets: _, ref discr, switch_ty: _ } => { let mut applier = BackwardSwitchIntEdgeEffectsApplier { + body, pred, exit_state, - values: &body.switch_sources()[bb][pred], bb, propagate: &mut propagate, effects_applied: false, @@ -305,9 +305,9 @@ impl Direction for Backward { } struct BackwardSwitchIntEdgeEffectsApplier<'a, D, F> { + body: &'a mir::Body<'a>, pred: BasicBlock, exit_state: &'a mut D, - values: &'a [Option<u128>], bb: BasicBlock, propagate: &'a mut F, @@ -322,7 +322,8 @@ where fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) { assert!(!self.effects_applied); - let targets = self.values.iter().map(|&value| SwitchIntTarget { value, target: self.bb }); + let values = &self.body.switch_sources()[&(self.bb, self.pred)]; + let targets = values.iter().map(|&value| SwitchIntTarget { value, target: self.bb }); let mut tmp = None; for target in targets { diff --git a/compiler/rustc_mir_dataflow/src/framework/engine.rs b/compiler/rustc_mir_dataflow/src/framework/engine.rs index 88ed0128a1f..50efb4c1dc4 100644 --- a/compiler/rustc_mir_dataflow/src/framework/engine.rs +++ b/compiler/rustc_mir_dataflow/src/framework/engine.rs @@ -333,14 +333,11 @@ struct RustcMirAttrs { impl RustcMirAttrs { fn parse(tcx: TyCtxt<'_>, def_id: DefId) -> Result<Self, ()> { - let attrs = tcx.get_attrs(def_id); - let mut result = Ok(()); let mut ret = RustcMirAttrs::default(); - let rustc_mir_attrs = attrs - .iter() - .filter(|attr| attr.has_name(sym::rustc_mir)) + 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 { diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index 599b4087c78..c6a85bc43f4 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -125,7 +125,7 @@ where } fn target(&self, edge: &Self::Edge) -> Self::Node { - self.body[edge.source].terminator().successors().nth(edge.index).copied().unwrap() + self.body[edge.source].terminator().successors().nth(edge.index).unwrap() } } diff --git a/compiler/rustc_mir_dataflow/src/impls/mod.rs b/compiler/rustc_mir_dataflow/src/impls/mod.rs index d90ead3228c..c9722a6df77 100644 --- a/compiler/rustc_mir_dataflow/src/impls/mod.rs +++ b/compiler/rustc_mir_dataflow/src/impls/mod.rs @@ -37,21 +37,21 @@ pub use self::storage_liveness::{MaybeRequiresStorage, MaybeStorageLive}; /// /// ```rust /// struct S; -/// fn foo(pred: bool) { // maybe-init: -/// // {} -/// let a = S; let b = S; let c; let d; // {a, b} +/// fn foo(pred: bool) { // maybe-init: +/// // {} +/// let a = S; let mut b = S; let c; let d; // {a, b} /// /// if pred { -/// drop(a); // { b} -/// b = S; // { b} +/// drop(a); // { b} +/// b = S; // { b} /// /// } else { -/// drop(b); // {a} -/// d = S; // {a, d} +/// drop(b); // {a} +/// d = S; // {a, d} /// -/// } // {a, b, d} +/// } // {a, b, d} /// -/// c = S; // {a, b, c, d} +/// c = S; // {a, b, c, d} /// } /// ``` /// @@ -90,21 +90,21 @@ impl<'a, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'tcx> { /// /// ```rust /// struct S; -/// fn foo(pred: bool) { // maybe-uninit: -/// // {a, b, c, d} -/// let a = S; let b = S; let c; let d; // { c, d} +/// fn foo(pred: bool) { // maybe-uninit: +/// // {a, b, c, d} +/// let a = S; let mut b = S; let c; let d; // { c, d} /// /// if pred { -/// drop(a); // {a, c, d} -/// b = S; // {a, c, d} +/// drop(a); // {a, c, d} +/// b = S; // {a, c, d} /// /// } else { -/// drop(b); // { b, c, d} -/// d = S; // { b, c } +/// drop(b); // { b, c, d} +/// d = S; // { b, c } /// -/// } // {a, b, c, d} +/// } // {a, b, c, d} /// -/// c = S; // {a, b, d} +/// c = S; // {a, b, d} /// } /// ``` /// @@ -155,21 +155,21 @@ impl<'a, 'tcx> HasMoveData<'tcx> for MaybeUninitializedPlaces<'a, 'tcx> { /// /// ```rust /// struct S; -/// fn foo(pred: bool) { // definite-init: -/// // { } -/// let a = S; let b = S; let c; let d; // {a, b } +/// fn foo(pred: bool) { // definite-init: +/// // { } +/// let a = S; let mut b = S; let c; let d; // {a, b } /// /// if pred { -/// drop(a); // { b, } -/// b = S; // { b, } +/// drop(a); // { b, } +/// b = S; // { b, } /// /// } else { -/// drop(b); // {a, } -/// d = S; // {a, d} +/// drop(b); // {a, } +/// d = S; // {a, d} /// -/// } // { } +/// } // { } /// -/// c = S; // { c } +/// c = S; // { c } /// } /// ``` /// @@ -210,21 +210,21 @@ impl<'a, 'tcx> HasMoveData<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> { /// /// ```rust /// struct S; -/// fn foo(pred: bool) { // ever-init: -/// // { } -/// let a = S; let b = S; let c; let d; // {a, b } +/// fn foo(pred: bool) { // ever-init: +/// // { } +/// let a = S; let mut b = S; let c; let d; // {a, b } /// /// if pred { -/// drop(a); // {a, b, } -/// b = S; // {a, b, } +/// drop(a); // {a, b, } +/// b = S; // {a, b, } /// /// } else { -/// drop(b); // {a, b, } -/// d = S; // {a, b, d } +/// drop(b); // {a, b, } +/// d = S; // {a, b, d } /// -/// } // {a, b, d } +/// } // {a, b, d } /// -/// c = S; // {a, b, c, d } +/// c = S; // {a, b, c, d } /// } /// ``` pub struct EverInitializedPlaces<'a, 'tcx> { diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index d0837bcf754..c1124a533bf 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -14,9 +14,9 @@ extern crate tracing; #[macro_use] extern crate rustc_middle; -use rustc_ast::{self as ast, MetaItem}; -use rustc_middle::ty; -use rustc_session::Session; +use rustc_ast::MetaItem; +use rustc_hir::def_id::DefId; +use rustc_middle::ty::{self, TyCtxt}; use rustc_span::symbol::{sym, Symbol}; pub use self::drop_flag_effects::{ @@ -49,19 +49,13 @@ pub struct MoveDataParamEnv<'tcx> { pub param_env: ty::ParamEnv<'tcx>, } -pub fn has_rustc_mir_with( - _sess: &Session, - attrs: &[ast::Attribute], - name: Symbol, -) -> Option<MetaItem> { - for attr in attrs { - if attr.has_name(sym::rustc_mir) { - let items = attr.meta_item_list(); - for item in items.iter().flat_map(|l| l.iter()) { - match item.meta_item() { - Some(mi) if mi.has_name(name) => return Some(mi.clone()), - _ => continue, - } +pub fn has_rustc_mir_with(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Option<MetaItem> { + for attr in tcx.get_attrs(def_id, sym::rustc_mir) { + let items = attr.meta_item_list(); + for item in items.iter().flat_map(|l| l.iter()) { + match item.meta_item() { + Some(mi) if mi.has_name(name) => return Some(mi.clone()), + _ => continue, } } } diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs index 51ab5b43bff..2f884887ad9 100644 --- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs +++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs @@ -1,7 +1,5 @@ -use rustc_ast::ast; use rustc_span::symbol::sym; use rustc_span::Span; -use rustc_target::spec::abi::Abi; use rustc_index::bit_set::BitSet; use rustc_middle::mir::MirPass; @@ -31,43 +29,41 @@ impl<'tcx> MirPass<'tcx> for SanityCheck { debug!("running rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id)); } - let attributes = tcx.get_attrs(def_id); let param_env = tcx.param_env(def_id); let move_data = MoveData::gather_moves(body, tcx, param_env).unwrap(); let mdpe = MoveDataParamEnv { move_data, param_env }; - let sess = &tcx.sess; - if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_maybe_init).is_some() { + if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_init).is_some() { let flow_inits = MaybeInitializedPlaces::new(tcx, body, &mdpe) .into_engine(tcx, body) .iterate_to_fixpoint(); - sanity_check_via_rustc_peek(tcx, body, &attributes, &flow_inits); + sanity_check_via_rustc_peek(tcx, body, &flow_inits); } - if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_maybe_uninit).is_some() { + if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_uninit).is_some() { let flow_uninits = MaybeUninitializedPlaces::new(tcx, body, &mdpe) .into_engine(tcx, body) .iterate_to_fixpoint(); - sanity_check_via_rustc_peek(tcx, body, &attributes, &flow_uninits); + sanity_check_via_rustc_peek(tcx, body, &flow_uninits); } - if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_definite_init).is_some() { + if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_definite_init).is_some() { let flow_def_inits = DefinitelyInitializedPlaces::new(tcx, body, &mdpe) .into_engine(tcx, body) .iterate_to_fixpoint(); - sanity_check_via_rustc_peek(tcx, body, &attributes, &flow_def_inits); + sanity_check_via_rustc_peek(tcx, body, &flow_def_inits); } - if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_liveness).is_some() { + if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_liveness).is_some() { let flow_liveness = MaybeLiveLocals.into_engine(tcx, body).iterate_to_fixpoint(); - sanity_check_via_rustc_peek(tcx, body, &attributes, &flow_liveness); + sanity_check_via_rustc_peek(tcx, body, &flow_liveness); } - if has_rustc_mir_with(sess, &attributes, sym::stop_after_dataflow).is_some() { + if has_rustc_mir_with(tcx, def_id, sym::stop_after_dataflow).is_some() { tcx.sess.fatal("stop_after_dataflow ended compilation"); } } @@ -92,7 +88,6 @@ impl<'tcx> MirPass<'tcx> for SanityCheck { pub fn sanity_check_via_rustc_peek<'tcx, A>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, - _attributes: &[ast::Attribute], results: &Results<'tcx, A>, ) where A: RustcPeekAt<'tcx>, @@ -197,9 +192,8 @@ impl PeekCall { &terminator.kind { if let ty::FnDef(def_id, substs) = *func.literal.ty().kind() { - let sig = tcx.fn_sig(def_id); let name = tcx.item_name(def_id); - if sig.abi() != Abi::RustIntrinsic || name != sym::rustc_peek { + if !tcx.is_intrinsic(def_id) || name != sym::rustc_peek { return None; } diff --git a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs index 757dc093755..11980382ffd 100644 --- a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs +++ b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs @@ -1,6 +1,6 @@ use crate::MirPass; +use rustc_ast::InlineAsmOptions; use rustc_hir::def::DefKind; -use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::*; use rustc_middle::ty::layout; use rustc_middle::ty::{self, TyCtxt}; @@ -46,7 +46,6 @@ impl<'tcx> MirPass<'tcx> for AbortUnwindingCalls { // // Here we test for this function itself whether its ABI allows // unwinding or not. - let body_flags = tcx.codegen_fn_attrs(def_id).flags; let body_ty = tcx.type_of(def_id); let body_abi = match body_ty.kind() { ty::FnDef(..) => body_ty.fn_sig(tcx).abi(), @@ -54,7 +53,7 @@ impl<'tcx> MirPass<'tcx> for AbortUnwindingCalls { ty::Generator(..) => Abi::Rust, _ => span_bug!(body.span, "unexpected body ty: {:?}", body_ty), }; - let body_can_unwind = layout::fn_can_unwind(tcx, body_flags, body_abi); + let body_can_unwind = layout::fn_can_unwind(tcx, Some(def_id), body_abi); // Look in this function body for any basic blocks which are terminated // with a function call, and whose function we're calling may unwind. @@ -73,19 +72,25 @@ impl<'tcx> MirPass<'tcx> for AbortUnwindingCalls { TerminatorKind::Call { func, .. } => { let ty = func.ty(body, tcx); let sig = ty.fn_sig(tcx); - let flags = match ty.kind() { - ty::FnPtr(_) => CodegenFnAttrFlags::empty(), - ty::FnDef(def_id, _) => tcx.codegen_fn_attrs(*def_id).flags, + let fn_def_id = match ty.kind() { + ty::FnPtr(_) => None, + &ty::FnDef(def_id, _) => Some(def_id), _ => span_bug!(span, "invalid callee of type {:?}", ty), }; - layout::fn_can_unwind(tcx, flags, sig.abi()) + layout::fn_can_unwind(tcx, fn_def_id, sig.abi()) } TerminatorKind::Drop { .. } | TerminatorKind::DropAndReplace { .. } => { tcx.sess.opts.debugging_opts.panic_in_drop == PanicStrategy::Unwind - && layout::fn_can_unwind(tcx, CodegenFnAttrFlags::empty(), Abi::Rust) + && layout::fn_can_unwind(tcx, None, Abi::Rust) } TerminatorKind::Assert { .. } | TerminatorKind::FalseUnwind { .. } => { - layout::fn_can_unwind(tcx, CodegenFnAttrFlags::empty(), Abi::Rust) + layout::fn_can_unwind(tcx, None, Abi::Rust) + } + TerminatorKind::InlineAsm { options, .. } => { + options.contains(InlineAsmOptions::MAY_UNWIND) + } + _ if terminator.unwind().is_some() => { + span_bug!(span, "unexpected terminator that may unwind {:?}", terminator) } _ => continue, }; diff --git a/compiler/rustc_mir_transform/src/check_unsafety.rs b/compiler/rustc_mir_transform/src/check_unsafety.rs index dde79214b16..1f73b7da815 100644 --- a/compiler/rustc_mir_transform/src/check_unsafety.rs +++ b/compiler/rustc_mir_transform/src/check_unsafety.rs @@ -375,7 +375,8 @@ impl<'tcx> UnsafetyChecker<'_, 'tcx> { } let callee_features = &self.tcx.codegen_fn_attrs(func_did).target_features; - let self_features = &self.tcx.codegen_fn_attrs(self.body_did).target_features; + // The body might be a constant, so it doesn't have codegen attributes. + let self_features = &self.tcx.body_codegen_attrs(self.body_did.to_def_id()).target_features; // Is `callee_features` a subset of `calling_features`? if !callee_features.iter().all(|feature| self_features.contains(feature)) { diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs index f7535d338da..54c3cc46b26 100644 --- a/compiler/rustc_mir_transform/src/const_prop.rs +++ b/compiler/rustc_mir_transform/src/const_prop.rs @@ -18,7 +18,9 @@ use rustc_middle::mir::{ }; use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::subst::{InternalSubsts, Subst}; -use rustc_middle::ty::{self, ConstKind, Instance, ParamEnv, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{ + self, ConstKind, EarlyBinder, Instance, ParamEnv, Ty, TyCtxt, TypeFoldable, +}; use rustc_span::{def_id::DefId, Span}; use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout}; use rustc_target::spec::abi::Abi; @@ -28,7 +30,8 @@ use crate::MirPass; use rustc_const_eval::interpret::{ self, compile_time_machine, AllocId, ConstAllocation, ConstValue, CtfeValidationMode, Frame, ImmTy, Immediate, InterpCx, InterpResult, LocalState, LocalValue, MemPlace, MemoryKind, OpTy, - Operand as InterpOperand, PlaceTy, Scalar, ScalarMaybeUninit, StackPopCleanup, StackPopUnwind, + Operand as InterpOperand, PlaceTy, Pointer, Scalar, ScalarMaybeUninit, StackPopCleanup, + StackPopUnwind, }; /// The maximum number of bytes that we'll allocate space for a local or the return value. @@ -286,6 +289,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> } #[inline(always)] + fn expose_ptr( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _ptr: Pointer<AllocId>, + ) -> InterpResult<'tcx> { + throw_machine_stop_str!("exposing pointers isn't supported in ConstProp") + } + + #[inline(always)] fn init_frame_extra( _ecx: &mut InterpCx<'mir, 'tcx, Self>, frame: Frame<'mir, 'tcx>, @@ -374,7 +385,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { ); let ret = ecx - .layout_of(body.return_ty().subst(tcx, substs)) + .layout_of(EarlyBinder(body.return_ty()).subst(tcx, substs)) .ok() // Don't bother allocating memory for ZST types which have no values // or for large values. diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs index aa898cfd3ba..6c0df98bc27 100644 --- a/compiler/rustc_mir_transform/src/const_prop_lint.rs +++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs @@ -18,7 +18,7 @@ use rustc_middle::mir::{ use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::subst::{InternalSubsts, Subst}; use rustc_middle::ty::{ - self, ConstInt, ConstKind, Instance, ParamEnv, ScalarInt, Ty, TyCtxt, TypeFoldable, + self, ConstInt, ConstKind, EarlyBinder, Instance, ParamEnv, ScalarInt, Ty, TyCtxt, TypeFoldable, }; use rustc_session::lint; use rustc_span::{def_id::DefId, Span}; @@ -30,8 +30,8 @@ use crate::MirLint; use rustc_const_eval::const_eval::ConstEvalErr; use rustc_const_eval::interpret::{ self, compile_time_machine, AllocId, ConstAllocation, Frame, ImmTy, InterpCx, InterpResult, - LocalState, LocalValue, MemPlace, MemoryKind, OpTy, Operand as InterpOperand, PlaceTy, Scalar, - ScalarMaybeUninit, StackPopCleanup, StackPopUnwind, + LocalState, LocalValue, MemPlace, MemoryKind, OpTy, Operand as InterpOperand, PlaceTy, Pointer, + Scalar, ScalarMaybeUninit, StackPopCleanup, StackPopUnwind, }; /// The maximum number of bytes that we'll allocate space for a local or the return value. @@ -281,6 +281,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> } #[inline(always)] + fn expose_ptr( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _ptr: Pointer<AllocId>, + ) -> InterpResult<'tcx> { + throw_machine_stop_str!("exposing pointers isn't supported in ConstProp") + } + + #[inline(always)] fn init_frame_extra( _ecx: &mut InterpCx<'mir, 'tcx, Self>, frame: Frame<'mir, 'tcx>, @@ -370,7 +378,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { ); let ret = ecx - .layout_of(body.return_ty().subst(tcx, substs)) + .layout_of(EarlyBinder(body.return_ty()).subst(tcx, substs)) .ok() // Don't bother allocating memory for ZST types which have no values // or for large values. diff --git a/compiler/rustc_mir_transform/src/coverage/debug.rs b/compiler/rustc_mir_transform/src/coverage/debug.rs index 8e28ed2426b..434bf9d849e 100644 --- a/compiler/rustc_mir_transform/src/coverage/debug.rs +++ b/compiler/rustc_mir_transform/src/coverage/debug.rs @@ -701,7 +701,7 @@ pub(super) fn dump_coverage_graphviz<'tcx>( edge_labels.retain(|label| label != "unreachable"); let edge_counters = from_terminator .successors() - .map(|&successor_bb| graphviz_data.get_edge_counter(from_bcb, successor_bb)); + .map(|successor_bb| graphviz_data.get_edge_counter(from_bcb, successor_bb)); iter::zip(&edge_labels, edge_counters) .map(|(label, some_counter)| { if let Some(counter) = some_counter { diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs index 6bb7e676e85..47190fa0d1a 100644 --- a/compiler/rustc_mir_transform/src/coverage/graph.rs +++ b/compiler/rustc_mir_transform/src/coverage/graph.rs @@ -484,17 +484,17 @@ fn bcb_filtered_successors<'a, 'tcx>( body: &'tcx &'a mir::Body<'tcx>, term_kind: &'tcx TerminatorKind<'tcx>, ) -> Box<dyn Iterator<Item = BasicBlock> + 'a> { - let mut successors = term_kind.successors(); Box::new( match &term_kind { // SwitchInt successors are never unwind, and all of them should be traversed. - TerminatorKind::SwitchInt { .. } => successors, + TerminatorKind::SwitchInt { ref targets, .. } => { + None.into_iter().chain(targets.all_targets().into_iter().copied()) + } // For all other kinds, return only the first successor, if any, and ignore unwinds. // NOTE: `chain(&[])` is required to coerce the `option::iter` (from // `next().into_iter()`) into the `mir::Successors` aliased type. - _ => successors.next().into_iter().chain(&[]), + _ => term_kind.successors().next().into_iter().chain((&[]).into_iter().copied()), } - .copied() .filter(move |&successor| body[successor].terminator().kind != TerminatorKind::Unreachable), ) } diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index 5b7b343949c..512d4daf343 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -485,7 +485,7 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> { }) { let merged_prefix_len = self.curr_original_span.lo() - self.curr().span.lo(); let after_macro_bang = - merged_prefix_len + BytePos(visible_macro.as_str().bytes().count() as u32 + 1); + merged_prefix_len + BytePos(visible_macro.as_str().len() as u32 + 1); let mut macro_name_cov = self.curr().clone(); self.curr_mut().span = self.curr().span.with_lo(self.curr().span.lo() + after_macro_bang); diff --git a/compiler/rustc_mir_transform/src/function_item_references.rs b/compiler/rustc_mir_transform/src/function_item_references.rs index 2ed14b91778..1f9bd90d11f 100644 --- a/compiler/rustc_mir_transform/src/function_item_references.rs +++ b/compiler/rustc_mir_transform/src/function_item_references.rs @@ -6,7 +6,7 @@ use rustc_middle::mir::*; use rustc_middle::ty::{ self, subst::{GenericArgKind, Subst, SubstsRef}, - PredicateKind, Ty, TyCtxt, + EarlyBinder, PredicateKind, Ty, TyCtxt, }; use rustc_session::lint::builtin::FUNCTION_ITEM_REFERENCES; use rustc_span::{symbol::sym, Span}; @@ -90,7 +90,7 @@ impl<'tcx> FunctionItemRefChecker<'_, 'tcx> { // If the inner type matches the type bound by `Pointer` if inner_ty == bound_ty { // Do a substitution using the parameters from the callsite - let subst_ty = inner_ty.subst(self.tcx, substs_ref); + let subst_ty = EarlyBinder(inner_ty).subst(self.tcx, substs_ref); if let Some((fn_id, fn_substs)) = FunctionItemRefChecker::is_fn_ref(subst_ty) { diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs index 144ea0ec619..9e7bf5c7929 100644 --- a/compiler/rustc_mir_transform/src/generator.rs +++ b/compiler/rustc_mir_transform/src/generator.rs @@ -18,13 +18,13 @@ //! First upvars are stored //! It is followed by the generator state field. //! Then finally the MIR locals which are live across a suspension point are stored. -//! +//! ```ignore (illustrative) //! struct Generator { //! upvars..., //! state: u32, //! mir_locals..., //! } -//! +//! ``` //! This pass computes the meaning of the state field and the MIR locals which are live //! across a suspension point. There are however three hardcoded generator states: //! 0 - Generator have not been resumed yet @@ -247,7 +247,7 @@ impl<'tcx> TransformVisitor<'tcx> { assert_eq!(self.state_adt_ref.variant(idx).fields.len(), 1); let ty = self .tcx - .type_of(self.state_adt_ref.variant(idx).fields[0].did) + .bound_type_of(self.state_adt_ref.variant(idx).fields[0].did) .subst(self.tcx, self.state_substs); expand_aggregate( Place::return_place(), @@ -1042,8 +1042,7 @@ fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool { | TerminatorKind::Unreachable | TerminatorKind::GeneratorDrop | TerminatorKind::FalseEdge { .. } - | TerminatorKind::FalseUnwind { .. } - | TerminatorKind::InlineAsm { .. } => {} + | TerminatorKind::FalseUnwind { .. } => {} // Resume will *continue* unwinding, but if there's no other unwinding terminator it // will never be reached. @@ -1057,6 +1056,7 @@ fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool { TerminatorKind::Drop { .. } | TerminatorKind::DropAndReplace { .. } | TerminatorKind::Call { .. } + | TerminatorKind::InlineAsm { .. } | TerminatorKind::Assert { .. } => return true, } } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 5e6dabeba6d..017d9e74dc4 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -17,7 +17,7 @@ use crate::MirPass; use std::iter; use std::ops::{Range, RangeFrom}; -crate mod cycle; +pub(crate) mod cycle; const INSTR_COST: usize = 5; const CALL_PENALTY: usize = 25; @@ -260,7 +260,7 @@ impl<'tcx> Inliner<'tcx> { return None; } - let fn_sig = self.tcx.fn_sig(def_id).subst(self.tcx, substs); + let fn_sig = self.tcx.bound_fn_sig(def_id).subst(self.tcx, substs); return Some(CallSite { callee, @@ -418,8 +418,7 @@ impl<'tcx> Inliner<'tcx> { } } // Don't give intrinsics the extra penalty for calls - let f = tcx.fn_sig(def_id); - if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic { + if tcx.is_intrinsic(def_id) { cost += INSTR_COST; } else { cost += CALL_PENALTY; @@ -450,7 +449,7 @@ impl<'tcx> Inliner<'tcx> { } if !is_drop { - for &succ in term.successors() { + for succ in term.successors() { work_list.push(succ); } } @@ -555,7 +554,8 @@ impl<'tcx> Inliner<'tcx> { new_scopes: SourceScope::new(caller_body.source_scopes.len()).., new_blocks: BasicBlock::new(caller_body.basic_blocks().len()).., destination: dest, - return_block: callsite.target, + callsite_scope: caller_body.source_scopes[callsite.source_info.scope].clone(), + callsite, cleanup_block: cleanup, in_cleanup_block: false, tcx: self.tcx, @@ -567,31 +567,6 @@ impl<'tcx> Inliner<'tcx> { // (or existing ones, in a few special cases) in the caller. integrator.visit_body(&mut callee_body); - for scope in &mut callee_body.source_scopes { - // FIXME(eddyb) move this into a `fn visit_scope_data` in `Integrator`. - if scope.parent_scope.is_none() { - let callsite_scope = &caller_body.source_scopes[callsite.source_info.scope]; - - // Attach the outermost callee scope as a child of the callsite - // scope, via the `parent_scope` and `inlined_parent_scope` chains. - scope.parent_scope = Some(callsite.source_info.scope); - assert_eq!(scope.inlined_parent_scope, None); - scope.inlined_parent_scope = if callsite_scope.inlined.is_some() { - Some(callsite.source_info.scope) - } else { - callsite_scope.inlined_parent_scope - }; - - // Mark the outermost callee scope as an inlined one. - assert_eq!(scope.inlined, None); - scope.inlined = Some((callsite.callee, callsite.source_info.span)); - } else if scope.inlined_parent_scope.is_none() { - // Make it easy to find the scope with `inlined` set above. - scope.inlined_parent_scope = - Some(integrator.map_scope(OUTERMOST_SOURCE_SCOPE)); - } - } - // If there are any locals without storage markers, give them storage only for the // duration of the call. for local in callee_body.vars_and_temps_iter() { @@ -787,7 +762,8 @@ struct Integrator<'a, 'tcx> { new_scopes: RangeFrom<SourceScope>, new_blocks: RangeFrom<BasicBlock>, destination: Place<'tcx>, - return_block: Option<BasicBlock>, + callsite_scope: SourceScopeData<'tcx>, + callsite: &'a CallSite<'tcx>, cleanup_block: Option<BasicBlock>, in_cleanup_block: bool, tcx: TyCtxt<'tcx>, @@ -833,6 +809,28 @@ impl<'tcx> MutVisitor<'tcx> for Integrator<'_, 'tcx> { *local = self.map_local(*local); } + fn visit_source_scope_data(&mut self, scope_data: &mut SourceScopeData<'tcx>) { + self.super_source_scope_data(scope_data); + if scope_data.parent_scope.is_none() { + // Attach the outermost callee scope as a child of the callsite + // scope, via the `parent_scope` and `inlined_parent_scope` chains. + scope_data.parent_scope = Some(self.callsite.source_info.scope); + assert_eq!(scope_data.inlined_parent_scope, None); + scope_data.inlined_parent_scope = if self.callsite_scope.inlined.is_some() { + Some(self.callsite.source_info.scope) + } else { + self.callsite_scope.inlined_parent_scope + }; + + // Mark the outermost callee scope as an inlined one. + assert_eq!(scope_data.inlined, None); + scope_data.inlined = Some((self.callsite.callee, self.callsite.source_info.span)); + } else if scope_data.inlined_parent_scope.is_none() { + // Make it easy to find the scope with `inlined` set above. + scope_data.inlined_parent_scope = Some(self.map_scope(OUTERMOST_SOURCE_SCOPE)); + } + } + fn visit_source_scope(&mut self, scope: &mut SourceScope) { *scope = self.map_scope(*scope); } @@ -939,7 +937,7 @@ impl<'tcx> MutVisitor<'tcx> for Integrator<'_, 'tcx> { } } TerminatorKind::Return => { - terminator.kind = if let Some(tgt) = self.return_block { + terminator.kind = if let Some(tgt) = self.callsite.target { TerminatorKind::Goto { target: tgt } } else { TerminatorKind::Unreachable diff --git a/compiler/rustc_mir_transform/src/inline/cycle.rs b/compiler/rustc_mir_transform/src/inline/cycle.rs index ea1ec6249bc..fd7de2bd1dc 100644 --- a/compiler/rustc_mir_transform/src/inline/cycle.rs +++ b/compiler/rustc_mir_transform/src/inline/cycle.rs @@ -1,5 +1,4 @@ -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::sso::SsoHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::mir::TerminatorKind; @@ -10,7 +9,7 @@ use rustc_session::Limit; // FIXME: check whether it is cheaper to precompute the entire call graph instead of invoking // this query ridiculously often. #[instrument(level = "debug", skip(tcx, root, target))] -crate fn mir_callgraph_reachable<'tcx>( +pub(crate) fn mir_callgraph_reachable<'tcx>( tcx: TyCtxt<'tcx>, (root, target): (ty::Instance<'tcx>, LocalDefId), ) -> bool { @@ -45,7 +44,10 @@ crate fn mir_callgraph_reachable<'tcx>( ) -> bool { trace!(%caller); for &(callee, substs) in tcx.mir_inliner_callees(caller.def) { - let substs = caller.subst_mir_and_normalize_erasing_regions(tcx, param_env, substs); + let Ok(substs) = caller.try_subst_mir_and_normalize_erasing_regions(tcx, param_env, substs) else { + trace!(?caller, ?param_env, ?substs, "cannot normalize, skipping"); + continue; + }; let Some(callee) = ty::Instance::resolve(tcx, param_env, callee, substs).unwrap() else { trace!(?callee, "cannot resolve, skipping"); continue; @@ -134,7 +136,7 @@ crate fn mir_callgraph_reachable<'tcx>( ) } -crate fn mir_inliner_callees<'tcx>( +pub(crate) fn mir_inliner_callees<'tcx>( tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>, ) -> &'tcx [(DefId, SubstsRef<'tcx>)] { @@ -150,7 +152,7 @@ crate fn mir_inliner_callees<'tcx>( // Functions from other crates and MIR shims _ => tcx.instance_mir(instance), }; - let mut calls = SsoHashSet::new(); + let mut calls = FxIndexSet::default(); for bb_data in body.basic_blocks() { let terminator = bb_data.terminator(); if let TerminatorKind::Call { func, .. } = &terminator.kind { diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 40cc6dafe61..1e8c373a411 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -1,7 +1,6 @@ #![allow(rustc::potential_query_instability)] #![feature(box_patterns)] #![feature(box_syntax)] -#![feature(crate_visibility_modifier)] #![feature(let_chains)] #![feature(let_else)] #![feature(map_try_insert)] @@ -170,7 +169,7 @@ fn mir_keys(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet<LocalDefId> { intravisit::walk_struct_def(self, v) } } - tcx.hir().visit_all_item_likes(&mut GatherCtors { tcx, set: &mut set }.as_deep_visitor()); + tcx.hir().deep_visit_all_item_likes(&mut GatherCtors { tcx, set: &mut set }); set } diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index 684d988ee9e..d0d0e09d525 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -6,7 +6,6 @@ use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; -use rustc_target::spec::abi::Abi; pub struct LowerIntrinsics; @@ -139,8 +138,7 @@ fn resolve_rust_intrinsic<'tcx>( func_ty: Ty<'tcx>, ) -> Option<(Symbol, SubstsRef<'tcx>)> { if let ty::FnDef(def_id, substs) = *func_ty.kind() { - let fn_sig = func_ty.fn_sig(tcx); - if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = fn_sig.abi() { + if tcx.is_intrinsic(def_id) { return Some((tcx.item_name(def_id), substs)); } } diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs index 3c14a324c36..7cd7d26328a 100644 --- a/compiler/rustc_mir_transform/src/match_branches.rs +++ b/compiler/rustc_mir_transform/src/match_branches.rs @@ -14,7 +14,7 @@ pub struct MatchBranchSimplification; /// /// For example: /// -/// ```rust +/// ```ignore (MIR) /// bb0: { /// switchInt(move _3) -> [42_isize: bb1, otherwise: bb2]; /// } @@ -32,7 +32,7 @@ pub struct MatchBranchSimplification; /// /// into: /// -/// ```rust +/// ```ignore (MIR) /// bb0: { /// _2 = Eq(move _3, const 42_isize); /// goto -> bb3; diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs index 4d214b0356c..f925d13b2fb 100644 --- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs @@ -65,7 +65,7 @@ impl RemoveNoopLandingPads { | TerminatorKind::SwitchInt { .. } | TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } => { - terminator.successors().all(|&succ| nop_landing_pads.contains(succ)) + terminator.successors().all(|succ| nop_landing_pads.contains(succ)) } TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index d10cac2ac76..016b3bc0980 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -4,7 +4,7 @@ use rustc_hir::lang_items::LangItem; use rustc_middle::mir::*; use rustc_middle::ty::query::Providers; use rustc_middle::ty::subst::{InternalSubsts, Subst}; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt}; use rustc_target::abi::VariantIdx; use rustc_index::vec::{Idx, IndexVec}; @@ -70,7 +70,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' // of this function. Is this intentional? if let Some(ty::Generator(gen_def_id, substs, _)) = ty.map(Ty::kind) { let body = tcx.optimized_mir(*gen_def_id).generator_drop().unwrap(); - let body = body.clone().subst(tcx, substs); + let body = EarlyBinder(body.clone()).subst(tcx, substs); debug!("make_shim({:?}) = {:?}", instance, body); return body; } @@ -151,7 +151,7 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option<Ty<'tcx>>) } else { InternalSubsts::identity_for_item(tcx, def_id) }; - let sig = tcx.fn_sig(def_id).subst(tcx, substs); + let sig = tcx.bound_fn_sig(def_id).subst(tcx, substs); let sig = tcx.erase_late_bound_regions(sig); let span = tcx.def_span(def_id); @@ -343,7 +343,7 @@ impl<'tcx> CloneShimBuilder<'tcx> { // otherwise going to be TySelf and we can't index // or access fields of a Place of type TySelf. let substs = tcx.mk_substs_trait(self_ty, &[]); - let sig = tcx.fn_sig(def_id).subst(tcx, substs); + let sig = tcx.bound_fn_sig(def_id).subst(tcx, substs); let sig = tcx.erase_late_bound_regions(sig); let span = tcx.def_span(def_id); @@ -541,7 +541,7 @@ fn build_call_shim<'tcx>( assert_eq!(sig_substs.is_some(), !instance.has_polymorphic_mir_body()); if let Some(sig_substs) = sig_substs { - sig = sig.subst(tcx, sig_substs); + sig = EarlyBinder(sig).subst(tcx, sig_substs); } if let CallKind::Indirect(fnty) = call_kind { diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index b42e3909cf3..72e08343925 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -81,7 +81,7 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { for (_, data) in traversal::preorder(body) { if let Some(ref term) = data.terminator { - for &tgt in term.successors() { + for tgt in term.successors() { pred_count[tgt] += 1; } } @@ -235,8 +235,8 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { }; let first_succ = { - if let Some(&first_succ) = terminator.successors().next() { - if terminator.successors().all(|s| *s == first_succ) { + if let Some(first_succ) = terminator.successors().next() { + if terminator.successors().all(|s| s == first_succ) { let count = terminator.successors().count(); self.pred_count[first_succ] -= (count - 1) as u32; first_succ diff --git a/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs b/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs index da683a33651..bbfaace7041 100644 --- a/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs +++ b/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs @@ -12,7 +12,7 @@ use rustc_middle::{ /// Pass to convert `if` conditions on integrals into switches on the integral. /// For an example, it turns something like /// -/// ``` +/// ```ignore (MIR) /// _3 = Eq(move _4, const 43i32); /// StorageDead(_4); /// switchInt(_3) -> [false: bb2, otherwise: bb3]; @@ -20,7 +20,7 @@ use rustc_middle::{ /// /// into: /// -/// ``` +/// ```ignore (MIR) /// switchInt(_4) -> [43i32: bb3, otherwise: bb2]; /// ``` pub struct SimplifyComparisonIntegral; diff --git a/compiler/rustc_mir_transform/src/simplify_try.rs b/compiler/rustc_mir_transform/src/simplify_try.rs index ce4b45062e8..b3d45c7a221 100644 --- a/compiler/rustc_mir_transform/src/simplify_try.rs +++ b/compiler/rustc_mir_transform/src/simplify_try.rs @@ -1,10 +1,12 @@ //! The general point of the optimizations provided here is to simplify something like: //! //! ```rust +//! # fn foo<T, E>(x: Result<T, E>) -> Result<T, E> { //! match x { //! Ok(x) => Ok(x), //! Err(x) => Err(x) //! } +//! # } //! ``` //! //! into just `x`. @@ -23,7 +25,7 @@ use std::slice::Iter; /// /// This is done by transforming basic blocks where the statements match: /// -/// ```rust +/// ```ignore (MIR) /// _LOCAL_TMP = ((_LOCAL_1 as Variant ).FIELD: TY ); /// _TMP_2 = _LOCAL_TMP; /// ((_LOCAL_0 as Variant).FIELD: TY) = move _TMP_2; @@ -32,7 +34,7 @@ use std::slice::Iter; /// /// into: /// -/// ```rust +/// ```ignore (MIR) /// _LOCAL_0 = move _LOCAL_1 /// ``` pub struct SimplifyArmIdentity; @@ -472,7 +474,7 @@ impl Visitor<'_> for LocalUseCounter { } /// Match on: -/// ```rust +/// ```ignore (MIR) /// _LOCAL_INTO = ((_LOCAL_FROM as Variant).FIELD: TY); /// ``` fn match_get_variant_field<'tcx>( @@ -492,7 +494,7 @@ fn match_get_variant_field<'tcx>( } /// Match on: -/// ```rust +/// ```ignore (MIR) /// ((_LOCAL_FROM as Variant).FIELD: TY) = move _LOCAL_INTO; /// ``` fn match_set_variant_field<'tcx>(stmt: &Statement<'tcx>) -> Option<(Local, Local, VarField<'tcx>)> { @@ -507,7 +509,7 @@ fn match_set_variant_field<'tcx>(stmt: &Statement<'tcx>) -> Option<(Local, Local } /// Match on: -/// ```rust +/// ```ignore (MIR) /// discriminant(_LOCAL_TO_SET) = VAR_IDX; /// ``` fn match_set_discr(stmt: &Statement<'_>) -> Option<(Local, VariantIdx)> { @@ -690,7 +692,7 @@ impl<'tcx> SimplifyBranchSameOptimizationFinder<'_, 'tcx> { /// /// Statements can be trivially equal if the kinds match. /// But they can also be considered equal in the following case A: - /// ``` + /// ```ignore (MIR) /// discriminant(_0) = 0; // bb1 /// _0 = move _1; // bb2 /// ``` diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 1828aecb375..ee02151152c 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -91,12 +91,13 @@ //! another function. It suffices to just take a reference in order to introduce //! an edge. Consider the following example: //! -//! ```rust +//! ``` +//! # use core::fmt::Display; //! fn print_val<T: Display>(x: T) { //! println!("{}", x); //! } //! -//! fn call_fn(f: &Fn(i32), x: i32) { +//! fn call_fn(f: &dyn Fn(i32), x: i32) { //! f(x); //! } //! @@ -445,12 +446,9 @@ fn collect_items_rec<'tcx>( // depend on any other items. } hir::InlineAsmOperand::SymFn { anon_const } => { - let def_id = tcx.hir().body_owner_def_id(anon_const.body).to_def_id(); - if let Ok(val) = tcx.const_eval_poly(def_id) { - rustc_data_structures::stack::ensure_sufficient_stack(|| { - collect_const_value(tcx, val, &mut neighbors); - }); - } + let fn_ty = + tcx.typeck_body(anon_const.body).node_type(anon_const.hir_id); + visit_fn_use(tcx, fn_ty, false, *op_sp, &mut neighbors); } hir::InlineAsmOperand::SymStatic { path: _, def_id } => { let instance = Instance::mono(tcx, *def_id); @@ -1167,7 +1165,7 @@ struct RootCollector<'a, 'tcx> { impl<'v> RootCollector<'_, 'v> { fn process_item(&mut self, id: hir::ItemId) { - match self.tcx.hir().def_kind(id.def_id) { + match self.tcx.def_kind(id.def_id) { DefKind::Enum | DefKind::Struct | DefKind::Union => { let item = self.tcx.hir().item(id); match item.kind { @@ -1228,7 +1226,7 @@ impl<'v> RootCollector<'_, 'v> { } fn process_impl_item(&mut self, id: hir::ImplItemId) { - if matches!(self.tcx.hir().def_kind(id.def_id), DefKind::AssocFn) { + if matches!(self.tcx.def_kind(id.def_id), DefKind::AssocFn) { self.push_if_root(id.def_id); } } diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index f0333d6c6da..ef4560b5ec4 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -1,5 +1,4 @@ #![feature(array_windows)] -#![feature(crate_visibility_modifier)] #![feature(control_flow_enum)] #![feature(let_else)] #![recursion_limit = "256"] diff --git a/compiler/rustc_monomorphize/src/polymorphize.rs b/compiler/rustc_monomorphize/src/polymorphize.rs index cf13c856a71..3cfd935d8b0 100644 --- a/compiler/rustc_monomorphize/src/polymorphize.rs +++ b/compiler/rustc_monomorphize/src/polymorphize.rs @@ -197,7 +197,7 @@ fn emit_unused_generic_params_error<'tcx>( unused_parameters: &FiniteBitSet<u32>, ) { let base_def_id = tcx.typeck_root_def_id(def_id); - if !tcx.get_attrs(base_def_id).iter().any(|a| a.has_name(sym::rustc_polymorphize_error)) { + if !tcx.has_attr(base_def_id, sym::rustc_polymorphize_error) { return; } diff --git a/compiler/rustc_monomorphize/src/util.rs b/compiler/rustc_monomorphize/src/util.rs index 04baa01832b..d3aaad46015 100644 --- a/compiler/rustc_monomorphize/src/util.rs +++ b/compiler/rustc_monomorphize/src/util.rs @@ -7,7 +7,7 @@ use std::io::prelude::*; /// /// During the same compile all closures dump the information in the same file /// "closure_profile_XXXXX.csv", which is created in the directory where the compiler is invoked. -crate fn dump_closure_profile<'tcx>(tcx: TyCtxt<'tcx>, closure_instance: Instance<'tcx>) { +pub(crate) fn dump_closure_profile<'tcx>(tcx: TyCtxt<'tcx>, closure_instance: Instance<'tcx>) { let Ok(mut file) = OpenOptions::new() .create(true) .append(true) diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index ee54dd44f71..e9701ec2d7f 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -31,7 +31,7 @@ pub struct UnmatchedBrace { pub candidate_span: Option<Span>, } -crate fn parse_token_trees<'a>( +pub(crate) fn parse_token_trees<'a>( sess: &'a ParseSess, src: &'a str, start_pos: BytePos, diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 28c2a63db27..df1765952ce 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -2,7 +2,6 @@ #![feature(array_windows)] #![feature(box_patterns)] -#![feature(crate_visibility_modifier)] #![feature(if_let_guard)] #![feature(let_chains)] #![feature(let_else)] @@ -13,11 +12,8 @@ extern crate tracing; use rustc_ast as ast; -use rustc_ast::token::{self, Nonterminal, Token, TokenKind}; -use rustc_ast::tokenstream::{self, AttributesData, CanSynthesizeMissingTokens, LazyTokenStream}; -use rustc_ast::tokenstream::{AttrAnnotatedTokenStream, AttrAnnotatedTokenTree}; -use rustc_ast::tokenstream::{Spacing, TokenStream}; -use rustc_ast::AstLike; +use rustc_ast::token; +use rustc_ast::tokenstream::TokenStream; use rustc_ast::Attribute; use rustc_ast::{AttrItem, MetaItem}; use rustc_ast_pretty::pprust; @@ -27,7 +23,6 @@ use rustc_session::parse::ParseSess; use rustc_span::{FileName, SourceFile, Span}; use std::path::Path; -use std::str; pub const MACRO_ARGUMENTS: Option<&str> = Some("macro arguments"); @@ -241,91 +236,10 @@ pub fn parse_in<'a, T>( Ok(result) } -// NOTE(Centril): The following probably shouldn't be here but it acknowledges the -// fact that architecturally, we are using parsing (read on below to understand why). - -pub fn nt_to_tokenstream( - nt: &Nonterminal, - sess: &ParseSess, - synthesize_tokens: CanSynthesizeMissingTokens, -) -> TokenStream { - // A `Nonterminal` is often a parsed AST item. At this point we now - // need to convert the parsed AST to an actual token stream, e.g. - // un-parse it basically. - // - // Unfortunately there's not really a great way to do that in a - // guaranteed lossless fashion right now. The fallback here is to just - // stringify the AST node and reparse it, but this loses all span - // information. - // - // As a result, some AST nodes are annotated with the token stream they - // came from. Here we attempt to extract these lossless token streams - // before we fall back to the stringification. - - let convert_tokens = - |tokens: Option<&LazyTokenStream>| Some(tokens?.create_token_stream().to_tokenstream()); - - let tokens = match *nt { - Nonterminal::NtItem(ref item) => prepend_attrs(&item.attrs, item.tokens.as_ref()), - Nonterminal::NtBlock(ref block) => convert_tokens(block.tokens.as_ref()), - Nonterminal::NtStmt(ref stmt) if let ast::StmtKind::Empty = stmt.kind => { - let tokens = AttrAnnotatedTokenStream::new(vec![( - tokenstream::AttrAnnotatedTokenTree::Token(Token::new( - TokenKind::Semi, - stmt.span, - )), - Spacing::Alone, - )]); - prepend_attrs(&stmt.attrs(), Some(&LazyTokenStream::new(tokens))) - } - Nonterminal::NtStmt(ref stmt) => prepend_attrs(&stmt.attrs(), stmt.tokens()), - Nonterminal::NtPat(ref pat) => convert_tokens(pat.tokens.as_ref()), - Nonterminal::NtTy(ref ty) => convert_tokens(ty.tokens.as_ref()), - Nonterminal::NtIdent(ident, is_raw) => { - Some(tokenstream::TokenTree::token(token::Ident(ident.name, is_raw), ident.span).into()) - } - Nonterminal::NtLifetime(ident) => { - Some(tokenstream::TokenTree::token(token::Lifetime(ident.name), ident.span).into()) - } - Nonterminal::NtMeta(ref attr) => convert_tokens(attr.tokens.as_ref()), - Nonterminal::NtPath(ref path) => convert_tokens(path.tokens.as_ref()), - Nonterminal::NtVis(ref vis) => convert_tokens(vis.tokens.as_ref()), - Nonterminal::NtExpr(ref expr) | Nonterminal::NtLiteral(ref expr) => { - prepend_attrs(&expr.attrs, expr.tokens.as_ref()) - } - }; - - if let Some(tokens) = tokens { - return tokens; - } else if matches!(synthesize_tokens, CanSynthesizeMissingTokens::Yes) { - return fake_token_stream(sess, nt); - } else { - panic!( - "Missing tokens for nt {:?} at {:?}: {:?}", - nt, - nt.span(), - pprust::nonterminal_to_string(nt) - ); - } -} - -fn prepend_attrs(attrs: &[Attribute], tokens: Option<&LazyTokenStream>) -> Option<TokenStream> { - let tokens = tokens?; - if attrs.is_empty() { - return Some(tokens.create_token_stream().to_tokenstream()); - } - let attr_data = AttributesData { attrs: attrs.to_vec().into(), tokens: tokens.clone() }; - let wrapped = AttrAnnotatedTokenStream::new(vec![( - AttrAnnotatedTokenTree::Attributes(attr_data), - Spacing::Alone, - )]); - Some(wrapped.to_tokenstream()) -} - -pub fn fake_token_stream(sess: &ParseSess, nt: &Nonterminal) -> TokenStream { - let source = pprust::nonterminal_to_string(nt); +pub fn fake_token_stream_for_item(sess: &ParseSess, item: &ast::Item) -> TokenStream { + let source = pprust::item_to_string(item); let filename = FileName::macro_expansion_source_code(&source); - parse_stream_from_source_str(filename, source, sess, Some(nt.span())) + parse_stream_from_source_str(filename, source, sess, Some(item.span)) } pub fn fake_token_stream_for_crate(sess: &ParseSess, krate: &ast::Crate) -> TokenStream { diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 358b01df3b9..3ae8bb07cd0 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -284,7 +284,7 @@ impl<'a> Parser<'a> { /// terminated by a semicolon. /// /// Matches `inner_attrs*`. - crate fn parse_inner_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> { + pub(crate) fn parse_inner_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> { let mut attrs: Vec<ast::Attribute> = vec![]; loop { let start_pos: u32 = self.token_cursor.num_next_calls.try_into().unwrap(); @@ -322,7 +322,7 @@ impl<'a> Parser<'a> { Ok(attrs) } - crate fn parse_unsuffixed_lit(&mut self) -> PResult<'a, ast::Lit> { + pub(crate) fn parse_unsuffixed_lit(&mut self) -> PResult<'a, ast::Lit> { let lit = self.parse_lit()?; debug!("checking if {:?} is unusuffixed", lit); @@ -358,7 +358,7 @@ impl<'a> Parser<'a> { } /// Matches `COMMASEP(meta_item_inner)`. - crate fn parse_meta_seq_top(&mut self) -> PResult<'a, Vec<ast::NestedMetaItem>> { + pub(crate) fn parse_meta_seq_top(&mut self) -> PResult<'a, Vec<ast::NestedMetaItem>> { // Presumably, the majority of the time there will only be one attr. let mut nmis = Vec::with_capacity(1); while self.token.kind != token::Eof { @@ -371,9 +371,10 @@ impl<'a> Parser<'a> { } /// Matches the following grammar (per RFC 1559). - /// - /// meta_item : PATH ( '=' UNSUFFIXED_LIT | '(' meta_item_inner? ')' )? ; - /// meta_item_inner : (meta_item | UNSUFFIXED_LIT) (',' meta_item_inner)? ; + /// ```ebnf + /// meta_item : PATH ( '=' UNSUFFIXED_LIT | '(' meta_item_inner? ')' )? ; + /// meta_item_inner : (meta_item | UNSUFFIXED_LIT) (',' meta_item_inner)? ; + /// ``` pub fn parse_meta_item(&mut self) -> PResult<'a, ast::MetaItem> { let nt_meta = match self.token.kind { token::Interpolated(ref nt) => match **nt { @@ -400,7 +401,7 @@ impl<'a> Parser<'a> { Ok(ast::MetaItem { path, kind, span }) } - crate fn parse_meta_item_kind(&mut self) -> PResult<'a, ast::MetaItemKind> { + pub(crate) fn parse_meta_item_kind(&mut self) -> PResult<'a, ast::MetaItemKind> { Ok(if self.eat(&token::Eq) { ast::MetaItemKind::NameValue(self.parse_unsuffixed_lit()?) } else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) { diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index a12621564ab..6c750ff428f 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -3,7 +3,7 @@ use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::{AttrAnnotatedTokenStream, AttributesData, CreateTokenStream}; use rustc_ast::tokenstream::{AttrAnnotatedTokenTree, DelimSpan, LazyTokenStream, Spacing}; use rustc_ast::{self as ast}; -use rustc_ast::{AstLike, AttrVec, Attribute}; +use rustc_ast::{AttrVec, Attribute, HasAttrs, HasTokens}; use rustc_errors::PResult; use rustc_span::{sym, Span}; @@ -192,7 +192,7 @@ impl<'a> Parser<'a> { /// This restriction shouldn't be an issue in practice, /// since this function is used to record the tokens for /// a parsed AST item, which always has matching delimiters. - pub fn collect_tokens_trailing_token<R: AstLike>( + pub fn collect_tokens_trailing_token<R: HasAttrs + HasTokens>( &mut self, attrs: AttrWrapper, force_collect: ForceCollect, @@ -388,12 +388,6 @@ impl<'a> Parser<'a> { /// Converts a flattened iterator of tokens (including open and close delimiter tokens) /// into a `TokenStream`, creating a `TokenTree::Delimited` for each matching pair /// of open and close delims. -// FIXME(#67062): Currently, we don't parse `Invisible`-delimited groups correctly, -// which can cause us to end up with mismatched `Invisible` delimiters in our -// captured tokens. This function contains several hacks to work around this - -// essentially, we throw away mismatched `Invisible` delimiters when we encounter them. -// Once we properly parse `Invisible` delimiters, they can be captured just like any -// other tokens, and these hacks can be removed. fn make_token_stream( mut iter: impl Iterator<Item = (FlatToken, Spacing)>, break_last_token: bool, @@ -412,35 +406,10 @@ fn make_token_stream( stack.push(FrameData { open_delim_sp: Some((delim, span)), inner: vec![] }); } FlatToken::Token(Token { kind: TokenKind::CloseDelim(delim), span }) => { - // HACK: If we encounter a mismatched `Invisible` delimiter at the top - // level, just ignore it. - if matches!(delim, Delimiter::Invisible) - && (stack.len() == 1 - || !matches!( - stack.last_mut().unwrap().open_delim_sp.unwrap().0, - Delimiter::Invisible - )) - { - token_and_spacing = iter.next(); - continue; - } let frame_data = stack .pop() .unwrap_or_else(|| panic!("Token stack was empty for token: {:?}", token)); - // HACK: If our current frame has a mismatched opening `Invisible` delimiter, - // merge our current frame with the one above it. That is, transform - // `[ { < first second } third ]` into `[ { first second } third ]` - if !matches!(delim, Delimiter::Invisible) - && matches!(frame_data.open_delim_sp.unwrap().0, Delimiter::Invisible) - { - stack.last_mut().unwrap().inner.extend(frame_data.inner); - // Process our closing delimiter again, this time at the previous - // frame in the stack - token_and_spacing = Some((token, spacing)); - continue; - } - let (open_delim, open_sp) = frame_data.open_delim_sp.unwrap(); assert_eq!( open_delim, delim, @@ -472,13 +441,6 @@ fn make_token_stream( } token_and_spacing = iter.next(); } - // HACK: If we don't have a closing `Invisible` delimiter for our last - // frame, merge the frame with the top-level frame. That is, - // turn `< first second` into `first second` - if stack.len() == 2 && stack[1].open_delim_sp.unwrap().0 == Delimiter::Invisible { - let temp_buf = stack.pop().unwrap(); - stack.last_mut().unwrap().inner.extend(temp_buf.inner); - } let mut final_buf = stack.pop().expect("Missing final buf!"); if break_last_token { let (last_token, spacing) = final_buf.inner.pop().unwrap(); diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index beffbdc5de4..69e12063cc1 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -21,7 +21,7 @@ use rustc_errors::{pluralize, struct_span_err, Diagnostic, EmissionGuarantee, Er use rustc_errors::{ Applicability, DiagnosticBuilder, DiagnosticMessage, Handler, MultiSpan, PResult, }; -use rustc_macros::SessionDiagnostic; +use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic}; use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, Ident}; use rustc_span::{Span, SpanSnippetError, DUMMY_SP}; @@ -132,7 +132,7 @@ impl RecoverQPath for Expr { } /// Control whether the closing delimiter should be consumed when calling `Parser::consume_block`. -crate enum ConsumeClosingDelim { +pub(crate) enum ConsumeClosingDelim { Yes, No, } @@ -252,6 +252,40 @@ struct AmbiguousPlus { pub span: Span, } +#[derive(SessionDiagnostic)] +#[error(code = "E0178", slug = "parser-maybe-recover-from-bad-type-plus")] +struct BadTypePlus<'a> { + pub ty: String, + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub sub: BadTypePlusSub<'a>, +} + +#[derive(SessionSubdiagnostic, Clone, Copy)] +pub enum BadTypePlusSub<'a> { + #[suggestion( + slug = "parser-add-paren", + code = "{sum_with_parens}", + applicability = "machine-applicable" + )] + AddParen { + sum_with_parens: &'a str, + #[primary_span] + span: Span, + }, + #[label(slug = "parser-forgot-paren")] + ForgotParen { + #[primary_span] + span: Span, + }, + #[label(slug = "parser-expect-path")] + ExpectPath { + #[primary_span] + span: Span, + }, +} + // SnapshotParser is used to create a snapshot of the parser // without causing duplicate errors being emitted when the `Parser` // is dropped. @@ -1255,17 +1289,11 @@ impl<'a> Parser<'a> { let bounds = self.parse_generic_bounds(None)?; let sum_span = ty.span.to(self.prev_token.span); - let mut err = struct_span_err!( - self.sess.span_diagnostic, - sum_span, - E0178, - "expected a path on the left-hand side of `+`, not `{}`", - pprust::ty_to_string(ty) - ); + let sum_with_parens: String; - match ty.kind { + let sub = match ty.kind { TyKind::Rptr(ref lifetime, ref mut_ty) => { - let sum_with_parens = pprust::to_string(|s| { + sum_with_parens = pprust::to_string(|s| { s.s.word("&"); s.print_opt_lifetime(lifetime); s.print_mutability(mut_ty.mutbl, false); @@ -1274,21 +1302,15 @@ impl<'a> Parser<'a> { s.print_type_bounds(" +", &bounds); s.pclose() }); - err.span_suggestion( - sum_span, - "try adding parentheses", - sum_with_parens, - Applicability::MachineApplicable, - ); - } - TyKind::Ptr(..) | TyKind::BareFn(..) => { - err.span_label(sum_span, "perhaps you forgot parentheses?"); - } - _ => { - err.span_label(sum_span, "expected a path"); + + BadTypePlusSub::AddParen { sum_with_parens: &sum_with_parens, span: sum_span } } - } - err.emit(); + TyKind::Ptr(..) | TyKind::BareFn(..) => BadTypePlusSub::ForgotParen { span: sum_span }, + _ => BadTypePlusSub::ExpectPath { span: sum_span }, + }; + + self.sess.emit_err(BadTypePlus { ty: pprust::ty_to_string(ty), span: sum_span, sub }); + Ok(()) } @@ -2109,8 +2131,7 @@ impl<'a> Parser<'a> { brace_depth -= 1; continue; } - } else if self.token == token::Eof || self.eat(&token::CloseDelim(Delimiter::Invisible)) - { + } else if self.token == token::Eof { return; } else { self.bump(); @@ -2438,7 +2459,7 @@ impl<'a> Parser<'a> { /// Some special error handling for the "top-level" patterns in a match arm, /// `for` loop, `let`, &c. (in contrast to subpatterns within such). - crate fn maybe_recover_colon_colon_in_pat_typo( + pub(crate) fn maybe_recover_colon_colon_in_pat_typo( &mut self, mut first_pat: P<Pat>, ra: RecoverColon, @@ -2554,7 +2575,7 @@ impl<'a> Parser<'a> { first_pat } - crate fn maybe_recover_unexpected_block_label(&mut self) -> bool { + pub(crate) fn maybe_recover_unexpected_block_label(&mut self) -> bool { let Some(label) = self.eat_label().filter(|_| { self.eat(&token::Colon) && self.token.kind == token::OpenDelim(Delimiter::Brace) }) else { @@ -2575,7 +2596,7 @@ impl<'a> Parser<'a> { /// Some special error handling for the "top-level" patterns in a match arm, /// `for` loop, `let`, &c. (in contrast to subpatterns within such). - crate fn maybe_recover_unexpected_comma( + pub(crate) fn maybe_recover_unexpected_comma( &mut self, lo: Span, rc: RecoverComma, @@ -2622,7 +2643,7 @@ impl<'a> Parser<'a> { Err(err) } - crate fn maybe_recover_bounds_doubled_colon(&mut self, ty: &Ty) -> PResult<'a, ()> { + pub(crate) fn maybe_recover_bounds_doubled_colon(&mut self, ty: &Ty) -> PResult<'a, ()> { let TyKind::Path(qself, path) = &ty.kind else { return Ok(()) }; let qself_position = qself.as_ref().map(|qself| qself.position); for (i, segments) in path.segments.windows(2).enumerate() { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 6114e7aaa7b..b5467c659a2 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2380,7 +2380,7 @@ impl<'a> Parser<'a> { Ok(self.mk_expr(lo.to(self.prev_token.span), ExprKind::Loop(body, opt_label), attrs)) } - crate fn eat_label(&mut self) -> Option<Label> { + pub(crate) fn eat_label(&mut self) -> Option<Label> { self.token.lifetime().map(|ident| { self.bump(); Label { ident } @@ -3049,7 +3049,7 @@ impl<'a> Parser<'a> { await_expr } - crate fn mk_expr(&self, span: Span, kind: ExprKind, attrs: AttrVec) -> P<Expr> { + pub(crate) fn mk_expr(&self, span: Span, kind: ExprKind, attrs: AttrVec) -> P<Expr> { P(Expr { kind, span, attrs, id: DUMMY_NODE_ID, tokens: None }) } diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index 8081bac7cfd..1930dec8c3b 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -51,7 +51,7 @@ impl<'a> Parser<'a> { }) } - crate fn parse_const_param( + pub(crate) fn parse_const_param( &mut self, preceding_attrs: Vec<Attribute>, ) -> PResult<'a, GenericParam> { diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 77a03428c16..0f940cffcc4 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -485,7 +485,7 @@ impl<'a> Parser<'a> { /// Parses an implementation item. /// - /// ``` + /// ```ignore (illustrative) /// impl<'a, T> TYPE { /* impl items */ } /// impl<'a, T> TRAIT for TYPE { /* impl items */ } /// impl<'a, T> !TRAIT for TYPE { /* impl items */ } @@ -493,7 +493,7 @@ impl<'a> Parser<'a> { /// ``` /// /// We actually parse slightly more relaxed grammar for better error reporting and recovery. - /// ``` + /// ```ebnf /// "impl" GENERICS "const"? "!"? TYPE "for"? (TYPE | "..") ("where" PREDICATES)? "{" BODY "}" /// "impl" GENERICS "const"? "!"? TYPE ("where" PREDICATES)? "{" BODY "}" /// ``` @@ -806,7 +806,7 @@ impl<'a> Parser<'a> { } /// Parses a `type` alias with the following grammar: - /// ``` + /// ```ebnf /// TypeAlias = "type" Ident Generics {":" GenericBounds}? {"=" Ty}? ";" ; /// ``` /// The `"type"` has already been eaten. @@ -930,7 +930,7 @@ impl<'a> Parser<'a> { /// /// # Examples /// - /// ``` + /// ```ignore (illustrative) /// extern crate foo; /// extern crate bar as foo; /// ``` @@ -1630,7 +1630,7 @@ impl<'a> Parser<'a> { /// Parses a declarative macro 2.0 definition. /// The `macro` keyword has already been parsed. - /// ``` + /// ```ebnf /// MacBody = "{" TOKEN_STREAM "}" ; /// MacParams = "(" TOKEN_STREAM ")" ; /// DeclMac = "macro" Ident MacParams? MacBody ; diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 63112f23605..5bd07c31c0e 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -25,9 +25,9 @@ use rustc_ast::tokenstream::{self, DelimSpan, Spacing}; use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_ast::AttrId; use rustc_ast::DUMMY_NODE_ID; -use rustc_ast::{self as ast, AnonConst, AstLike, AttrStyle, AttrVec, Const, CrateSugar, Extern}; +use rustc_ast::{self as ast, AnonConst, AttrStyle, AttrVec, Const, CrateSugar, Extern}; use rustc_ast::{Async, Expr, ExprKind, MacArgs, MacArgsEq, MacDelimiter, Mutability, StrLit}; -use rustc_ast::{Unsafe, Visibility, VisibilityKind}; +use rustc_ast::{HasAttrs, HasTokens, Unsafe, Visibility, VisibilityKind}; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; use rustc_errors::PResult; @@ -1389,7 +1389,7 @@ impl<'a> Parser<'a> { } } - pub fn collect_tokens_no_attrs<R: AstLike>( + pub fn collect_tokens_no_attrs<R: HasAttrs + HasTokens>( &mut self, f: impl FnOnce(&mut Self) -> PResult<'a, R>, ) -> PResult<'a, R> { @@ -1415,7 +1415,7 @@ impl<'a> Parser<'a> { } } -crate fn make_unclosed_delims_error( +pub(crate) fn make_unclosed_delims_error( unmatched: UnmatchedBrace, sess: &ParseSess, ) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> { diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 6974f318f94..e215b6872bf 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -1,6 +1,6 @@ use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, NonterminalKind, Token}; -use rustc_ast::AstLike; +use rustc_ast::HasTokens; use rustc_ast_pretty::pprust; use rustc_errors::PResult; use rustc_span::symbol::{kw, Ident}; diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index ac693597662..27a6a487474 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -13,10 +13,8 @@ use rustc_ast as ast; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, TokenKind}; use rustc_ast::util::classify; -use rustc_ast::{ - AstLike, AttrStyle, AttrVec, Attribute, LocalKind, MacCall, MacCallStmt, MacStmtStyle, -}; -use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, Local, Stmt}; +use rustc_ast::{AttrStyle, AttrVec, Attribute, LocalKind, MacCall, MacCallStmt, MacStmtStyle}; +use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, HasAttrs, Local, Stmt}; use rustc_ast::{StmtKind, DUMMY_NODE_ID}; use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, PResult}; use rustc_span::source_map::{BytePos, Span}; @@ -38,7 +36,7 @@ impl<'a> Parser<'a> { /// If `force_capture` is true, forces collection of tokens regardless of whether /// or not we have attributes - crate fn parse_stmt_without_recovery( + pub(crate) fn parse_stmt_without_recovery( &mut self, capture_semi: bool, force_collect: ForceCollect, @@ -502,7 +500,7 @@ impl<'a> Parser<'a> { /// Parses the rest of a block expression or function body. /// Precondition: already parsed the '{'. - crate fn parse_block_tail( + pub(crate) fn parse_block_tail( &mut self, lo: Span, s: BlockCheckMode, diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 9e771a8af1a..fb3f5eb3f9f 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -52,7 +52,7 @@ pub(super) enum RecoverQuestionMark { /// Signals whether parsing a type should recover `->`. /// /// More specifically, when parsing a function like: -/// ```rust +/// ```compile_fail /// fn foo() => u8 { 0 } /// fn bar(): u8 { 0 } /// ``` @@ -499,12 +499,12 @@ impl<'a> Parser<'a> { } /// Parses a function pointer type (`TyKind::BareFn`). - /// ``` - /// [unsafe] [extern "ABI"] fn (S) -> T - /// ^~~~~^ ^~~~^ ^~^ ^ - /// | | | | - /// | | | Return type - /// Function Style ABI Parameter types + /// ```ignore (illustrative) + /// [unsafe] [extern "ABI"] fn (S) -> T + /// // ^~~~~^ ^~~~^ ^~^ ^ + /// // | | | | + /// // | | | Return type + /// // Function Style ABI Parameter types /// ``` /// We actually parse `FnHeader FnDecl`, but we error on `const` and `async` qualifiers. fn parse_ty_bare_fn( @@ -518,6 +518,7 @@ impl<'a> Parser<'a> { kind: rustc_ast::VisibilityKind::Inherited, tokens: None, }; + let span_start = self.token.span; let ast::FnHeader { ext, unsafety, constness, asyncness } = self.parse_fn_front_matter(&inherited_vis)?; let decl = self.parse_fn_decl(|_| false, AllowPlus::No, recover_return_sign)?; @@ -531,7 +532,8 @@ impl<'a> Parser<'a> { if let ast::Async::Yes { span, .. } = asyncness { self.error_fn_ptr_bad_qualifier(whole_span, span, "async"); } - Ok(TyKind::BareFn(P(BareFnTy { ext, unsafety, generic_params: params, decl }))) + let decl_span = span_start.to(self.token.span); + Ok(TyKind::BareFn(P(BareFnTy { ext, unsafety, generic_params: params, decl, decl_span }))) } /// Emit an error for the given bad function pointer qualifier. @@ -707,7 +709,7 @@ impl<'a> Parser<'a> { } /// Parses a bound according to the grammar: - /// ``` + /// ```ebnf /// BOUND = TY_BOUND | LT_BOUND /// ``` fn parse_generic_bound(&mut self) -> PResult<'a, Result<GenericBound, Span>> { @@ -729,7 +731,7 @@ impl<'a> Parser<'a> { } /// Parses a lifetime ("outlives") bound, e.g. `'a`, according to: - /// ``` + /// ```ebnf /// LT_BOUND = LIFETIME /// ``` fn parse_generic_lt_bound( @@ -787,7 +789,7 @@ impl<'a> Parser<'a> { /// /// If no modifiers are present, this does not consume any tokens. /// - /// ``` + /// ```ebnf /// TY_BOUND_MODIFIERS = ["~const"] ["?"] /// ``` fn parse_ty_bound_modifiers(&mut self) -> PResult<'a, BoundModifiers> { @@ -807,7 +809,7 @@ impl<'a> Parser<'a> { } /// Parses a type bound according to: - /// ``` + /// ```ebnf /// TY_BOUND = TY_BOUND_NOPAREN | (TY_BOUND_NOPAREN) /// TY_BOUND_NOPAREN = [TY_BOUND_MODIFIERS] [for<LT_PARAM_DEFS>] SIMPLE_PATH /// ``` diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index fd7e2901ee2..3d5da114ecf 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -4,7 +4,8 @@ //! conflicts between multiple such attributes attached to the same //! item. -use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; +use rustc_ast::tokenstream::DelimSpan; +use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, MacArgs, MetaItemKind, NestedMetaItem}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan}; use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; @@ -22,6 +23,7 @@ use rustc_session::lint::builtin::{ use rustc_session::parse::feature_err; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::{Span, DUMMY_SP}; +use rustc_target::spec::abi::Abi; use std::collections::hash_map::Entry; pub(crate) fn target_from_impl_item<'tcx>( @@ -104,6 +106,9 @@ impl CheckAttrVisitor<'_> { sym::rustc_allow_const_fn_unstable => { self.check_rustc_allow_const_fn_unstable(hir_id, &attr, span, target) } + sym::rustc_std_internal_symbol => { + self.check_rustc_std_internal_symbol(&attr, span, target) + } sym::naked => self.check_naked(hir_id, attr, span, target), sym::rustc_legacy_const_generics => { self.check_rustc_legacy_const_generics(&attr, span, target, item) @@ -125,6 +130,9 @@ impl CheckAttrVisitor<'_> { sym::rustc_allow_incoherent_impl => { self.check_allow_incoherent_impl(&attr, span, target) } + sym::rustc_has_incoherent_inherent_impls => { + self.check_has_incoherent_inherent_impls(&attr, span, target) + } sym::rustc_const_unstable | sym::rustc_const_stable | sym::unstable @@ -190,6 +198,7 @@ impl CheckAttrVisitor<'_> { return; } + // FIXME(@lcnr): this doesn't belong here. if matches!(target, Target::Closure | Target::Fn | Target::Method(_) | Target::ForeignFn) { self.tcx.ensure().codegen_fn_attrs(self.tcx.hir().local_def_id(hir_id)); } @@ -807,6 +816,68 @@ impl CheckAttrVisitor<'_> { } } + /// Checks `#[doc(hidden)]` attributes. Returns `true` if valid. + fn check_doc_hidden( + &self, + attr: &Attribute, + meta_index: usize, + meta: &NestedMetaItem, + hir_id: HirId, + target: Target, + ) -> bool { + if let Target::AssocConst + | Target::AssocTy + | Target::Method(MethodKind::Trait { body: true }) = target + { + let parent_hir_id = self.tcx.hir().get_parent_item(hir_id); + let containing_item = self.tcx.hir().expect_item(parent_hir_id); + + if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = containing_item.kind { + let meta_items = attr.meta_item_list().unwrap(); + + let (span, replacement_span) = if meta_items.len() == 1 { + (attr.span, attr.span) + } else { + let meta_span = meta.span(); + ( + meta_span, + meta_span.until(match meta_items.get(meta_index + 1) { + Some(next_item) => next_item.span(), + None => match attr.get_normal_item().args { + MacArgs::Delimited(DelimSpan { close, .. }, ..) => close, + _ => unreachable!(), + }, + }), + ) + }; + + // FIXME: #[doc(hidden)] was previously erroneously allowed on trait impl items, + // so for backward compatibility only emit a warning and do not mark it as invalid. + self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, span, |lint| { + lint.build("`#[doc(hidden)]` is ignored on trait impl items") + .warn( + "this was previously accepted by the compiler but is \ + being phased out; it will become a hard error in \ + a future release!", + ) + .note( + "whether the impl item is `doc(hidden)` or not \ + entirely depends on the corresponding trait item", + ) + .span_suggestion( + replacement_span, + "remove this attribute", + String::new(), + Applicability::MachineApplicable, + ) + .emit(); + }); + } + } + + true + } + /// Checks that an attribute is *not* used at the crate level. Returns `true` if valid. fn check_attr_not_crate_level( &self, @@ -925,7 +996,7 @@ impl CheckAttrVisitor<'_> { let mut is_valid = true; if let Some(mi) = attr.meta() && let Some(list) = mi.meta_item_list() { - for meta in list { + for (meta_index, meta) in list.into_iter().enumerate() { if let Some(i_meta) = meta.meta_item() { match i_meta.name_or_empty() { sym::alias @@ -966,6 +1037,15 @@ impl CheckAttrVisitor<'_> { is_valid = false; } + sym::hidden if !self.check_doc_hidden(attr, + meta_index, + meta, + hir_id, + target, + ) => { + is_valid = false; + } + // no_default_passes: deprecated // passes: deprecated // plugins: removed, but rustdoc warns about it itself @@ -1096,7 +1176,6 @@ impl CheckAttrVisitor<'_> { } } - /// Warns against some misuses of `#[pass_by_value]` fn check_allow_incoherent_impl(&self, attr: &Attribute, span: Span, target: Target) -> bool { match target { Target::Method(MethodKind::Inherent) => true, @@ -1114,6 +1193,30 @@ impl CheckAttrVisitor<'_> { } } + fn check_has_incoherent_inherent_impls( + &self, + attr: &Attribute, + span: Span, + target: Target, + ) -> bool { + match target { + Target::Trait | Target::Struct | Target::Enum | Target::Union | Target::ForeignTy => { + true + } + _ => { + self.tcx + .sess + .struct_span_err( + attr.span, + "`rustc_has_incoherent_inherent_impls` attribute should be applied to types or traits.", + ) + .span_label(span, "only adts, extern types and traits are supported") + .emit(); + false + } + } + } + /// Warns against some misuses of `#[must_use]` fn check_must_use(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool { let node = self.tcx.hir().get(hir_id); @@ -1215,22 +1318,27 @@ impl CheckAttrVisitor<'_> { /// Checks if `#[link]` is applied to an item other than a foreign module. fn check_link(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { - match target { - Target::ForeignMod => {} - _ => { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - let mut diag = lint.build("attribute should be applied to an `extern` block"); - diag.warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", - ); + if target == Target::ForeignMod + && let hir::Node::Item(item) = self.tcx.hir().get(hir_id) + && let Item { kind: ItemKind::ForeignMod { abi, .. }, .. } = item + && !matches!(abi, Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic) + { + return; + } - diag.span_label(span, "not an `extern` block"); - diag.emit(); - }); + self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { + let mut diag = + lint.build("attribute should be applied to an `extern` block with non-Rust ABI"); + diag.warn( + "this was previously accepted by the compiler but is \ + being phased out; it will become a hard error in \ + a future release!", + ); + if target != Target::ForeignMod { + diag.span_label(span, "not an `extern` block"); } - } + diag.emit(); + }); } /// Checks if `#[link_name]` is applied to an item other than a foreign function or static. @@ -1633,7 +1741,7 @@ impl CheckAttrVisitor<'_> { } } sym::align => { - if let (Target::Fn, true) = (target, !self.tcx.features().fn_align) { + if let (Target::Fn, false) = (target, self.tcx.features().fn_align) { feature_err( &self.tcx.sess.parse_sess, sym::fn_align, @@ -1954,6 +2062,25 @@ impl CheckAttrVisitor<'_> { } } + fn check_rustc_std_internal_symbol( + &self, + attr: &Attribute, + span: Span, + target: Target, + ) -> bool { + match target { + Target::Fn | Target::Static => true, + _ => { + self.tcx + .sess + .struct_span_err(attr.span, "attribute should be applied functions or statics") + .span_label(span, "not a function or static") + .emit(); + false + } + } + } + /// default_method_body_is_const should only be applied to trait methods with default bodies. fn check_default_method_body_is_const( &self, @@ -2263,7 +2390,7 @@ fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>) fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { let check_attr_visitor = &mut CheckAttrVisitor { tcx }; - tcx.hir().visit_item_likes_in_module(module_def_id, &mut check_attr_visitor.as_deep_visitor()); + tcx.hir().deep_visit_item_likes_in_module(module_def_id, check_attr_visitor); if module_def_id.is_top_level_module() { check_attr_visitor.check_attributes(CRATE_HIR_ID, DUMMY_SP, Target::Mod, None); check_invalid_crate_level_attr(tcx, tcx.hir().krate_attrs()); diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs index 9e352fa5cc6..04d6e9f205a 100644 --- a/compiler/rustc_passes/src/check_const.rs +++ b/compiler/rustc_passes/src/check_const.rs @@ -57,89 +57,71 @@ impl NonConstExpr { fn check_mod_const_bodies(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { let mut vis = CheckConstVisitor::new(tcx); - tcx.hir().visit_item_likes_in_module(module_def_id, &mut vis.as_deep_visitor()); - tcx.hir().visit_item_likes_in_module(module_def_id, &mut CheckConstTraitVisitor::new(tcx)); + tcx.hir().deep_visit_item_likes_in_module(module_def_id, &mut vis); } pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { check_mod_const_bodies, ..*providers }; } -struct CheckConstTraitVisitor<'tcx> { - tcx: TyCtxt<'tcx>, -} - -impl<'tcx> CheckConstTraitVisitor<'tcx> { - fn new(tcx: TyCtxt<'tcx>) -> Self { - CheckConstTraitVisitor { tcx } - } -} - -impl<'tcx> hir::itemlikevisit::ItemLikeVisitor<'tcx> for CheckConstTraitVisitor<'tcx> { - /// check for const trait impls, and errors if the impl uses provided/default functions - /// of the trait being implemented; as those provided functions can be non-const. - fn visit_item<'hir>(&mut self, item: &'hir hir::Item<'hir>) { - let _: Option<_> = try { - if let hir::ItemKind::Impl(ref imp) = item.kind && let hir::Constness::Const = imp.constness { - let trait_def_id = imp.of_trait.as_ref()?.trait_def_id()?; - let ancestors = self - .tcx - .trait_def(trait_def_id) - .ancestors(self.tcx, item.def_id.to_def_id()) - .ok()?; - let mut to_implement = Vec::new(); - - for trait_item in self.tcx.associated_items(trait_def_id).in_definition_order() +fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) { + let _: Option<_> = try { + if let hir::ItemKind::Impl(ref imp) = item.kind && let hir::Constness::Const = imp.constness { + let trait_def_id = imp.of_trait.as_ref()?.trait_def_id()?; + let ancestors = tcx + .trait_def(trait_def_id) + .ancestors(tcx, item.def_id.to_def_id()) + .ok()?; + let mut to_implement = Vec::new(); + + for trait_item in tcx.associated_items(trait_def_id).in_definition_order() + { + if let ty::AssocItem { + kind: ty::AssocKind::Fn, + defaultness, + def_id: trait_item_id, + .. + } = *trait_item + { + // we can ignore functions that do not have default bodies: + // if those are unimplemented it will be caught by typeck. + if !defaultness.has_value() + || tcx + .has_attr(trait_item_id, sym::default_method_body_is_const) { - if let ty::AssocItem { - kind: ty::AssocKind::Fn, - defaultness, - def_id: trait_item_id, - .. - } = *trait_item - { - // we can ignore functions that do not have default bodies: - // if those are unimplemented it will be caught by typeck. - if !defaultness.has_value() - || self - .tcx - .has_attr(trait_item_id, sym::default_method_body_is_const) - { - continue; - } - - let is_implemented = ancestors - .leaf_def(self.tcx, trait_item_id) - .map(|node_item| !node_item.defining_node.is_from_trait()) - .unwrap_or(false); - - if !is_implemented { - to_implement.push(self.tcx.item_name(trait_item_id).to_string()); - } - } + continue; } - // all nonconst trait functions (not marked with #[default_method_body_is_const]) - // must be implemented - if !to_implement.is_empty() { - self.tcx - .sess - .struct_span_err( - item.span, - "const trait implementations may not use non-const default functions", - ) - .note(&format!("`{}` not implemented", to_implement.join("`, `"))) - .emit(); + let is_implemented = ancestors + .leaf_def(tcx, trait_item_id) + .map(|node_item| !node_item.defining_node.is_from_trait()) + .unwrap_or(false); + + if !is_implemented { + to_implement.push(trait_item_id); } + } } - }; - } - - fn visit_trait_item<'hir>(&mut self, _: &'hir hir::TraitItem<'hir>) {} - - fn visit_impl_item<'hir>(&mut self, _: &'hir hir::ImplItem<'hir>) {} - fn visit_foreign_item<'hir>(&mut self, _: &'hir hir::ForeignItem<'hir>) {} + // all nonconst trait functions (not marked with #[default_method_body_is_const]) + // must be implemented + if !to_implement.is_empty() { + let not_implemented = to_implement + .into_iter() + .map(|did| tcx.item_name(did).to_string()) + .collect::<Vec<_>>() + .join("`, `"); + tcx + .sess + .struct_span_err( + item.span, + "const trait implementations may not use non-const default functions", + ) + .note(&format!("`{}` not implemented", not_implemented)) + .emit(); + } + } + }; } #[derive(Copy, Clone)] @@ -170,7 +152,7 @@ impl<'tcx> CheckConstVisitor<'tcx> { // If `def_id` is `None`, we don't need to consider stability attributes. let def_id = match def_id { - Some(x) => x.to_def_id(), + Some(x) => x, None => return true, }; @@ -182,14 +164,16 @@ impl<'tcx> CheckConstVisitor<'tcx> { // If this crate is not using stability attributes, or this function is not claiming to be a // stable `const fn`, that is all that is required. - if !tcx.features().staged_api || tcx.has_attr(def_id, sym::rustc_const_unstable) { + if !tcx.features().staged_api + || tcx.has_attr(def_id.to_def_id(), sym::rustc_const_unstable) + { return true; } // However, we cannot allow stable `const fn`s to use unstable features without an explicit // opt-in via `rustc_allow_const_fn_unstable`. - attr::rustc_allow_const_fn_unstable(&tcx.sess, &tcx.get_attrs(def_id)) - .any(|name| name == feature_gate) + let attrs = tcx.hir().attrs(tcx.hir().local_def_id_to_hir_id(def_id)); + attr::rustc_allow_const_fn_unstable(&tcx.sess, attrs).any(|name| name == feature_gate) }; match required_gates { @@ -268,6 +252,11 @@ impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> { self.tcx.hir() } + fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { + intravisit::walk_item(self, item); + check_item(self.tcx, item); + } + fn visit_anon_const(&mut self, anon: &'tcx hir::AnonConst) { let kind = Some(hir::ConstContext::Const); self.recurse_into(kind, None, |this| intravisit::walk_anon_const(this, anon)); diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 991d0d45546..e78d9a59982 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -8,7 +8,6 @@ use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::{Node, PatKind, TyKind}; use rustc_middle::hir::nested_filter; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; @@ -452,21 +451,23 @@ fn has_allow_dead_code_or_lang_attr(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { } let def_id = tcx.hir().local_def_id(id); - let cg_attrs = tcx.codegen_fn_attrs(def_id); - - // #[used], #[no_mangle], #[export_name], etc also keeps the item alive - // forcefully, e.g., for placing it in a specific section. - if cg_attrs.contains_extern_indicator() - || cg_attrs.flags.contains(CodegenFnAttrFlags::USED) - || cg_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) - { - return true; + if tcx.def_kind(def_id).has_codegen_attrs() { + let cg_attrs = tcx.codegen_fn_attrs(def_id); + + // #[used], #[no_mangle], #[export_name], etc also keeps the item alive + // forcefully, e.g., for placing it in a specific section. + if cg_attrs.contains_extern_indicator() + || cg_attrs.flags.contains(CodegenFnAttrFlags::USED) + || cg_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) + { + return true; + } } tcx.lint_level_at_node(lint::builtin::DEAD_CODE, id).0 == lint::Allow } -// This visitor seeds items that +// These check_* functions seeds items that // 1) We want to explicitly consider as live: // * Item annotated with #[allow(dead_code)] // - This is done so that if we want to suppress warnings for a @@ -479,82 +480,95 @@ fn has_allow_dead_code_or_lang_attr(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { // or // 2) We are not sure to be live or not // * Implementations of traits and trait methods -struct LifeSeeder<'tcx> { - worklist: Vec<LocalDefId>, +fn check_item<'tcx>( tcx: TyCtxt<'tcx>, - // see `MarkSymbolVisitor::struct_constructors` - struct_constructors: FxHashMap<LocalDefId, LocalDefId>, -} - -impl<'v, 'tcx> ItemLikeVisitor<'v> for LifeSeeder<'tcx> { - fn visit_item(&mut self, item: &hir::Item<'_>) { - let allow_dead_code = has_allow_dead_code_or_lang_attr(self.tcx, item.hir_id()); - if allow_dead_code { - self.worklist.push(item.def_id); - } - match item.kind { - hir::ItemKind::Enum(ref enum_def, _) => { - let hir = self.tcx.hir(); + worklist: &mut Vec<LocalDefId>, + struct_constructors: &mut FxHashMap<LocalDefId, LocalDefId>, + id: hir::ItemId, +) { + let allow_dead_code = has_allow_dead_code_or_lang_attr(tcx, id.hir_id()); + if allow_dead_code { + worklist.push(id.def_id); + } + + match tcx.def_kind(id.def_id) { + DefKind::Enum => { + let item = tcx.hir().item(id); + if let hir::ItemKind::Enum(ref enum_def, _) = item.kind { + let hir = tcx.hir(); if allow_dead_code { - self.worklist.extend( + worklist.extend( enum_def.variants.iter().map(|variant| hir.local_def_id(variant.id)), ); } for variant in enum_def.variants { if let Some(ctor_hir_id) = variant.data.ctor_hir_id() { - self.struct_constructors + struct_constructors .insert(hir.local_def_id(ctor_hir_id), hir.local_def_id(variant.id)); } } } - hir::ItemKind::Impl(hir::Impl { ref of_trait, items, .. }) => { - if of_trait.is_some() { - self.worklist.push(item.def_id); - } - for impl_item_ref in *items { - let impl_item = self.tcx.hir().impl_item(impl_item_ref.id); - if of_trait.is_some() - || has_allow_dead_code_or_lang_attr(self.tcx, impl_item.hir_id()) - { - self.worklist.push(impl_item_ref.id.def_id); - } + } + DefKind::Impl => { + let of_trait = tcx.impl_trait_ref(id.def_id); + + if of_trait.is_some() { + worklist.push(id.def_id); + } + + // get DefIds from another query + let local_def_ids = tcx + .associated_item_def_ids(id.def_id) + .iter() + .filter_map(|def_id| def_id.as_local()); + + // And we access the Map here to get HirId from LocalDefId + for id in local_def_ids { + if of_trait.is_some() + || has_allow_dead_code_or_lang_attr(tcx, tcx.hir().local_def_id_to_hir_id(id)) + { + worklist.push(id); } } - hir::ItemKind::Struct(ref variant_data, _) => { + } + DefKind::Struct => { + let item = tcx.hir().item(id); + if let hir::ItemKind::Struct(ref variant_data, _) = item.kind { if let Some(ctor_hir_id) = variant_data.ctor_hir_id() { - self.struct_constructors - .insert(self.tcx.hir().local_def_id(ctor_hir_id), item.def_id); + struct_constructors.insert(tcx.hir().local_def_id(ctor_hir_id), item.def_id); } } - hir::ItemKind::GlobalAsm(_) => { - // global_asm! is always live. - self.worklist.push(item.def_id); - } - _ => (), } + DefKind::GlobalAsm => { + // global_asm! is always live. + worklist.push(id.def_id); + } + _ => {} } +} - fn visit_trait_item(&mut self, trait_item: &hir::TraitItem<'_>) { - use hir::TraitItemKind::{Const, Fn}; +fn check_trait_item<'tcx>(tcx: TyCtxt<'tcx>, worklist: &mut Vec<LocalDefId>, id: hir::TraitItemId) { + use hir::TraitItemKind::{Const, Fn}; + if matches!(tcx.def_kind(id.def_id), DefKind::AssocConst | DefKind::AssocFn) { + let trait_item = tcx.hir().trait_item(id); if matches!(trait_item.kind, Const(_, Some(_)) | Fn(_, hir::TraitFn::Provided(_))) - && has_allow_dead_code_or_lang_attr(self.tcx, trait_item.hir_id()) + && has_allow_dead_code_or_lang_attr(tcx, trait_item.hir_id()) { - self.worklist.push(trait_item.def_id); + worklist.push(trait_item.def_id); } } +} - fn visit_impl_item(&mut self, _item: &hir::ImplItem<'_>) { - // ignore: we are handling this in `visit_item` above - } - - fn visit_foreign_item(&mut self, foreign_item: &hir::ForeignItem<'_>) { - use hir::ForeignItemKind::{Fn, Static}; - if matches!(foreign_item.kind, Static(..) | Fn(..)) - && has_allow_dead_code_or_lang_attr(self.tcx, foreign_item.hir_id()) - { - self.worklist.push(foreign_item.def_id); - } +fn check_foreign_item<'tcx>( + tcx: TyCtxt<'tcx>, + worklist: &mut Vec<LocalDefId>, + id: hir::ForeignItemId, +) { + if matches!(tcx.def_kind(id.def_id), DefKind::Static(_) | DefKind::Fn) + && has_allow_dead_code_or_lang_attr(tcx, id.hir_id()) + { + worklist.push(id.def_id); } } @@ -562,7 +576,9 @@ fn create_and_seed_worklist<'tcx>( tcx: TyCtxt<'tcx>, ) -> (Vec<LocalDefId>, FxHashMap<LocalDefId, LocalDefId>) { let access_levels = &tcx.privacy_access_levels(()); - let worklist = access_levels + // see `MarkSymbolVisitor::struct_constructors` + let mut struct_constructors = Default::default(); + let mut worklist = access_levels .map .iter() .filter_map( @@ -574,11 +590,20 @@ fn create_and_seed_worklist<'tcx>( .chain(tcx.entry_fn(()).and_then(|(def_id, _)| def_id.as_local())) .collect::<Vec<_>>(); - // Seed implemented trait items - let mut life_seeder = LifeSeeder { worklist, tcx, struct_constructors: Default::default() }; - tcx.hir().visit_all_item_likes(&mut life_seeder); + let crate_items = tcx.hir_crate_items(()); + for id in crate_items.items() { + check_item(tcx, &mut worklist, &mut struct_constructors, id); + } + + for id in crate_items.trait_items() { + check_trait_item(tcx, &mut worklist, id); + } + + for id in crate_items.foreign_items() { + check_foreign_item(tcx, &mut worklist, id); + } - (life_seeder.worklist, life_seeder.struct_constructors) + (worklist, struct_constructors) } fn live_symbols_and_ignored_derived_traits<'tcx>( diff --git a/compiler/rustc_passes/src/debugger_visualizer.rs b/compiler/rustc_passes/src/debugger_visualizer.rs index f89092c57a3..8305830bc98 100644 --- a/compiler/rustc_passes/src/debugger_visualizer.rs +++ b/compiler/rustc_passes/src/debugger_visualizer.rs @@ -5,8 +5,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_expand::base::resolve_path; use rustc_hir as hir; use rustc_hir::def_id::CrateNum; -use rustc_hir::itemlikevisit::ItemLikeVisitor; -use rustc_hir::{HirId, Target}; +use rustc_hir::HirId; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::LOCAL_CRATE; @@ -14,96 +13,66 @@ use rustc_span::{sym, DebuggerVisualizerFile, DebuggerVisualizerType}; use std::sync::Arc; -struct DebuggerVisualizerCollector<'tcx> { - debugger_visualizers: FxHashSet<DebuggerVisualizerFile>, +fn check_for_debugger_visualizer<'tcx>( tcx: TyCtxt<'tcx>, -} - -impl<'v, 'tcx> ItemLikeVisitor<'v> for DebuggerVisualizerCollector<'tcx> { - fn visit_item(&mut self, item: &hir::Item<'_>) { - let target = Target::from_item(item); - match target { - Target::Mod => { - self.check_for_debugger_visualizer(item.hir_id()); - } - _ => {} - } - } - - fn visit_trait_item(&mut self, _: &hir::TraitItem<'_>) {} - - fn visit_impl_item(&mut self, _: &hir::ImplItem<'_>) {} - - fn visit_foreign_item(&mut self, _: &hir::ForeignItem<'_>) {} -} - -impl<'tcx> DebuggerVisualizerCollector<'tcx> { - fn new(tcx: TyCtxt<'tcx>) -> DebuggerVisualizerCollector<'tcx> { - DebuggerVisualizerCollector { tcx, debugger_visualizers: FxHashSet::default() } - } - - fn check_for_debugger_visualizer(&mut self, hir_id: HirId) { - let attrs = self.tcx.hir().attrs(hir_id); - for attr in attrs { - if attr.has_name(sym::debugger_visualizer) { - let list = match attr.meta_item_list() { - Some(list) => list, + hir_id: HirId, + debugger_visualizers: &mut FxHashSet<DebuggerVisualizerFile>, +) { + let attrs = tcx.hir().attrs(hir_id); + for attr in attrs { + if attr.has_name(sym::debugger_visualizer) { + let list = match attr.meta_item_list() { + Some(list) => list, + _ => continue, + }; + + let meta_item = match list.len() { + 1 => match list[0].meta_item() { + Some(meta_item) => meta_item, _ => continue, - }; - - let meta_item = match list.len() { - 1 => match list[0].meta_item() { - Some(meta_item) => meta_item, - _ => continue, - }, - _ => continue, - }; - - let file = match (meta_item.name_or_empty(), meta_item.value_str()) { - (sym::natvis_file, Some(value)) => { - match resolve_path(&self.tcx.sess.parse_sess, value.as_str(), attr.span) { - Ok(file) => file, - Err(mut err) => { - err.emit(); - continue; - } + }, + _ => continue, + }; + + let file = match (meta_item.name_or_empty(), meta_item.value_str()) { + (sym::natvis_file, Some(value)) => { + match resolve_path(&tcx.sess.parse_sess, value.as_str(), attr.span) { + Ok(file) => file, + Err(mut err) => { + err.emit(); + continue; } } - (_, _) => continue, + } + (_, _) => continue, + }; + + if file.is_file() { + let contents = match std::fs::read(&file) { + Ok(contents) => contents, + Err(err) => { + tcx.sess + .struct_span_err( + attr.span, + &format!( + "Unable to read contents of file `{}`. {}", + file.display(), + err + ), + ) + .emit(); + continue; + } }; - if file.is_file() { - let contents = match std::fs::read(&file) { - Ok(contents) => contents, - Err(err) => { - self.tcx - .sess - .struct_span_err( - attr.span, - &format!( - "Unable to read contents of file `{}`. {}", - file.display(), - err - ), - ) - .emit(); - continue; - } - }; - - self.debugger_visualizers.insert(DebuggerVisualizerFile::new( - Arc::from(contents), - DebuggerVisualizerType::Natvis, - )); - } else { - self.tcx - .sess - .struct_span_err( - attr.span, - &format!("{} is not a valid file", file.display()), - ) - .emit(); - } + debugger_visualizers.insert(DebuggerVisualizerFile::new( + Arc::from(contents), + DebuggerVisualizerType::Natvis, + )); + } else { + tcx.sess + .struct_span_err(attr.span, &format!("{} is not a valid file", file.display())) + .emit(); } } } @@ -114,17 +83,21 @@ fn debugger_visualizers<'tcx>(tcx: TyCtxt<'tcx>, cnum: CrateNum) -> Vec<Debugger assert_eq!(cnum, LOCAL_CRATE); // Initialize the collector. - let mut collector = DebuggerVisualizerCollector::new(tcx); + let mut debugger_visualizers = FxHashSet::default(); // Collect debugger visualizers in this crate. - tcx.hir().visit_all_item_likes(&mut collector); + tcx.hir().for_each_module(|id| { + check_for_debugger_visualizer( + tcx, + tcx.hir().local_def_id_to_hir_id(id), + &mut debugger_visualizers, + ) + }); // Collect debugger visualizers on the crate attributes. - collector.check_for_debugger_visualizer(CRATE_HIR_ID); + check_for_debugger_visualizer(tcx, CRATE_HIR_ID, &mut debugger_visualizers); // Extract out the found debugger_visualizer items. - let DebuggerVisualizerCollector { debugger_visualizers, .. } = collector; - let mut visualizers = debugger_visualizers.into_iter().collect::<Vec<_>>(); // Sort the visualizers so we always get a deterministic query result. diff --git a/compiler/rustc_passes/src/diagnostic_items.rs b/compiler/rustc_passes/src/diagnostic_items.rs index 9cbb7917e9a..e6b69d8986c 100644 --- a/compiler/rustc_passes/src/diagnostic_items.rs +++ b/compiler/rustc_passes/src/diagnostic_items.rs @@ -10,49 +10,22 @@ //! * Compiler internal types like `Ty` and `TyCtxt` use rustc_ast as ast; -use rustc_hir as hir; use rustc_hir::diagnostic_items::DiagnosticItems; -use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; use rustc_span::symbol::{sym, Symbol}; -struct DiagnosticItemCollector<'tcx> { +fn observe_item<'tcx>( tcx: TyCtxt<'tcx>, - diagnostic_items: DiagnosticItems, -} - -impl<'v, 'tcx> ItemLikeVisitor<'v> for DiagnosticItemCollector<'tcx> { - fn visit_item(&mut self, item: &hir::Item<'_>) { - self.observe_item(item.def_id); - } - - fn visit_trait_item(&mut self, trait_item: &hir::TraitItem<'_>) { - self.observe_item(trait_item.def_id); - } - - fn visit_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) { - self.observe_item(impl_item.def_id); - } - - fn visit_foreign_item(&mut self, foreign_item: &hir::ForeignItem<'_>) { - self.observe_item(foreign_item.def_id); - } -} - -impl<'tcx> DiagnosticItemCollector<'tcx> { - fn new(tcx: TyCtxt<'tcx>) -> DiagnosticItemCollector<'tcx> { - DiagnosticItemCollector { tcx, diagnostic_items: DiagnosticItems::default() } - } - - fn observe_item(&mut self, def_id: LocalDefId) { - let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); - let attrs = self.tcx.hir().attrs(hir_id); - if let Some(name) = extract(attrs) { - // insert into our table - collect_item(self.tcx, &mut self.diagnostic_items, name, def_id.to_def_id()); - } + diagnostic_items: &mut DiagnosticItems, + def_id: LocalDefId, +) { + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + let attrs = tcx.hir().attrs(hir_id); + if let Some(name) = extract(attrs) { + // insert into our table + collect_item(tcx, diagnostic_items, name, def_id.to_def_id()); } } @@ -83,7 +56,7 @@ fn collect_item(tcx: TyCtxt<'_>, items: &mut DiagnosticItems, name: Symbol, item } } -/// Extract the first `rustc_diagnostic_item = "$name"` out of a list of attributes.p +/// Extract the first `rustc_diagnostic_item = "$name"` out of a list of attributes. fn extract(attrs: &[ast::Attribute]) -> Option<Symbol> { attrs.iter().find_map(|attr| { if attr.has_name(sym::rustc_diagnostic_item) { attr.value_str() } else { None } @@ -95,12 +68,28 @@ fn diagnostic_items<'tcx>(tcx: TyCtxt<'tcx>, cnum: CrateNum) -> DiagnosticItems assert_eq!(cnum, LOCAL_CRATE); // Initialize the collector. - let mut collector = DiagnosticItemCollector::new(tcx); + let mut diagnostic_items = DiagnosticItems::default(); // Collect diagnostic items in this crate. - tcx.hir().visit_all_item_likes(&mut collector); + let crate_items = tcx.hir_crate_items(()); + + for id in crate_items.items() { + observe_item(tcx, &mut diagnostic_items, id.def_id); + } + + for id in crate_items.trait_items() { + observe_item(tcx, &mut diagnostic_items, id.def_id); + } + + for id in crate_items.impl_items() { + observe_item(tcx, &mut diagnostic_items, id.def_id); + } + + for id in crate_items.foreign_items() { + observe_item(tcx, &mut diagnostic_items, id.def_id); + } - collector.diagnostic_items + diagnostic_items } /// Traverse and collect all the diagnostic items in all crates. diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs index f84b848e08d..b90d44e2af5 100644 --- a/compiler/rustc_passes/src/entry.rs +++ b/compiler/rustc_passes/src/entry.rs @@ -1,8 +1,8 @@ use rustc_ast::entry::EntryPointType; use rustc_errors::struct_span_err; +use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; -use rustc_hir::itemlikevisit::ItemLikeVisitor; -use rustc_hir::{ForeignItem, ImplItem, Item, ItemKind, Node, TraitItem, CRATE_HIR_ID}; +use rustc_hir::{ItemId, Node, CRATE_HIR_ID}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{DefIdTree, TyCtxt}; use rustc_session::config::{CrateType, EntryFnType}; @@ -25,25 +25,6 @@ struct EntryContext<'tcx> { non_main_fns: Vec<Span>, } -impl<'tcx> ItemLikeVisitor<'tcx> for EntryContext<'tcx> { - fn visit_item(&mut self, item: &'tcx Item<'tcx>) { - let at_root = self.tcx.opt_local_parent(item.def_id) == Some(CRATE_DEF_ID); - find_item(item, self, at_root); - } - - fn visit_trait_item(&mut self, _trait_item: &'tcx TraitItem<'tcx>) { - // Entry fn is never a trait item. - } - - fn visit_impl_item(&mut self, _impl_item: &'tcx ImplItem<'tcx>) { - // Entry fn is never a trait item. - } - - fn visit_foreign_item(&mut self, _: &'tcx ForeignItem<'tcx>) { - // Entry fn is never a foreign item. - } -} - fn entry_fn(tcx: TyCtxt<'_>, (): ()) -> Option<(DefId, EntryFnType)> { let any_exe = tcx.sess.crate_types().iter().any(|ty| *ty == CrateType::Executable); if !any_exe { @@ -59,28 +40,35 @@ fn entry_fn(tcx: TyCtxt<'_>, (): ()) -> Option<(DefId, EntryFnType)> { let mut ctxt = EntryContext { tcx, attr_main_fn: None, start_fn: None, non_main_fns: Vec::new() }; - tcx.hir().visit_all_item_likes(&mut ctxt); + for id in tcx.hir().items() { + find_item(id, &mut ctxt); + } configure_main(tcx, &ctxt) } // Beware, this is duplicated in `librustc_builtin_macros/test_harness.rs` // (with `ast::Item`), so make sure to keep them in sync. -fn entry_point_type(ctxt: &EntryContext<'_>, item: &Item<'_>, at_root: bool) -> EntryPointType { - let attrs = ctxt.tcx.hir().attrs(item.hir_id()); +// A small optimization was added so that hir::Item is fetched only when needed. +// An equivalent optimization was not applied to the duplicated code in test_harness.rs. +fn entry_point_type(ctxt: &EntryContext<'_>, id: ItemId, at_root: bool) -> EntryPointType { + let attrs = ctxt.tcx.hir().attrs(id.hir_id()); if ctxt.tcx.sess.contains_name(attrs, sym::start) { EntryPointType::Start } else if ctxt.tcx.sess.contains_name(attrs, sym::rustc_main) { EntryPointType::MainAttr - } else if item.ident.name == sym::main { - if at_root { - // This is a top-level function so can be `main`. - EntryPointType::MainNamed + } else { + if let Some(name) = ctxt.tcx.opt_item_name(id.def_id.to_def_id()) + && name == sym::main { + if at_root { + // This is a top-level function so can be `main`. + EntryPointType::MainNamed + } else { + EntryPointType::OtherMain + } } else { - EntryPointType::OtherMain + EntryPointType::None } - } else { - EntryPointType::None } } @@ -89,11 +77,13 @@ fn throw_attr_err(sess: &Session, span: Span, attr: &str) { .emit(); } -fn find_item(item: &Item<'_>, ctxt: &mut EntryContext<'_>, at_root: bool) { - match entry_point_type(ctxt, item, at_root) { +fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) { + let at_root = ctxt.tcx.opt_local_parent(id.def_id) == Some(CRATE_DEF_ID); + + match entry_point_type(ctxt, id, at_root) { EntryPointType::None => (), - _ if !matches!(item.kind, ItemKind::Fn(..)) => { - let attrs = ctxt.tcx.hir().attrs(item.hir_id()); + _ if !matches!(ctxt.tcx.def_kind(id.def_id), DefKind::Fn) => { + let attrs = ctxt.tcx.hir().attrs(id.hir_id()); if let Some(attr) = ctxt.tcx.sess.find_by_name(attrs, sym::start) { throw_attr_err(&ctxt.tcx.sess, attr.span, "start"); } @@ -103,31 +93,39 @@ fn find_item(item: &Item<'_>, ctxt: &mut EntryContext<'_>, at_root: bool) { } EntryPointType::MainNamed => (), EntryPointType::OtherMain => { - ctxt.non_main_fns.push(item.span); + ctxt.non_main_fns.push(ctxt.tcx.def_span(id.def_id)); } EntryPointType::MainAttr => { if ctxt.attr_main_fn.is_none() { - ctxt.attr_main_fn = Some((item.def_id, item.span)); + ctxt.attr_main_fn = Some((id.def_id, ctxt.tcx.def_span(id.def_id.to_def_id()))); } else { struct_span_err!( ctxt.tcx.sess, - item.span, + ctxt.tcx.def_span(id.def_id.to_def_id()), E0137, "multiple functions with a `#[main]` attribute" ) - .span_label(item.span, "additional `#[main]` function") + .span_label( + ctxt.tcx.def_span(id.def_id.to_def_id()), + "additional `#[main]` function", + ) .span_label(ctxt.attr_main_fn.unwrap().1, "first `#[main]` function") .emit(); } } EntryPointType::Start => { if ctxt.start_fn.is_none() { - ctxt.start_fn = Some((item.def_id, item.span)); + ctxt.start_fn = Some((id.def_id, ctxt.tcx.def_span(id.def_id.to_def_id()))); } else { - struct_span_err!(ctxt.tcx.sess, item.span, E0138, "multiple `start` functions") - .span_label(ctxt.start_fn.unwrap().1, "previous `#[start]` function here") - .span_label(item.span, "multiple `start` functions") - .emit(); + struct_span_err!( + ctxt.tcx.sess, + ctxt.tcx.def_span(id.def_id.to_def_id()), + E0138, + "multiple `start` functions" + ) + .span_label(ctxt.start_fn.unwrap().1, "previous `#[start]` function here") + .span_label(ctxt.tcx.def_span(id.def_id.to_def_id()), "multiple `start` functions") + .emit(); } } } diff --git a/compiler/rustc_passes/src/hir_id_validator.rs b/compiler/rustc_passes/src/hir_id_validator.rs index 379a6827c8a..23ff0a91159 100644 --- a/compiler/rustc_passes/src/hir_id_validator.rs +++ b/compiler/rustc_passes/src/hir_id_validator.rs @@ -3,7 +3,6 @@ use rustc_data_structures::sync::Lock; use rustc_hir as hir; use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID}; use rustc_hir::intravisit; -use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::{HirId, ItemLocalId}; use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter; @@ -20,8 +19,14 @@ pub fn check_crate(tcx: TyCtxt<'_>) { let hir_map = tcx.hir(); hir_map.par_for_each_module(|module_id| { - hir_map - .visit_item_likes_in_module(module_id, &mut OuterVisitor { hir_map, errors: &errors }) + let mut v = HirIdValidator { + hir_map, + owner: None, + hir_ids_seen: Default::default(), + errors: &errors, + }; + + tcx.hir().deep_visit_item_likes_in_module(module_id, &mut v); }); let errors = errors.into_inner(); @@ -39,13 +44,8 @@ struct HirIdValidator<'a, 'hir> { errors: &'a Lock<Vec<String>>, } -struct OuterVisitor<'a, 'hir> { - hir_map: Map<'hir>, - errors: &'a Lock<Vec<String>>, -} - -impl<'a, 'hir> OuterVisitor<'a, 'hir> { - fn new_inner_visitor(&self, hir_map: Map<'hir>) -> HirIdValidator<'a, 'hir> { +impl<'a, 'hir> HirIdValidator<'a, 'hir> { + fn new_visitor(&self, hir_map: Map<'hir>) -> HirIdValidator<'a, 'hir> { HirIdValidator { hir_map, owner: None, @@ -53,31 +53,7 @@ impl<'a, 'hir> OuterVisitor<'a, 'hir> { errors: self.errors, } } -} - -impl<'a, 'hir> ItemLikeVisitor<'hir> for OuterVisitor<'a, 'hir> { - fn visit_item(&mut self, i: &'hir hir::Item<'hir>) { - let mut inner_visitor = self.new_inner_visitor(self.hir_map); - inner_visitor.check(i.def_id, |this| intravisit::walk_item(this, i)); - } - - fn visit_trait_item(&mut self, i: &'hir hir::TraitItem<'hir>) { - let mut inner_visitor = self.new_inner_visitor(self.hir_map); - inner_visitor.check(i.def_id, |this| intravisit::walk_trait_item(this, i)); - } - - fn visit_impl_item(&mut self, i: &'hir hir::ImplItem<'hir>) { - let mut inner_visitor = self.new_inner_visitor(self.hir_map); - inner_visitor.check(i.def_id, |this| intravisit::walk_impl_item(this, i)); - } - - fn visit_foreign_item(&mut self, i: &'hir hir::ForeignItem<'hir>) { - let mut inner_visitor = self.new_inner_visitor(self.hir_map); - inner_visitor.check(i.def_id, |this| intravisit::walk_foreign_item(this, i)); - } -} -impl<'a, 'hir> HirIdValidator<'a, 'hir> { #[cold] #[inline(never)] fn error(&self, f: impl FnOnce() -> String) { @@ -146,6 +122,11 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for HirIdValidator<'a, 'hir> { self.hir_map } + fn visit_item(&mut self, i: &'hir hir::Item<'hir>) { + let mut inner_visitor = self.new_visitor(self.hir_map); + inner_visitor.check(i.def_id, |this| intravisit::walk_item(this, i)); + } + fn visit_id(&mut self, hir_id: HirId) { let owner = self.owner.expect("no owner"); @@ -163,17 +144,18 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for HirIdValidator<'a, 'hir> { self.hir_ids_seen.insert(hir_id.local_id); } - fn visit_impl_item_ref(&mut self, _: &'hir hir::ImplItemRef) { - // Explicitly do nothing here. ImplItemRefs contain hir::Visibility - // values that actually belong to an ImplItem instead of the ItemKind::Impl - // we are currently in. So for those it's correct that they have a - // different owner. + fn visit_foreign_item(&mut self, i: &'hir hir::ForeignItem<'hir>) { + let mut inner_visitor = self.new_visitor(self.hir_map); + inner_visitor.check(i.def_id, |this| intravisit::walk_foreign_item(this, i)); + } + + fn visit_trait_item(&mut self, i: &'hir hir::TraitItem<'hir>) { + let mut inner_visitor = self.new_visitor(self.hir_map); + inner_visitor.check(i.def_id, |this| intravisit::walk_trait_item(this, i)); } - fn visit_foreign_item_ref(&mut self, _: &'hir hir::ForeignItemRef) { - // Explicitly do nothing here. ForeignItemRefs contain hir::Visibility - // values that actually belong to an ForeignItem instead of the ItemKind::ForeignMod - // we are currently in. So for those it's correct that they have a - // different owner. + fn visit_impl_item(&mut self, i: &'hir hir::ImplItem<'hir>) { + let mut inner_visitor = self.new_visitor(self.hir_map); + inner_visitor.check(i.def_id, |this| intravisit::walk_impl_item(this, i)); } } diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index 237a8abfabe..6a234294ed1 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -318,7 +318,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { ast_visit::walk_variant(self, v) } - fn visit_lifetime(&mut self, lifetime: &'v ast::Lifetime) { + fn visit_lifetime(&mut self, lifetime: &'v ast::Lifetime, _: ast_visit::LifetimeCtxt) { self.record("Lifetime", Id::None, lifetime); ast_visit::walk_lifetime(self, lifetime) } diff --git a/compiler/rustc_passes/src/intrinsicck.rs b/compiler/rustc_passes/src/intrinsicck.rs index 7028fc44126..9c840777baf 100644 --- a/compiler/rustc_passes/src/intrinsicck.rs +++ b/compiler/rustc_passes/src/intrinsicck.rs @@ -14,10 +14,9 @@ use rustc_session::lint; use rustc_span::{sym, Span, Symbol, DUMMY_SP}; use rustc_target::abi::{Pointer, VariantIdx}; use rustc_target::asm::{InlineAsmRegOrRegClass, InlineAsmType}; -use rustc_target::spec::abi::Abi::RustIntrinsic; fn check_mod_intrinsics(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { - tcx.hir().visit_item_likes_in_module(module_def_id, &mut ItemVisitor { tcx }.as_deep_visitor()); + tcx.hir().deep_visit_item_likes_in_module(module_def_id, &mut ItemVisitor { tcx }); } pub fn provide(providers: &mut Providers) { @@ -63,8 +62,7 @@ fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { impl<'tcx> ExprVisitor<'tcx> { fn def_id_is_transmute(&self, def_id: DefId) -> bool { - self.tcx.fn_sig(def_id).abi() == RustIntrinsic - && self.tcx.item_name(def_id) == sym::transmute + self.tcx.is_intrinsic(def_id) && self.tcx.item_name(def_id) == sym::transmute } fn check_transmute(&self, span: Span, from: Ty<'tcx>, to: Ty<'tcx>) { diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs index 18d9bdf8e17..79900a90aed 100644 --- a/compiler/rustc_passes/src/lang_items.rs +++ b/compiler/rustc_passes/src/lang_items.rs @@ -240,9 +240,9 @@ fn get_lang_items(tcx: TyCtxt<'_>, (): ()) -> LanguageItems { let crate_items = tcx.hir_crate_items(()); for id in crate_items.items() { - collector.check_for_lang(Target::from_def_kind(tcx.hir().def_kind(id.def_id)), id.hir_id()); + collector.check_for_lang(Target::from_def_kind(tcx.def_kind(id.def_id)), id.hir_id()); - if matches!(tcx.hir().def_kind(id.def_id), DefKind::Enum) { + if matches!(tcx.def_kind(id.def_id), DefKind::Enum) { let item = tcx.hir().item(id); if let hir::ItemKind::Enum(def, ..) = &item.kind { for variant in def.variants { diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs index 00e8eb5eb2b..fd03f657111 100644 --- a/compiler/rustc_passes/src/layout_test.rs +++ b/compiler/rustc_passes/src/layout_test.rs @@ -1,8 +1,6 @@ use rustc_ast::Attribute; -use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; -use rustc_hir::itemlikevisit::ItemLikeVisitor; -use rustc_hir::ItemKind; use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::{ParamEnv, Ty, TyCtxt}; use rustc_span::symbol::sym; @@ -12,99 +10,87 @@ use rustc_target::abi::{HasDataLayout, TargetDataLayout}; pub fn test_layout(tcx: TyCtxt<'_>) { if tcx.features().rustc_attrs { // if the `rustc_attrs` feature is not enabled, don't bother testing layout - tcx.hir().visit_all_item_likes(&mut LayoutTest { tcx }); - } -} - -struct LayoutTest<'tcx> { - tcx: TyCtxt<'tcx>, -} - -impl<'tcx> ItemLikeVisitor<'tcx> for LayoutTest<'tcx> { - fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { - match item.kind { - ItemKind::TyAlias(..) - | ItemKind::Enum(..) - | ItemKind::Struct(..) - | ItemKind::Union(..) => { - for attr in self.tcx.get_attrs(item.def_id.to_def_id()).iter() { - if attr.has_name(sym::rustc_layout) { - self.dump_layout_of(item.def_id, item, attr); - } + for id in tcx.hir().items() { + if matches!( + tcx.def_kind(id.def_id), + DefKind::TyAlias | DefKind::Enum | DefKind::Struct | DefKind::Union + ) { + for attr in tcx.get_attrs(id.def_id.to_def_id(), sym::rustc_layout) { + dump_layout_of(tcx, id.def_id, attr); } } - _ => {} } } - - fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem<'tcx>) {} - fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem<'tcx>) {} - fn visit_foreign_item(&mut self, _: &'tcx hir::ForeignItem<'tcx>) {} } -impl<'tcx> LayoutTest<'tcx> { - fn dump_layout_of(&self, item_def_id: LocalDefId, item: &hir::Item<'tcx>, attr: &Attribute) { - let tcx = self.tcx; - let param_env = self.tcx.param_env(item_def_id); - let ty = self.tcx.type_of(item_def_id); - match self.tcx.layout_of(param_env.and(ty)) { - Ok(ty_layout) => { - // Check out the `#[rustc_layout(..)]` attribute to tell what to dump. - // The `..` are the names of fields to dump. - let meta_items = attr.meta_item_list().unwrap_or_default(); - for meta_item in meta_items { - match meta_item.name_or_empty() { - sym::abi => { - self.tcx.sess.span_err(item.span, &format!("abi: {:?}", ty_layout.abi)); - } +fn dump_layout_of<'tcx>(tcx: TyCtxt<'tcx>, item_def_id: LocalDefId, attr: &Attribute) { + let tcx = tcx; + let param_env = tcx.param_env(item_def_id); + let ty = tcx.type_of(item_def_id); + match tcx.layout_of(param_env.and(ty)) { + Ok(ty_layout) => { + // Check out the `#[rustc_layout(..)]` attribute to tell what to dump. + // The `..` are the names of fields to dump. + let meta_items = attr.meta_item_list().unwrap_or_default(); + for meta_item in meta_items { + match meta_item.name_or_empty() { + sym::abi => { + tcx.sess.span_err( + tcx.def_span(item_def_id.to_def_id()), + &format!("abi: {:?}", ty_layout.abi), + ); + } - sym::align => { - self.tcx - .sess - .span_err(item.span, &format!("align: {:?}", ty_layout.align)); - } + sym::align => { + tcx.sess.span_err( + tcx.def_span(item_def_id.to_def_id()), + &format!("align: {:?}", ty_layout.align), + ); + } - sym::size => { - self.tcx - .sess - .span_err(item.span, &format!("size: {:?}", ty_layout.size)); - } + sym::size => { + tcx.sess.span_err( + tcx.def_span(item_def_id.to_def_id()), + &format!("size: {:?}", ty_layout.size), + ); + } - sym::homogeneous_aggregate => { - self.tcx.sess.span_err( - item.span, - &format!( - "homogeneous_aggregate: {:?}", - ty_layout - .homogeneous_aggregate(&UnwrapLayoutCx { tcx, param_env }), - ), - ); - } + sym::homogeneous_aggregate => { + tcx.sess.span_err( + tcx.def_span(item_def_id.to_def_id()), + &format!( + "homogeneous_aggregate: {:?}", + ty_layout.homogeneous_aggregate(&UnwrapLayoutCx { tcx, param_env }), + ), + ); + } - sym::debug => { - let normalized_ty = self.tcx.normalize_erasing_regions( - param_env.with_reveal_all_normalized(self.tcx), - ty, - ); - self.tcx.sess.span_err( - item.span, - &format!("layout_of({:?}) = {:#?}", normalized_ty, *ty_layout), - ); - } + sym::debug => { + let normalized_ty = tcx.normalize_erasing_regions( + param_env.with_reveal_all_normalized(tcx), + ty, + ); + tcx.sess.span_err( + tcx.def_span(item_def_id.to_def_id()), + &format!("layout_of({:?}) = {:#?}", normalized_ty, *ty_layout), + ); + } - name => { - self.tcx.sess.span_err( - meta_item.span(), - &format!("unrecognized field name `{}`", name), - ); - } + name => { + tcx.sess.span_err( + meta_item.span(), + &format!("unrecognized field name `{}`", name), + ); } } } + } - Err(layout_error) => { - self.tcx.sess.span_err(item.span, &format!("layout error: {:?}", layout_error)); - } + Err(layout_error) => { + tcx.sess.span_err( + tcx.def_span(item_def_id.to_def_id()), + &format!("layout error: {:?}", layout_error), + ); } } } diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index d9d08488d28..510280ee386 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -6,7 +6,6 @@ #![allow(rustc::potential_query_instability)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![feature(crate_visibility_modifier)] #![feature(iter_intersperse)] #![feature(let_else)] #![feature(let_chains)] @@ -39,7 +38,6 @@ mod liveness; pub mod loops; mod naked_functions; mod reachable; -mod region; pub mod stability; mod upvars; mod weak_lang_items; @@ -58,7 +56,6 @@ pub fn provide(providers: &mut Providers) { liveness::provide(providers); intrinsicck::provide(providers); reachable::provide(providers); - region::provide(providers); stability::provide(providers); upvars::provide(providers); } diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 9eba7fb0811..b09d9831d43 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -140,7 +140,7 @@ fn live_node_kind_to_string(lnk: LiveNodeKind, tcx: TyCtxt<'_>) -> String { } fn check_mod_liveness(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { - tcx.hir().visit_item_likes_in_module(module_def_id, &mut IrMaps::new(tcx).as_deep_visitor()); + tcx.hir().deep_visit_item_likes_in_module(module_def_id, &mut IrMaps::new(tcx)); } pub fn provide(providers: &mut Providers) { @@ -373,8 +373,8 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) { self.add_from_pat(&arm.pat); - if let Some(hir::Guard::IfLet(ref pat, _)) = arm.guard { - self.add_from_pat(pat); + if let Some(hir::Guard::IfLet(ref let_expr)) = arm.guard { + self.add_from_pat(let_expr.pat); } intravisit::walk_arm(self, arm); } @@ -914,9 +914,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { let guard_succ = arm.guard.as_ref().map_or(body_succ, |g| match g { hir::Guard::If(e) => self.propagate_through_expr(e, body_succ), - hir::Guard::IfLet(pat, e) => { - let let_bind = self.define_bindings_in_pat(pat, body_succ); - self.propagate_through_expr(e, let_bind) + hir::Guard::IfLet(let_expr) => { + let let_bind = self.define_bindings_in_pat(let_expr.pat, body_succ); + self.propagate_through_expr(let_expr.init, let_bind) } }); let arm_succ = self.define_bindings_in_pat(&arm.pat, guard_succ); diff --git a/compiler/rustc_passes/src/loops.rs b/compiler/rustc_passes/src/loops.rs index 02b09daf0a4..e0dac09870d 100644 --- a/compiler/rustc_passes/src/loops.rs +++ b/compiler/rustc_passes/src/loops.rs @@ -31,9 +31,9 @@ struct CheckLoopVisitor<'a, 'hir> { } fn check_mod_loops(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { - tcx.hir().visit_item_likes_in_module( + tcx.hir().deep_visit_item_likes_in_module( module_def_id, - &mut CheckLoopVisitor { sess: &tcx.sess, hir_map: tcx.hir(), cx: Normal }.as_deep_visitor(), + &mut CheckLoopVisitor { sess: &tcx.sess, hir_map: tcx.hir(), cx: Normal }, ); } diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs index e85720952da..83f728d4076 100644 --- a/compiler/rustc_passes/src/naked_functions.rs +++ b/compiler/rustc_passes/src/naked_functions.rs @@ -14,13 +14,10 @@ use rustc_span::Span; use rustc_target::spec::abi::Abi; fn check_mod_naked_functions(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { - tcx.hir().visit_item_likes_in_module( - module_def_id, - &mut CheckNakedFunctions { tcx }.as_deep_visitor(), - ); + tcx.hir().deep_visit_item_likes_in_module(module_def_id, &mut CheckNakedFunctions { tcx }); } -crate fn provide(providers: &mut Providers) { +pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { check_mod_naked_functions, ..*providers }; } diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index b603352a9be..0ded6a421f5 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -10,7 +10,6 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::Node; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::middle::privacy; @@ -208,7 +207,11 @@ impl<'tcx> ReachableContext<'tcx> { } else { false }; - let codegen_attrs = self.tcx.codegen_fn_attrs(search_item); + let codegen_attrs = if self.tcx.def_kind(search_item).has_codegen_attrs() { + self.tcx.codegen_fn_attrs(search_item) + } else { + CodegenFnAttrs::EMPTY + }; let is_extern = codegen_attrs.contains_extern_indicator(); let std_internal = codegen_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL); @@ -310,77 +313,60 @@ impl<'tcx> ReachableContext<'tcx> { } } -// Some methods from non-exported (completely private) trait impls still have to be -// reachable if they are called from inlinable code. Generally, it's not known until -// monomorphization if a specific trait impl item can be reachable or not. So, we -// conservatively mark all of them as reachable. -// FIXME: One possible strategy for pruning the reachable set is to avoid marking impl -// items of non-exported traits (or maybe all local traits?) unless their respective -// trait items are used from inlinable code through method call syntax or UFCS, or their -// trait is a lang item. -struct CollectPrivateImplItemsVisitor<'a, 'tcx> { +fn check_item<'tcx>( tcx: TyCtxt<'tcx>, - access_levels: &'a privacy::AccessLevels, - worklist: &'a mut Vec<LocalDefId>, -} + id: hir::ItemId, + worklist: &mut Vec<LocalDefId>, + access_levels: &privacy::AccessLevels, +) { + if has_custom_linkage(tcx, id.def_id) { + worklist.push(id.def_id); + } -impl CollectPrivateImplItemsVisitor<'_, '_> { - fn push_to_worklist_if_has_custom_linkage(&mut self, def_id: LocalDefId) { - // Anything which has custom linkage gets thrown on the worklist no - // matter where it is in the crate, along with "special std symbols" - // which are currently akin to allocator symbols. - let codegen_attrs = self.tcx.codegen_fn_attrs(def_id); - if codegen_attrs.contains_extern_indicator() - || codegen_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) - // FIXME(nbdd0121): `#[used]` are marked as reachable here so it's picked up by - // `linked_symbols` in cg_ssa. They won't be exported in binary or cdylib due to their - // `SymbolExportLevel::Rust` export level but may end up being exported in dylibs. - || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED) - || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) - { - self.worklist.push(def_id); - } + if !matches!(tcx.def_kind(id.def_id), DefKind::Impl) { + return; } -} -impl<'a, 'tcx> ItemLikeVisitor<'tcx> for CollectPrivateImplItemsVisitor<'a, 'tcx> { - fn visit_item(&mut self, item: &hir::Item<'_>) { - self.push_to_worklist_if_has_custom_linkage(item.def_id); - - // We need only trait impls here, not inherent impls, and only non-exported ones - if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(ref trait_ref), ref items, .. }) = - item.kind - { - if !self.access_levels.is_reachable(item.def_id) { - // FIXME(#53488) remove `let` - let tcx = self.tcx; - self.worklist.extend(items.iter().map(|ii_ref| ii_ref.id.def_id)); - - let Res::Def(DefKind::Trait, trait_def_id) = trait_ref.path.res else { - unreachable!(); - }; + // We need only trait impls here, not inherent impls, and only non-exported ones + let item = tcx.hir().item(id); + if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(ref trait_ref), ref items, .. }) = + item.kind + { + if !access_levels.is_reachable(item.def_id) { + // FIXME(#53488) remove `let` + let tcx = tcx; + worklist.extend(items.iter().map(|ii_ref| ii_ref.id.def_id)); - if !trait_def_id.is_local() { - return; - } + let Res::Def(DefKind::Trait, trait_def_id) = trait_ref.path.res else { + unreachable!(); + }; - self.worklist.extend( - tcx.provided_trait_methods(trait_def_id) - .map(|assoc| assoc.def_id.expect_local()), - ); + if !trait_def_id.is_local() { + return; } - } - } - - fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'_>) {} - fn visit_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) { - self.push_to_worklist_if_has_custom_linkage(impl_item.def_id); + worklist.extend( + tcx.provided_trait_methods(trait_def_id).map(|assoc| assoc.def_id.expect_local()), + ); + } } +} - fn visit_foreign_item(&mut self, _foreign_item: &hir::ForeignItem<'_>) { - // We never export foreign functions as they have no body to export. +fn has_custom_linkage<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> bool { + // Anything which has custom linkage gets thrown on the worklist no + // matter where it is in the crate, along with "special std symbols" + // which are currently akin to allocator symbols. + if !tcx.def_kind(def_id).has_codegen_attrs() { + return false; } + let codegen_attrs = tcx.codegen_fn_attrs(def_id); + codegen_attrs.contains_extern_indicator() + || codegen_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) + // FIXME(nbdd0121): `#[used]` are marked as reachable here so it's picked up by + // `linked_symbols` in cg_ssa. They won't be exported in binary or cdylib due to their + // `SymbolExportLevel::Rust` export level but may end up being exported in dylibs. + || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED) + || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) } fn reachable_set<'tcx>(tcx: TyCtxt<'tcx>, (): ()) -> FxHashSet<LocalDefId> { @@ -412,12 +398,25 @@ fn reachable_set<'tcx>(tcx: TyCtxt<'tcx>, (): ()) -> FxHashSet<LocalDefId> { } } { - let mut collect_private_impl_items = CollectPrivateImplItemsVisitor { - tcx, - access_levels, - worklist: &mut reachable_context.worklist, - }; - tcx.hir().visit_all_item_likes(&mut collect_private_impl_items); + // Some methods from non-exported (completely private) trait impls still have to be + // reachable if they are called from inlinable code. Generally, it's not known until + // monomorphization if a specific trait impl item can be reachable or not. So, we + // conservatively mark all of them as reachable. + // FIXME: One possible strategy for pruning the reachable set is to avoid marking impl + // items of non-exported traits (or maybe all local traits?) unless their respective + // trait items are used from inlinable code through method call syntax or UFCS, or their + // trait is a lang item. + let crate_items = tcx.hir_crate_items(()); + + for id in crate_items.items() { + check_item(tcx, id, &mut reachable_context.worklist, access_levels); + } + + for id in crate_items.impl_items() { + if has_custom_linkage(tcx, id.def_id) { + reachable_context.worklist.push(id.def_id); + } + } } // Step 2: Mark all symbols that the symbols on the worklist touch. diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 10dc587be6e..70cb1f2a281 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -110,7 +110,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { ) where F: FnOnce(&mut Self), { - let attrs = self.tcx.get_attrs(def_id.to_def_id()); + let attrs = self.tcx.hir().attrs(self.tcx.hir().local_def_id_to_hir_id(def_id)); debug!("annotate(id = {:?}, attrs = {:?})", def_id, attrs); let depr = attr::find_deprecation(&self.tcx.sess, attrs); @@ -147,7 +147,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { // Propagate unstability. This can happen even for non-staged-api crates in case // -Zforce-unstable-if-unmarked is set. if let Some(stab) = self.parent_stab { - if inherit_deprecation.yes() && stab.level.is_unstable() { + if inherit_deprecation.yes() && stab.is_unstable() { self.index.stab_map.insert(def_id, stab); } } @@ -190,7 +190,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { if const_stab.is_none() { debug!("annotate: const_stab not found, parent = {:?}", self.parent_const_stab); if let Some(parent) = self.parent_const_stab { - if parent.level.is_unstable() { + if parent.is_const_unstable() { self.index.const_stab_map.insert(def_id, parent); } } @@ -272,9 +272,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { if stab.is_none() { debug!("annotate: stab not found, parent = {:?}", self.parent_stab); if let Some(stab) = self.parent_stab { - if inherit_deprecation.yes() && stab.level.is_unstable() - || inherit_from_parent.yes() - { + if inherit_deprecation.yes() && stab.is_unstable() || inherit_from_parent.yes() { self.index.stab_map.insert(def_id, stab); } } @@ -532,7 +530,8 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> { return; } - let is_const = self.tcx.is_const_fn(def_id.to_def_id()); + let is_const = self.tcx.is_const_fn(def_id.to_def_id()) + || self.tcx.is_const_trait_impl_raw(def_id.to_def_id()); let is_stable = self .tcx .lookup_stability(def_id) @@ -661,7 +660,7 @@ fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index { /// Cross-references the feature names of unstable APIs with enabled /// features and possibly prints errors. fn check_mod_unstable_api_usage(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { - tcx.hir().visit_item_likes_in_module(module_def_id, &mut Checker { tcx }.as_deep_visitor()); + tcx.hir().deep_visit_item_likes_in_module(module_def_id, &mut Checker { tcx }); } pub(crate) fn provide(providers: &mut Providers) { @@ -710,16 +709,23 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { // For implementations of traits, check the stability of each item // individually as it's possible to have a stable trait with unstable // items. - hir::ItemKind::Impl(hir::Impl { of_trait: Some(ref t), self_ty, items, .. }) => { - if self.tcx.features().staged_api { + hir::ItemKind::Impl(hir::Impl { + of_trait: Some(ref t), + self_ty, + items, + constness, + .. + }) => { + let features = self.tcx.features(); + if features.staged_api { + let attrs = self.tcx.hir().attrs(item.hir_id()); + let (stab, const_stab) = attr::find_stability(&self.tcx.sess, attrs, item.span); + // If this impl block has an #[unstable] attribute, give an // error if all involved types and traits are stable, because // it will have no effect. // See: https://github.com/rust-lang/rust/issues/55436 - let attrs = self.tcx.hir().attrs(item.hir_id()); - if let (Some((Stability { level: attr::Unstable { .. }, .. }, span)), _) = - attr::find_stability(&self.tcx.sess, attrs, item.span) - { + if let Some((Stability { level: attr::Unstable { .. }, .. }, span)) = stab { let mut c = CheckTraitImplStable { tcx: self.tcx, fully_stable: true }; c.visit_ty(self_ty); c.visit_trait_ref(t); @@ -735,6 +741,19 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { ); } } + + // `#![feature(const_trait_impl)]` is unstable, so any impl declared stable + // needs to have an error emitted. + if features.const_trait_impl + && *constness == hir::Constness::Const + && const_stab.map_or(false, |(stab, _)| stab.is_const_stable()) + { + self.tcx + .sess + .struct_span_err(item.span, "trait implementations cannot be const stable yet") + .note("see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information") + .emit(); + } } for impl_item_ref in *items { @@ -837,7 +856,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { let mut missing = MissingStabilityAnnotations { tcx, access_levels }; missing.check_missing_stability(CRATE_DEF_ID, tcx.hir().span(CRATE_HIR_ID)); tcx.hir().walk_toplevel_module(&mut missing); - tcx.hir().visit_all_item_likes(&mut missing.as_deep_visitor()); + tcx.hir().deep_visit_all_item_likes(&mut missing); } let declared_lang_features = &tcx.features().declared_lang_features; diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index ee459d9c129..e6c7b4064fb 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -14,8 +14,8 @@ use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet, CRATE_DEF_ID}; -use rustc_hir::intravisit::{self, DeepVisitor, Visitor}; -use rustc_hir::{AssocItemKind, HirIdSet, Node, PatKind}; +use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::{AssocItemKind, HirIdSet, ItemId, Node, PatKind}; use rustc_middle::bug; use rustc_middle::hir::nested_filter; use rustc_middle::middle::privacy::{AccessLevel, AccessLevels}; @@ -775,7 +775,14 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { } // Corner case: if the variant is reachable, but its // enum is not, make the enum reachable as well. - self.update(item.def_id, variant_level); + self.reach(item.def_id, variant_level).ty(); + } + if let Some(hir_id) = variant.data.ctor_hir_id() { + let ctor_def_id = self.tcx.hir().local_def_id(hir_id); + let ctor_level = self.get(ctor_def_id); + if ctor_level.is_some() { + self.reach(item.def_id, ctor_level).ty(); + } } } } @@ -803,6 +810,13 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { } } } + if let Some(hir_id) = struct_def.ctor_hir_id() { + let ctor_def_id = self.tcx.hir().local_def_id(hir_id); + let ctor_level = self.get(ctor_def_id); + if ctor_level.is_some() { + self.reach(item.def_id, ctor_level).ty(); + } + } } } @@ -1802,12 +1816,12 @@ impl<'tcx> DefIdVisitor<'tcx> for SearchInterfaceForPrivateItemsVisitor<'tcx> { } } -struct PrivateItemsInPublicInterfacesVisitor<'tcx> { +struct PrivateItemsInPublicInterfacesChecker<'tcx> { tcx: TyCtxt<'tcx>, old_error_set_ancestry: LocalDefIdSet, } -impl<'tcx> PrivateItemsInPublicInterfacesVisitor<'tcx> { +impl<'tcx> PrivateItemsInPublicInterfacesChecker<'tcx> { fn check( &self, def_id: LocalDefId, @@ -1841,110 +1855,110 @@ impl<'tcx> PrivateItemsInPublicInterfacesVisitor<'tcx> { check.ty(); } } -} - -impl<'tcx> Visitor<'tcx> for PrivateItemsInPublicInterfacesVisitor<'tcx> { - type NestedFilter = nested_filter::OnlyBodies; - fn nested_visit_map(&mut self) -> Self::Map { - self.tcx.hir() - } - - fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { + pub fn check_item(&mut self, id: ItemId) { let tcx = self.tcx; - let item_visibility = tcx.visibility(item.def_id); + let item_visibility = tcx.visibility(id.def_id); + let def_kind = tcx.def_kind(id.def_id); - match item.kind { - // Crates are always public. - hir::ItemKind::ExternCrate(..) => {} - // All nested items are checked by `visit_item`. - hir::ItemKind::Mod(..) => {} - // Checked in resolve. - hir::ItemKind::Use(..) => {} - // No subitems. - hir::ItemKind::Macro(..) | hir::ItemKind::GlobalAsm(..) => {} - // Subitems of these items have inherited publicity. - hir::ItemKind::Const(..) - | hir::ItemKind::Static(..) - | hir::ItemKind::Fn(..) - | hir::ItemKind::TyAlias(..) => { - self.check(item.def_id, item_visibility).generics().predicates().ty(); + match def_kind { + DefKind::Const | DefKind::Static(_) | DefKind::Fn | DefKind::TyAlias => { + self.check(id.def_id, item_visibility).generics().predicates().ty(); } - hir::ItemKind::OpaqueTy(..) => { + DefKind::OpaqueTy => { // `ty()` for opaque types is the underlying type, // it's not a part of interface, so we skip it. - self.check(item.def_id, item_visibility).generics().bounds(); + self.check(id.def_id, item_visibility).generics().bounds(); } - hir::ItemKind::Trait(.., trait_item_refs) => { - self.check(item.def_id, item_visibility).generics().predicates(); + DefKind::Trait => { + let item = tcx.hir().item(id); + if let hir::ItemKind::Trait(.., trait_item_refs) = item.kind { + self.check(item.def_id, item_visibility).generics().predicates(); - for trait_item_ref in trait_item_refs { - self.check_assoc_item( - trait_item_ref.id.def_id, - trait_item_ref.kind, - trait_item_ref.defaultness, - item_visibility, - ); - - if let AssocItemKind::Type = trait_item_ref.kind { - self.check(trait_item_ref.id.def_id, item_visibility).bounds(); + for trait_item_ref in trait_item_refs { + self.check_assoc_item( + trait_item_ref.id.def_id, + trait_item_ref.kind, + trait_item_ref.defaultness, + item_visibility, + ); + + if let AssocItemKind::Type = trait_item_ref.kind { + self.check(trait_item_ref.id.def_id, item_visibility).bounds(); + } } } } - hir::ItemKind::TraitAlias(..) => { - self.check(item.def_id, item_visibility).generics().predicates(); + DefKind::TraitAlias => { + self.check(id.def_id, item_visibility).generics().predicates(); } - hir::ItemKind::Enum(ref def, _) => { - self.check(item.def_id, item_visibility).generics().predicates(); + DefKind::Enum => { + let item = tcx.hir().item(id); + if let hir::ItemKind::Enum(ref def, _) = item.kind { + self.check(item.def_id, item_visibility).generics().predicates(); - for variant in def.variants { - for field in variant.data.fields() { - self.check(self.tcx.hir().local_def_id(field.hir_id), item_visibility).ty(); + for variant in def.variants { + for field in variant.data.fields() { + self.check(self.tcx.hir().local_def_id(field.hir_id), item_visibility) + .ty(); + } } } } // Subitems of foreign modules have their own publicity. - hir::ItemKind::ForeignMod { items, .. } => { - for foreign_item in items { - let vis = tcx.visibility(foreign_item.id.def_id); - self.check(foreign_item.id.def_id, vis).generics().predicates().ty(); + DefKind::ForeignMod => { + let item = tcx.hir().item(id); + if let hir::ItemKind::ForeignMod { items, .. } = item.kind { + for foreign_item in items { + let vis = tcx.visibility(foreign_item.id.def_id); + self.check(foreign_item.id.def_id, vis).generics().predicates().ty(); + } } } // Subitems of structs and unions have their own publicity. - hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => { - self.check(item.def_id, item_visibility).generics().predicates(); + DefKind::Struct | DefKind::Union => { + let item = tcx.hir().item(id); + if let hir::ItemKind::Struct(ref struct_def, _) + | hir::ItemKind::Union(ref struct_def, _) = item.kind + { + self.check(item.def_id, item_visibility).generics().predicates(); - for field in struct_def.fields() { - let def_id = tcx.hir().local_def_id(field.hir_id); - let field_visibility = tcx.visibility(def_id); - self.check(def_id, min(item_visibility, field_visibility, tcx)).ty(); + for field in struct_def.fields() { + let def_id = tcx.hir().local_def_id(field.hir_id); + let field_visibility = tcx.visibility(def_id); + self.check(def_id, min(item_visibility, field_visibility, tcx)).ty(); + } } } // An inherent impl is public when its type is public // Subitems of inherent impls have their own publicity. // A trait impl is public when both its type and its trait are public // Subitems of trait impls have inherited publicity. - hir::ItemKind::Impl(ref impl_) => { - let impl_vis = ty::Visibility::of_impl(item.def_id, tcx, &Default::default()); - // check that private components do not appear in the generics or predicates of inherent impls - // this check is intentionally NOT performed for impls of traits, per #90586 - if impl_.of_trait.is_none() { - self.check(item.def_id, impl_vis).generics().predicates(); - } - for impl_item_ref in impl_.items { - let impl_item_vis = if impl_.of_trait.is_none() { - min(tcx.visibility(impl_item_ref.id.def_id), impl_vis, tcx) - } else { - impl_vis - }; - self.check_assoc_item( - impl_item_ref.id.def_id, - impl_item_ref.kind, - impl_item_ref.defaultness, - impl_item_vis, - ); + DefKind::Impl => { + let item = tcx.hir().item(id); + if let hir::ItemKind::Impl(ref impl_) = item.kind { + let impl_vis = ty::Visibility::of_impl(item.def_id, tcx, &Default::default()); + // check that private components do not appear in the generics or predicates of inherent impls + // this check is intentionally NOT performed for impls of traits, per #90586 + if impl_.of_trait.is_none() { + self.check(item.def_id, impl_vis).generics().predicates(); + } + for impl_item_ref in impl_.items { + let impl_item_vis = if impl_.of_trait.is_none() { + min(tcx.visibility(impl_item_ref.id.def_id), impl_vis, tcx) + } else { + impl_vis + }; + self.check_assoc_item( + impl_item_ref.id.def_id, + impl_item_ref.kind, + impl_item_ref.defaultness, + impl_item_vis, + ); + } } } + _ => {} } } } @@ -2069,7 +2083,7 @@ fn check_private_in_public(tcx: TyCtxt<'_>, (): ()) { } // Check for private types and traits in public interfaces. - let mut visitor = PrivateItemsInPublicInterfacesVisitor { + let mut checker = PrivateItemsInPublicInterfacesChecker { tcx, // Only definition IDs are ever searched in `old_error_set_ancestry`, // so we can filter away all non-definition IDs at this point. @@ -2078,5 +2092,8 @@ fn check_private_in_public(tcx: TyCtxt<'_>, (): ()) { .filter_map(|hir_id| tcx.hir().opt_local_def_id(hir_id)) .collect(), }; - tcx.hir().visit_all_item_likes(&mut DeepVisitor::new(&mut visitor)); + + for id in tcx.hir().items() { + checker.check_item(id); + } } diff --git a/compiler/rustc_query_impl/src/keys.rs b/compiler/rustc_query_impl/src/keys.rs index 3f0f856b5dd..6fbafeb1d32 100644 --- a/compiler/rustc_query_impl/src/keys.rs +++ b/compiler/rustc_query_impl/src/keys.rs @@ -435,6 +435,16 @@ impl Key for Symbol { } } +impl Key for Option<Symbol> { + #[inline(always)] + fn query_crate_is_local(&self) -> bool { + true + } + fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { + DUMMY_SP + } +} + /// Canonical query goals correspond to abstract trait operations that /// are not tied to any crate in particular. impl<'tcx, T> Key for Canonical<'tcx, T> { diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index 6ebff5388f4..bfc51dedbc7 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -1,7 +1,6 @@ //! Support for serializing the dep-graph and reloading it. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![feature(crate_visibility_modifier)] #![feature(nll)] #![feature(min_specialization)] #![feature(once_cell)] @@ -21,7 +20,7 @@ use rustc_middle::dep_graph::{self, DepKindStruct, SerializedDepNodeIndex}; use rustc_middle::ty::query::{query_keys, query_storage, query_stored, query_values}; use rustc_middle::ty::query::{ExternProviders, Providers, QueryEngine}; use rustc_middle::ty::{self, TyCtxt}; -use rustc_span::def_id::LocalDefId; +use rustc_span::def_id::{LocalDefId, LOCAL_CRATE}; use rustc_span::Span; #[macro_use] diff --git a/compiler/rustc_query_impl/src/on_disk_cache.rs b/compiler/rustc_query_impl/src/on_disk_cache.rs index f2f895367ff..c4920408527 100644 --- a/compiler/rustc_query_impl/src/on_disk_cache.rs +++ b/compiler/rustc_query_impl/src/on_disk_cache.rs @@ -788,10 +788,24 @@ impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx [rustc_ast::InlineAsm } } -impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx [Span] { - fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { - RefDecodable::decode(d) - } +macro_rules! impl_ref_decoder { + (<$tcx:tt> $($ty:ty,)*) => { + $(impl<'a, $tcx> Decodable<CacheDecoder<'a, $tcx>> for &$tcx [$ty] { + fn decode(d: &mut CacheDecoder<'a, $tcx>) -> Self { + RefDecodable::decode(d) + } + })* + }; +} + +impl_ref_decoder! {<'tcx> + Span, + rustc_ast::Attribute, + rustc_span::symbol::Ident, + ty::Variance, + rustc_span::def_id::DefId, + rustc_span::def_id::LocalDefId, + (rustc_middle::middle::exported_symbols::ExportedSymbol<'tcx>, rustc_middle::middle::exported_symbols::SymbolExportInfo), } //- ENCODING ------------------------------------------------------------------- diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index ae4ad428159..634236c0dac 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -129,7 +129,7 @@ impl<'tcx> QueryCtxt<'tcx> { QueryCtxt { tcx, queries } } - crate fn on_disk_cache(self) -> Option<&'tcx on_disk_cache::OnDiskCache<'tcx>> { + pub(crate) fn on_disk_cache(self) -> Option<&'tcx on_disk_cache::OnDiskCache<'tcx>> { self.queries.on_disk_cache.as_ref() } diff --git a/compiler/rustc_query_system/src/dep_graph/debug.rs b/compiler/rustc_query_system/src/dep_graph/debug.rs index a544ac2c343..f9f3169af69 100644 --- a/compiler/rustc_query_system/src/dep_graph/debug.rs +++ b/compiler/rustc_query_system/src/dep_graph/debug.rs @@ -7,9 +7,9 @@ use std::error::Error; /// A dep-node filter goes from a user-defined string to a query over /// nodes. Right now the format is like this: -/// -/// x & y & z -/// +/// ```ignore (illustrative) +/// x & y & z +/// ``` /// where the format-string of the dep-node must contain `x`, `y`, and /// `z`. #[derive(Debug)] diff --git a/compiler/rustc_resolve/Cargo.toml b/compiler/rustc_resolve/Cargo.toml index bd27c16c732..b2178ff5995 100644 --- a/compiler/rustc_resolve/Cargo.toml +++ b/compiler/rustc_resolve/Cargo.toml @@ -4,7 +4,6 @@ version = "0.0.0" edition = "2021" [lib] -test = false doctest = false [dependencies] diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 783ff5a3f91..3879779635b 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -81,7 +81,7 @@ impl<'a> ToNameBinding<'a> for (Res, ty::Visibility, Span, LocalExpnId, IsMacroE impl<'a> Resolver<'a> { /// Defines `name` in namespace `ns` of module `parent` to be `def` if it is not yet defined; /// otherwise, reports an error. - crate fn define<T>(&mut self, parent: Module<'a>, ident: Ident, ns: Namespace, def: T) + pub(crate) fn define<T>(&mut self, parent: Module<'a>, ident: Ident, ns: Namespace, def: T) where T: ToNameBinding<'a>, { @@ -127,7 +127,7 @@ impl<'a> Resolver<'a> { /// If `def_id` refers to a module (in resolver's sense, i.e. a module item, crate root, enum, /// or trait), then this function returns that module's resolver representation, otherwise it /// returns `None`. - crate fn get_module(&mut self, def_id: DefId) -> Option<Module<'a>> { + pub(crate) fn get_module(&mut self, def_id: DefId) -> Option<Module<'a>> { if let module @ Some(..) = self.module_map.get(&def_id) { return module.copied(); } @@ -162,7 +162,7 @@ impl<'a> Resolver<'a> { } } - crate fn expn_def_scope(&mut self, expn_id: ExpnId) -> Module<'a> { + pub(crate) fn expn_def_scope(&mut self, expn_id: ExpnId) -> Module<'a> { match expn_id.expn_data().macro_def_id { Some(def_id) => self.macro_def_scope(def_id), None => expn_id @@ -172,7 +172,7 @@ impl<'a> Resolver<'a> { } } - crate fn macro_def_scope(&mut self, def_id: DefId) -> Module<'a> { + pub(crate) fn macro_def_scope(&mut self, def_id: DefId) -> Module<'a> { if let Some(id) = def_id.as_local() { self.local_macro_def_scopes[&id] } else { @@ -180,7 +180,7 @@ impl<'a> Resolver<'a> { } } - crate fn get_macro(&mut self, res: Res) -> Option<Lrc<SyntaxExtension>> { + pub(crate) fn get_macro(&mut self, res: Res) -> Option<Lrc<SyntaxExtension>> { match res { Res::Def(DefKind::Macro(..), def_id) => Some(self.get_macro_by_def_id(def_id)), Res::NonMacroAttr(_) => Some(self.non_macro_attr.clone()), @@ -188,13 +188,13 @@ impl<'a> Resolver<'a> { } } - crate fn get_macro_by_def_id(&mut self, def_id: DefId) -> Lrc<SyntaxExtension> { + pub(crate) fn get_macro_by_def_id(&mut self, def_id: DefId) -> Lrc<SyntaxExtension> { if let Some(ext) = self.macro_map.get(&def_id) { return ext.clone(); } let ext = Lrc::new(match self.cstore().load_macro_untracked(def_id, &self.session) { - LoadedMacro::MacroDef(item, edition) => self.compile_macro(&item, edition), + LoadedMacro::MacroDef(item, edition) => self.compile_macro(&item, edition).0, LoadedMacro::ProcMacro(ext) => ext, }); @@ -202,7 +202,7 @@ impl<'a> Resolver<'a> { ext } - crate fn build_reduced_graph( + pub(crate) fn build_reduced_graph( &mut self, fragment: &AstFragment, parent_scope: ParentScope<'a>, @@ -213,7 +213,7 @@ impl<'a> Resolver<'a> { visitor.parent_scope.macro_rules } - crate fn build_reduced_graph_external(&mut self, module: Module<'a>) { + pub(crate) fn build_reduced_graph_external(&mut self, module: Module<'a>) { for child in self.cstore().module_children_untracked(module.def_id(), self.session) { let parent_scope = ParentScope::module(module, self); BuildReducedGraphVisitor { r: self, parent_scope } @@ -1218,9 +1218,18 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { // Mark the given macro as unused unless its name starts with `_`. // Macro uses will remove items from this set, and the remaining // items will be reported as `unused_macros`. - fn insert_unused_macro(&mut self, ident: Ident, def_id: LocalDefId, node_id: NodeId) { + fn insert_unused_macro( + &mut self, + ident: Ident, + def_id: LocalDefId, + node_id: NodeId, + rule_spans: &[Span], + ) { if !ident.as_str().starts_with('_') { self.r.unused_macros.insert(def_id, (node_id, ident)); + for (rule_i, rule_span) in rule_spans.iter().enumerate() { + self.r.unused_macro_rules.insert((def_id, rule_i), (ident, *rule_span)); + } } } @@ -1228,15 +1237,16 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { let parent_scope = self.parent_scope; let expansion = parent_scope.expansion; let def_id = self.r.local_def_id(item.id); - let (ext, ident, span, macro_rules) = match &item.kind { + let (ext, ident, span, macro_rules, rule_spans) = match &item.kind { ItemKind::MacroDef(def) => { - let ext = Lrc::new(self.r.compile_macro(item, self.r.session.edition())); - (ext, item.ident, item.span, def.macro_rules) + let (ext, rule_spans) = self.r.compile_macro(item, self.r.session.edition()); + let ext = Lrc::new(ext); + (ext, item.ident, item.span, def.macro_rules, rule_spans) } ItemKind::Fn(..) => match self.proc_macro_stub(item) { Some((macro_kind, ident, span)) => { self.r.proc_macro_stubs.insert(def_id); - (self.r.dummy_ext(macro_kind), ident, span, false) + (self.r.dummy_ext(macro_kind), ident, span, false, Vec::new()) } None => return parent_scope.macro_rules, }, @@ -1258,13 +1268,12 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { }; let binding = (res, vis, span, expansion).to_name_binding(self.r.arenas); self.r.set_binding_parent_module(binding, parent_scope.module); - self.r.all_macro_rules.insert(ident.name, res); if is_macro_export { let module = self.r.graph_root; self.r.define(module, ident, MacroNS, (res, vis, span, expansion, IsMacroExport)); } else { self.r.check_reserved_macro_name(ident, res); - self.insert_unused_macro(ident, def_id, item.id); + self.insert_unused_macro(ident, def_id, item.id, &rule_spans); } self.r.visibilities.insert(def_id, vis); let scope = self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Binding( @@ -1287,7 +1296,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { _ => self.resolve_visibility(&item.vis), }; if vis != ty::Visibility::Public { - self.insert_unused_macro(ident, def_id, item.id); + self.insert_unused_macro(ident, def_id, item.id, &rule_spans); } self.r.define(module, ident, MacroNS, (res, vis, span, expansion)); self.r.visibilities.insert(def_id, vis); diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 6503b97a1d3..5dc720e0abc 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -224,7 +224,7 @@ fn calc_unused_spans( } impl Resolver<'_> { - crate fn check_unused(&mut self, krate: &ast::Crate) { + pub(crate) fn check_unused(&mut self, krate: &ast::Crate) { for import in self.potentially_unused_imports.iter() { match import.kind { _ if import.used.get() @@ -315,21 +315,28 @@ impl Resolver<'_> { "remove the unused import" }; - let parent_module = visitor.r.get_nearest_non_block_module( - visitor.r.local_def_id(unused.use_tree_id).to_def_id(), - ); - let test_module_span = match module_to_string(parent_module) { - Some(module) - if module == "test" - || module == "tests" - || module.starts_with("test_") - || module.starts_with("tests_") - || module.ends_with("_test") - || module.ends_with("_tests") => - { - Some(parent_module.span) + // If we are in the `--test` mode, suppress a help that adds the `#[cfg(test)]` + // attribute; however, if not, suggest adding the attribute. There is no way to + // retrieve attributes here because we do not have a `TyCtxt` yet. + let test_module_span = if visitor.r.session.opts.test { + None + } else { + let parent_module = visitor.r.get_nearest_non_block_module( + visitor.r.local_def_id(unused.use_tree_id).to_def_id(), + ); + match module_to_string(parent_module) { + Some(module) + if module == "test" + || module == "tests" + || module.starts_with("test_") + || module.starts_with("tests_") + || module.ends_with("_test") + || module.ends_with("_tests") => + { + Some(parent_module.span) + } + _ => None, } - _ => None, }; visitor.r.lint_buffer.buffer_lint_with_diagnostic( diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 1e8cca6122c..f9aff7fd686 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -11,7 +11,7 @@ use rustc_span::symbol::sym; use rustc_span::Span; use tracing::debug; -crate fn collect_definitions( +pub(crate) fn collect_definitions( resolver: &mut Resolver<'_>, fragment: &AstFragment, expansion: LocalExpnId, @@ -109,7 +109,7 @@ impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> { visit::walk_item(self, i); return self.visit_macro_invoc(i.id); } - ItemKind::GlobalAsm(..) => DefPathData::Misc, + ItemKind::GlobalAsm(..) => DefPathData::GlobalAsm, ItemKind::Use(..) => { return visit::walk_item(self, i); } @@ -160,11 +160,11 @@ impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> { } fn visit_use_tree(&mut self, use_tree: &'a UseTree, id: NodeId, _nested: bool) { - self.create_def(id, DefPathData::Misc, use_tree.span); + self.create_def(id, DefPathData::Use, use_tree.span); match use_tree.kind { UseTreeKind::Simple(_, id1, id2) => { - self.create_def(id1, DefPathData::Misc, use_tree.prefix.span); - self.create_def(id2, DefPathData::Misc, use_tree.prefix.span); + self.create_def(id1, DefPathData::Use, use_tree.prefix.span); + self.create_def(id2, DefPathData::Use, use_tree.prefix.span); } UseTreeKind::Glob => (), UseTreeKind::Nested(..) => {} diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 5d80f49626a..c199cff2038 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -35,39 +35,42 @@ use crate::{LexicalScopeBinding, NameBinding, NameBindingKind, PrivacyError, Vis use crate::{ParentScope, PathResult, ResolutionError, Resolver, Scope, ScopeSet}; use crate::{Segment, UseError}; +#[cfg(test)] +mod tests; + type Res = def::Res<ast::NodeId>; /// A vector of spans and replacements, a message and applicability. -crate type Suggestion = (Vec<(Span, String)>, String, Applicability); +pub(crate) type Suggestion = (Vec<(Span, String)>, String, Applicability); /// Potential candidate for an undeclared or out-of-scope label - contains the ident of a /// similarly named label and whether or not it is reachable. -crate type LabelSuggestion = (Ident, bool); +pub(crate) type LabelSuggestion = (Ident, bool); -crate enum SuggestionTarget { +pub(crate) enum SuggestionTarget { /// The target has a similar name as the name used by the programmer (probably a typo) SimilarlyNamed, /// The target is the only valid item that can be used in the corresponding context SingleItem, } -crate struct TypoSuggestion { +pub(crate) struct TypoSuggestion { pub candidate: Symbol, pub res: Res, pub target: SuggestionTarget, } impl TypoSuggestion { - crate fn typo_from_res(candidate: Symbol, res: Res) -> TypoSuggestion { + pub(crate) fn typo_from_res(candidate: Symbol, res: Res) -> TypoSuggestion { Self { candidate, res, target: SuggestionTarget::SimilarlyNamed } } - crate fn single_item_from_res(candidate: Symbol, res: Res) -> TypoSuggestion { + pub(crate) fn single_item_from_res(candidate: Symbol, res: Res) -> TypoSuggestion { Self { candidate, res, target: SuggestionTarget::SingleItem } } } /// A free importable items suggested in case of resolution failure. -crate struct ImportSuggestion { +pub(crate) struct ImportSuggestion { pub did: Option<DefId>, pub descr: &'static str, pub path: Path, @@ -89,7 +92,7 @@ fn reduce_impl_span_to_impl_keyword(sm: &SourceMap, impl_span: Span) -> Span { } impl<'a> Resolver<'a> { - crate fn report_errors(&mut self, krate: &Crate) { + pub(crate) fn report_errors(&mut self, krate: &Crate) { self.report_with_use_injections(krate); for &(span_use, span_def) in &self.macro_expanded_macro_export_errors { @@ -144,7 +147,7 @@ impl<'a> Resolver<'a> { } } - crate fn report_conflict<'b>( + pub(crate) fn report_conflict<'b>( &mut self, parent: Module<'_>, ident: Ident, @@ -416,7 +419,7 @@ impl<'a> Resolver<'a> { err.span_suggestion(span, message, String::new(), Applicability::MachineApplicable); } - crate fn lint_if_path_starts_with_module( + pub(crate) fn lint_if_path_starts_with_module( &mut self, finalize: Option<Finalize>, path: &[Segment], @@ -472,7 +475,7 @@ impl<'a> Resolver<'a> { ); } - crate fn add_module_candidates( + pub(crate) fn add_module_candidates( &mut self, module: Module<'a>, names: &mut Vec<TypoSuggestion>, @@ -492,11 +495,11 @@ impl<'a> Resolver<'a> { /// /// This takes the error provided, combines it with the span and any additional spans inside the /// error and emits it. - crate fn report_error(&mut self, span: Span, resolution_error: ResolutionError<'a>) { + pub(crate) fn report_error(&mut self, span: Span, resolution_error: ResolutionError<'a>) { self.into_struct_error(span, resolution_error).emit(); } - crate fn into_struct_error( + pub(crate) fn into_struct_error( &mut self, span: Span, resolution_error: ResolutionError<'a>, @@ -1049,7 +1052,7 @@ impl<'a> Resolver<'a> { } } - crate fn report_vis_error( + pub(crate) fn report_vis_error( &mut self, vis_resolution_error: VisResolutionError<'_>, ) -> ErrorGuaranteed { @@ -1410,7 +1413,7 @@ impl<'a> Resolver<'a> { /// /// N.B., the method does not look into imports, but this is not a problem, /// since we report the definitions (thus, the de-aliased imports). - crate fn lookup_import_candidates<FilterFn>( + pub(crate) fn lookup_import_candidates<FilterFn>( &mut self, lookup_ident: Ident, namespace: Namespace, @@ -1457,7 +1460,7 @@ impl<'a> Resolver<'a> { suggestions } - crate fn unresolved_macro_suggestions( + pub(crate) fn unresolved_macro_suggestions( &mut self, err: &mut Diagnostic, macro_kind: MacroKind, @@ -1548,7 +1551,7 @@ impl<'a> Resolver<'a> { } } - crate fn add_typo_suggestion( + pub(crate) fn add_typo_suggestion( &self, err: &mut Diagnostic, suggestion: Option<TypoSuggestion>, @@ -1777,7 +1780,7 @@ impl<'a> Resolver<'a> { err.emit(); } - crate fn find_similarly_named_module_or_crate( + pub(crate) fn find_similarly_named_module_or_crate( &mut self, ident: Symbol, current_module: &Module<'a>, @@ -1804,7 +1807,7 @@ impl<'a> Resolver<'a> { } } - crate fn report_path_resolution_error( + pub(crate) fn report_path_resolution_error( &mut self, path: &[Segment], opt_ns: Option<Namespace>, // `None` indicates a module path in import @@ -2270,16 +2273,16 @@ impl<'a, 'b> ImportResolver<'a, 'b> { /// Given a `binding_span` of a binding within a use statement: /// -/// ``` +/// ```ignore (illustrative) /// use foo::{a, b, c}; -/// ^ +/// // ^ /// ``` /// /// then return the span until the next binding or the end of the statement: /// -/// ``` +/// ```ignore (illustrative) /// use foo::{a, b, c}; -/// ^^^ +/// // ^^^ /// ``` fn find_span_of_binding_until_next_binding( sess: &Session, @@ -2323,14 +2326,14 @@ fn find_span_of_binding_until_next_binding( /// Given a `binding_span`, return the span through to the comma or opening brace of the previous /// binding. /// -/// ``` +/// ```ignore (illustrative) /// use foo::a::{a, b, c}; -/// ^^--- binding span -/// | -/// returned span +/// // ^^--- binding span +/// // | +/// // returned span /// /// use foo::{a, b, c}; -/// --- binding span +/// // --- binding span /// ``` fn extend_span_to_previous_binding(sess: &Session, binding_span: Span) -> Option<Span> { let source_map = sess.source_map(); @@ -2366,15 +2369,15 @@ fn extend_span_to_previous_binding(sess: &Session, binding_span: Span) -> Option /// Given a `use_span` of a binding within a use statement, returns the highlighted span and if /// it is a nested use tree. /// -/// ``` +/// ```ignore (illustrative) /// use foo::a::{b, c}; -/// ^^^^^^^^^^ // false +/// // ^^^^^^^^^^ -- false /// /// use foo::{a, b, c}; -/// ^^^^^^^^^^ // true +/// // ^^^^^^^^^^ -- true /// /// use foo::{a, b::{c, d}}; -/// ^^^^^^^^^^^^^^^ // true +/// // ^^^^^^^^^^^^^^^ -- true /// ``` fn find_span_immediately_after_crate_name( sess: &Session, @@ -2675,3 +2678,14 @@ fn is_span_suitable_for_use_injection(s: Span) -> bool { // import or other generated ones !s.from_expansion() } + +/// Convert the given number into the corresponding ordinal +pub(crate) fn ordinalize(v: usize) -> String { + let suffix = match ((11..=13).contains(&(v % 100)), v % 10) { + (false, 1) => "st", + (false, 2) => "nd", + (false, 3) => "rd", + _ => "th", + }; + format!("{v}{suffix}") +} diff --git a/compiler/rustc_resolve/src/diagnostics/tests.rs b/compiler/rustc_resolve/src/diagnostics/tests.rs new file mode 100644 index 00000000000..2aa6cc61e46 --- /dev/null +++ b/compiler/rustc_resolve/src/diagnostics/tests.rs @@ -0,0 +1,40 @@ +use super::ordinalize; + +#[test] +fn test_ordinalize() { + assert_eq!(ordinalize(1), "1st"); + assert_eq!(ordinalize(2), "2nd"); + assert_eq!(ordinalize(3), "3rd"); + assert_eq!(ordinalize(4), "4th"); + assert_eq!(ordinalize(5), "5th"); + // ... + assert_eq!(ordinalize(10), "10th"); + assert_eq!(ordinalize(11), "11th"); + assert_eq!(ordinalize(12), "12th"); + assert_eq!(ordinalize(13), "13th"); + assert_eq!(ordinalize(14), "14th"); + // ... + assert_eq!(ordinalize(20), "20th"); + assert_eq!(ordinalize(21), "21st"); + assert_eq!(ordinalize(22), "22nd"); + assert_eq!(ordinalize(23), "23rd"); + assert_eq!(ordinalize(24), "24th"); + // ... + assert_eq!(ordinalize(30), "30th"); + assert_eq!(ordinalize(31), "31st"); + assert_eq!(ordinalize(32), "32nd"); + assert_eq!(ordinalize(33), "33rd"); + assert_eq!(ordinalize(34), "34th"); + // ... + assert_eq!(ordinalize(7010), "7010th"); + assert_eq!(ordinalize(7011), "7011th"); + assert_eq!(ordinalize(7012), "7012th"); + assert_eq!(ordinalize(7013), "7013th"); + assert_eq!(ordinalize(7014), "7014th"); + // ... + assert_eq!(ordinalize(7020), "7020th"); + assert_eq!(ordinalize(7021), "7021st"); + assert_eq!(ordinalize(7022), "7022nd"); + assert_eq!(ordinalize(7023), "7023rd"); + assert_eq!(ordinalize(7024), "7024th"); +} diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index baaab33d71f..b25393c3ed8 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -28,7 +28,7 @@ impl<'a> Resolver<'a> { /// A generic scope visitor. /// Visits scopes in order to resolve some identifier in them or perform other actions. /// If the callback returns `Some` result, we stop visiting scopes and return it. - crate fn visit_scopes<T>( + pub(crate) fn visit_scopes<T>( &mut self, scope_set: ScopeSet<'a>, parent_scope: &ParentScope<'a>, @@ -274,7 +274,7 @@ impl<'a> Resolver<'a> { /// Invariant: This must only be called during main resolution, not during /// import resolution. #[tracing::instrument(level = "debug", skip(self, ribs))] - crate fn resolve_ident_in_lexical_scope( + pub(crate) fn resolve_ident_in_lexical_scope( &mut self, mut ident: Ident, ns: Namespace, @@ -368,7 +368,7 @@ impl<'a> Resolver<'a> { /// The function is used for resolving initial segments of macro paths (e.g., `foo` in /// `foo::bar!(); or `foo!();`) and also for import paths on 2018 edition. #[tracing::instrument(level = "debug", skip(self, scope_set))] - crate fn early_resolve_ident_in_lexical_scope( + pub(crate) fn early_resolve_ident_in_lexical_scope( &mut self, orig_ident: Ident, scope_set: ScopeSet<'a>, @@ -717,7 +717,7 @@ impl<'a> Resolver<'a> { } #[tracing::instrument(level = "debug", skip(self))] - crate fn maybe_resolve_ident_in_module( + pub(crate) fn maybe_resolve_ident_in_module( &mut self, module: ModuleOrUniformRoot<'a>, ident: Ident, @@ -729,7 +729,7 @@ impl<'a> Resolver<'a> { } #[tracing::instrument(level = "debug", skip(self))] - crate fn resolve_ident_in_module( + pub(crate) fn resolve_ident_in_module( &mut self, module: ModuleOrUniformRoot<'a>, ident: Ident, @@ -1171,6 +1171,7 @@ impl<'a> Resolver<'a> { | AssocItemRibKind | ModuleRibKind(..) | MacroDefinition(..) + | InlineAsmSymRibKind | ForwardGenericParamBanRibKind => { // Nothing to do. Continue. continue; @@ -1216,22 +1217,6 @@ impl<'a> Resolver<'a> { } return Res::Err; } - InlineAsmSymRibKind => { - let features = self.session.features_untracked(); - if !features.generic_const_exprs { - if let Some(span) = finalize { - self.report_error( - span, - ResolutionError::ParamInNonTrivialAnonConst { - name: rib_ident.name, - is_type: true, - }, - ); - } - return Res::Err; - } - continue; - } }; if let Some(span) = finalize { @@ -1262,6 +1247,7 @@ impl<'a> Resolver<'a> { | AssocItemRibKind | ModuleRibKind(..) | MacroDefinition(..) + | InlineAsmSymRibKind | ForwardGenericParamBanRibKind => continue, ConstantItemRibKind(trivial, _) => { @@ -1296,22 +1282,6 @@ impl<'a> Resolver<'a> { } return Res::Err; } - InlineAsmSymRibKind => { - let features = self.session.features_untracked(); - if !features.generic_const_exprs { - if let Some(span) = finalize { - self.report_error( - span, - ResolutionError::ParamInNonTrivialAnonConst { - name: rib_ident.name, - is_type: false, - }, - ); - } - return Res::Err; - } - continue; - } }; // This was an attempt to use a const parameter outside its scope. @@ -1333,7 +1303,7 @@ impl<'a> Resolver<'a> { } #[tracing::instrument(level = "debug", skip(self))] - crate fn maybe_resolve_path( + pub(crate) fn maybe_resolve_path( &mut self, path: &[Segment], opt_ns: Option<Namespace>, // `None` indicates a module path in import @@ -1343,7 +1313,7 @@ impl<'a> Resolver<'a> { } #[tracing::instrument(level = "debug", skip(self))] - crate fn resolve_path( + pub(crate) fn resolve_path( &mut self, path: &[Segment], opt_ns: Option<Namespace>, // `None` indicates a module path in import @@ -1354,7 +1324,7 @@ impl<'a> Resolver<'a> { self.resolve_path_with_ribs(path, opt_ns, parent_scope, finalize, None, ignore_binding) } - crate fn resolve_path_with_ribs( + pub(crate) fn resolve_path_with_ribs( &mut self, path: &[Segment], opt_ns: Option<Namespace>, // `None` indicates a module path in import diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 3d0e2b9921d..a8c8c674d2d 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -64,7 +64,7 @@ pub enum ImportKind<'a> { /// One import. #[derive(Debug, Clone)] -crate struct Import<'a> { +pub(crate) struct Import<'a> { pub kind: ImportKind<'a>, /// The ID of the `extern crate`, `UseTree` etc that imported this `Import`. @@ -125,7 +125,7 @@ impl<'a> Import<'a> { /// Records information about the resolution of a name in a namespace of a module. #[derive(Clone, Default, Debug)] -crate struct NameResolution<'a> { +pub(crate) struct NameResolution<'a> { /// Single imports that may define the name in the namespace. /// Imports are arena-allocated, so it's ok to use pointers as keys. pub single_imports: FxHashSet<Interned<'a, Import<'a>>>, @@ -146,7 +146,7 @@ impl<'a> NameResolution<'a> { }) } - crate fn add_single_import(&mut self, import: &'a Import<'a>) { + pub(crate) fn add_single_import(&mut self, import: &'a Import<'a>) { self.single_imports.insert(Interned::new_unchecked(import)); } } @@ -169,7 +169,7 @@ fn pub_use_of_private_extern_crate_hack(import: &Import<'_>, binding: &NameBindi impl<'a> Resolver<'a> { // Given a binding and an import that resolves to it, // return the corresponding binding defined by the import. - crate fn import( + pub(crate) fn import( &self, binding: &'a NameBinding<'a>, import: &'a Import<'a>, @@ -198,7 +198,7 @@ impl<'a> Resolver<'a> { } // Define the name or return the existing binding if there is a collision. - crate fn try_define( + pub(crate) fn try_define( &mut self, module: Module<'a>, key: BindingKey, diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index df6733ac45f..ff87baeef3e 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1,3 +1,4 @@ +// ignore-tidy-filelength //! "Late resolution" is the pass that resolves most of names in a crate beside imports and macros. //! It runs when the crate is fully expanded and its module structure is fully built. //! So it just walks through the crate and resolves all the expressions, types, etc. @@ -19,7 +20,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_errors::DiagnosticId; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, DefKind, PartialRes, PerNS}; -use rustc_hir::def_id::{DefId, CRATE_DEF_ID}; +use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID}; use rustc_hir::definitions::DefPathData; use rustc_hir::{PrimTy, TraitCandidate}; use rustc_index::vec::Idx; @@ -36,7 +37,7 @@ use std::mem::{replace, take}; use tracing::debug; mod diagnostics; -crate mod lifetimes; +pub(crate) mod lifetimes; type Res = def::Res<NodeId>; @@ -89,7 +90,7 @@ enum PatBoundCtx { /// Does this the item (from the item rib scope) allow generic parameters? #[derive(Copy, Clone, Debug, Eq, PartialEq)] -crate enum HasGenericParams { +pub(crate) enum HasGenericParams { Yes, No, } @@ -101,7 +102,7 @@ impl HasGenericParams { } #[derive(Copy, Clone, Debug, Eq, PartialEq)] -crate enum ConstantItemKind { +pub(crate) enum ConstantItemKind { Const, Static, } @@ -109,7 +110,7 @@ crate enum ConstantItemKind { /// The rib kind restricts certain accesses, /// e.g. to a `Res::Local` of an outer item. #[derive(Copy, Clone, Debug)] -crate enum RibKind<'a> { +pub(crate) enum RibKind<'a> { /// No restriction needs to be applied. NormalRibKind, @@ -158,7 +159,7 @@ crate enum RibKind<'a> { impl RibKind<'_> { /// Whether this rib kind contains generic parameters, as opposed to local /// variables. - crate fn contains_params(&self) -> bool { + pub(crate) fn contains_params(&self) -> bool { match self { NormalRibKind | ClosureOrAsyncRibKind @@ -186,7 +187,7 @@ impl RibKind<'_> { /// The resolution keeps a separate stack of ribs as it traverses the AST for each namespace. When /// resolving, the name is looked up from inside out. #[derive(Debug)] -crate struct Rib<'a, R = Res> { +pub(crate) struct Rib<'a, R = Res> { pub bindings: IdentMap<R>, pub kind: RibKind<'a>, } @@ -197,13 +198,19 @@ impl<'a, R> Rib<'a, R> { } } +#[derive(Clone, Copy, Debug)] +enum LifetimeUseSet { + One { use_span: Span, use_ctxt: visit::LifetimeCtxt }, + Many, +} + #[derive(Copy, Clone, Debug)] enum LifetimeRibKind { /// This rib acts as a barrier to forbid reference to lifetimes of a parent item. Item, /// This rib declares generic parameters. - Generics { parent: NodeId, span: Span, kind: LifetimeBinderKind }, + Generics { binder: NodeId, span: Span, kind: LifetimeBinderKind }, /// FIXME(const_generics): This patches over an ICE caused by non-'static lifetimes in const /// generics. We are disallowing this until we can decide on how we want to handle non-'static @@ -230,7 +237,7 @@ enum LifetimeRibKind { AnonymousReportError, /// Pass responsibility to `resolve_lifetime` code for all cases. - AnonymousPassThrough(NodeId), + AnonymousPassThrough(NodeId, /* in_fn_return */ bool), } #[derive(Copy, Clone, Debug)] @@ -271,13 +278,13 @@ impl LifetimeRib { } #[derive(Copy, Clone, PartialEq, Eq, Debug)] -crate enum AliasPossibility { +pub(crate) enum AliasPossibility { No, Maybe, } #[derive(Copy, Clone, Debug)] -crate enum PathSource<'a> { +pub(crate) enum PathSource<'a> { // Type paths `Path`. Type, // Trait paths in bounds or impls. @@ -359,7 +366,7 @@ impl<'a> PathSource<'a> { matches!(self, PathSource::Expr(Some(&Expr { kind: ExprKind::Call(..), .. }))) } - crate fn is_expected(self, res: Res) -> bool { + pub(crate) fn is_expected(self, res: Res) -> bool { match self { PathSource::Type => matches!( res, @@ -519,6 +526,9 @@ struct LateResolutionVisitor<'a, 'b, 'ast> { /// In most cases this will be `None`, in which case errors will always be reported. /// If it is `true`, then it will be updated when entering a nested function or trait body. in_func_body: bool, + + /// Count the number of places a lifetime is used. + lifetime_uses: FxHashMap<LocalDefId, LifetimeUseSet>, } /// Walks the whole crate in DFS order, visiting each item, resolving names as it goes. @@ -594,26 +604,24 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { self.diagnostic_metadata.current_trait_object = Some(&bounds[..]); } TyKind::BareFn(ref bare_fn) => { - let span = if bare_fn.generic_params.is_empty() { - ty.span.shrink_to_lo() - } else { - ty.span - }; + let span = ty.span.shrink_to_lo().to(bare_fn.decl_span.shrink_to_lo()); self.with_generic_param_rib( &bare_fn.generic_params, NormalRibKind, LifetimeRibKind::Generics { - parent: ty.id, + binder: ty.id, kind: LifetimeBinderKind::BareFnType, span, }, |this| { + this.visit_generic_param_vec(&bare_fn.generic_params, false); this.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough(ty.id), - |this| { - this.visit_generic_param_vec(&bare_fn.generic_params, false); - visit::walk_fn_decl(this, &bare_fn.decl); - }, + LifetimeRibKind::AnonymousPassThrough(ty.id, false), + |this| walk_list!(this, visit_param, &bare_fn.decl.inputs), + ); + this.with_lifetime_rib( + LifetimeRibKind::AnonymousPassThrough(ty.id, true), + |this| this.visit_fn_ret_ty(&bare_fn.decl.output), ); }, ); @@ -627,13 +635,12 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { self.diagnostic_metadata.current_type_path = prev_ty; } fn visit_poly_trait_ref(&mut self, tref: &'ast PolyTraitRef, _: &'ast TraitBoundModifier) { - let span = - if tref.bound_generic_params.is_empty() { tref.span.shrink_to_lo() } else { tref.span }; + let span = tref.span.shrink_to_lo().to(tref.trait_ref.path.span.shrink_to_lo()); self.with_generic_param_rib( &tref.bound_generic_params, NormalRibKind, LifetimeRibKind::Generics { - parent: tref.trait_ref.ref_id, + binder: tref.trait_ref.ref_id, kind: LifetimeBinderKind::PolyTrait, span, }, @@ -657,7 +664,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { &generics.params, ItemRibKind(HasGenericParams::Yes), LifetimeRibKind::Generics { - parent: foreign_item.id, + binder: foreign_item.id, kind: LifetimeBinderKind::Item, span: generics.span, }, @@ -671,7 +678,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { &generics.params, ItemRibKind(HasGenericParams::Yes), LifetimeRibKind::Generics { - parent: foreign_item.id, + binder: foreign_item.id, kind: LifetimeBinderKind::Function, span: generics.span, }, @@ -695,13 +702,20 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { // a body, or if there's no body for some other reason. FnKind::Fn(FnCtxt::Foreign, _, sig, _, generics, _) | FnKind::Fn(_, _, sig, _, generics, None) => { - self.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough(fn_id), |this| { - // We don't need to deal with patterns in parameters, because - // they are not possible for foreign or bodiless functions. - this.visit_fn_header(&sig.header); - this.visit_generics(generics); - visit::walk_fn_decl(this, &sig.decl); - }); + // We don't need to deal with patterns in parameters, because + // they are not possible for foreign or bodiless functions. + self.with_lifetime_rib( + LifetimeRibKind::AnonymousPassThrough(fn_id, false), + |this| { + this.visit_fn_header(&sig.header); + this.visit_generics(generics); + walk_list!(this, visit_param, &sig.decl.inputs); + }, + ); + self.with_lifetime_rib( + LifetimeRibKind::AnonymousPassThrough(fn_id, true), + |this| this.visit_fn_ret_ty(&sig.decl.output), + ); return; } FnKind::Fn(FnCtxt::Free, ..) => FnItemRibKind, @@ -764,28 +778,32 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { this.r.extra_lifetime_params_map.insert(async_node_id, extra_lifetime_params); this.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough(async_node_id), + LifetimeRibKind::AnonymousPassThrough(async_node_id, true), |this| visit::walk_fn_ret_ty(this, &declaration.output), ); } else { - this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough(fn_id), |this| { - // Add each argument to the rib. - this.resolve_params(&declaration.inputs); - - visit::walk_fn_ret_ty(this, &declaration.output); - }); + // Add each argument to the rib. + this.with_lifetime_rib( + LifetimeRibKind::AnonymousPassThrough(fn_id, false), + |this| this.resolve_params(&declaration.inputs), + ); + this.with_lifetime_rib( + LifetimeRibKind::AnonymousPassThrough(fn_id, true), + |this| visit::walk_fn_ret_ty(this, &declaration.output), + ); }; // Ignore errors in function bodies if this is rustdoc // Be sure not to set this until the function signature has been resolved. let previous_state = replace(&mut this.in_func_body, true); // Resolve the function body, potentially inside the body of an async closure - this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough(fn_id), |this| { - match fn_kind { + this.with_lifetime_rib( + LifetimeRibKind::AnonymousPassThrough(fn_id, false), + |this| match fn_kind { FnKind::Fn(.., body) => walk_list!(this, visit_block, body), FnKind::Closure(_, body) => this.visit_expr(body), - } - }); + }, + ); debug!("(resolving function) leaving function"); this.in_func_body = previous_state; @@ -793,8 +811,8 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { }); self.diagnostic_metadata.current_function = previous_value; } - fn visit_lifetime(&mut self, lifetime: &'ast Lifetime) { - self.resolve_lifetime(lifetime) + fn visit_lifetime(&mut self, lifetime: &'ast Lifetime, use_ctxt: visit::LifetimeCtxt) { + self.resolve_lifetime(lifetime, use_ctxt) } fn visit_generics(&mut self, generics: &'ast Generics) { @@ -859,7 +877,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { self.visit_ty(ty); } - GenericArg::Lifetime(lt) => self.visit_lifetime(lt), + GenericArg::Lifetime(lt) => self.visit_lifetime(lt, visit::LifetimeCtxt::GenericArg), GenericArg::Const(ct) => self.visit_anon_const(ct), } self.diagnostic_metadata.currently_processing_generics = prev; @@ -869,10 +887,16 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { if let Some(ref args) = path_segment.args { match &**args { GenericArgs::AngleBracketed(..) => visit::walk_generic_args(self, path_span, args), - GenericArgs::Parenthesized(..) => self.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough(path_segment.id), - |this| visit::walk_generic_args(this, path_span, args), - ), + GenericArgs::Parenthesized(ref data) => { + self.with_lifetime_rib( + LifetimeRibKind::AnonymousPassThrough(path_segment.id, false), + |this| walk_list!(this, visit_ty, &data.inputs), + ); + self.with_lifetime_rib( + LifetimeRibKind::AnonymousPassThrough(path_segment.id, true), + |this| visit::walk_fn_ret_ty(this, &data.output), + ) + } } } } @@ -890,16 +914,12 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { .. }) = p { - let span = if bound_generic_params.is_empty() { - predicate_span.shrink_to_lo() - } else { - *predicate_span - }; + let span = predicate_span.shrink_to_lo().to(bounded_ty.span.shrink_to_lo()); this.with_generic_param_rib( &bound_generic_params, NormalRibKind, LifetimeRibKind::Generics { - parent: bounded_ty.id, + binder: bounded_ty.id, kind: LifetimeBinderKind::WhereBound, span, }, @@ -918,6 +938,29 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { self.diagnostic_metadata.current_where_predicate = previous_value; } + fn visit_inline_asm(&mut self, asm: &'ast InlineAsm) { + for (op, _) in &asm.operands { + match op { + InlineAsmOperand::In { expr, .. } + | InlineAsmOperand::Out { expr: Some(expr), .. } + | InlineAsmOperand::InOut { expr, .. } => self.visit_expr(expr), + InlineAsmOperand::Out { expr: None, .. } => {} + InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { + self.visit_expr(in_expr); + if let Some(out_expr) = out_expr { + self.visit_expr(out_expr); + } + } + InlineAsmOperand::Const { anon_const, .. } => { + // Although this is `DefKind::AnonConst`, it is allowed to reference outer + // generic parameters like an inline const. + self.resolve_inline_const(anon_const); + } + InlineAsmOperand::Sym { sym } => self.visit_inline_asm_sym(sym), + } + } + } + fn visit_inline_asm_sym(&mut self, sym: &'ast InlineAsmSym) { // This is similar to the code for AnonConst. self.with_rib(ValueNS, InlineAsmSymRibKind, |this| { @@ -957,6 +1000,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { diagnostic_metadata: DiagnosticMetadata::default(), // errors at module scope should always be reported in_func_body: false, + lifetime_uses: Default::default(), } } @@ -1164,7 +1208,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } #[tracing::instrument(level = "debug", skip(self))] - fn resolve_lifetime(&mut self, lifetime: &'ast Lifetime) { + fn resolve_lifetime(&mut self, lifetime: &'ast Lifetime, use_ctxt: visit::LifetimeCtxt) { let ident = lifetime.ident; if ident.name == kw::StaticLifetime { @@ -1182,6 +1226,40 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { let normalized_ident = ident.normalize_to_macros_2_0(); if let Some(&(_, region)) = rib.bindings.get(&normalized_ident) { self.record_lifetime_res(lifetime.id, region); + + if let LifetimeRes::Param { param, .. } = region { + match self.lifetime_uses.entry(param) { + Entry::Vacant(v) => { + debug!("First use of {:?} at {:?}", region, ident.span); + let use_set = self + .lifetime_ribs + .iter() + .rev() + .find_map(|rib| match rib.kind { + // Do not suggest eliding a lifetime where an anonymous + // lifetime would be illegal. + LifetimeRibKind::Item + | LifetimeRibKind::AnonymousPassThrough(_, true) + | LifetimeRibKind::AnonymousReportError => { + Some(LifetimeUseSet::Many) + } + // An anonymous lifetime is legal here, go ahead. + LifetimeRibKind::AnonymousPassThrough(_, false) + | LifetimeRibKind::AnonymousCreateParameter(_) => { + Some(LifetimeUseSet::One { use_span: ident.span, use_ctxt }) + } + _ => None, + }) + .unwrap_or(LifetimeUseSet::Many); + debug!(?use_ctxt, ?use_set); + v.insert(use_set); + } + Entry::Occupied(mut o) => { + debug!("Many uses of {:?} at {:?}", region, ident.span); + *o.get_mut() = LifetimeUseSet::Many; + } + } + } return; } @@ -1248,7 +1326,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.record_lifetime_res(lifetime.id, LifetimeRes::Error); return; } - LifetimeRibKind::AnonymousPassThrough(node_id) => { + LifetimeRibKind::AnonymousPassThrough(node_id, _) => { self.record_lifetime_res( lifetime.id, LifetimeRes::Anonymous { binder: node_id, elided }, @@ -1368,7 +1446,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // `PathSegment`, for which there is no associated `'_` or `&T` with no explicit // lifetime. Instead, we simply create an implicit lifetime, which will be checked // later, at which point a suitable error will be emitted. - LifetimeRibKind::AnonymousPassThrough(binder) => { + LifetimeRibKind::AnonymousPassThrough(binder, _) => { res = LifetimeRes::Anonymous { binder, elided: true }; break; } @@ -1536,7 +1614,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { &generics.params, ItemRibKind(HasGenericParams::Yes), LifetimeRibKind::Generics { - parent: item.id, + binder: item.id, kind: LifetimeBinderKind::Item, span: generics.span, }, @@ -1606,7 +1684,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { &generics.params, ItemRibKind(HasGenericParams::Yes), LifetimeRibKind::Generics { - parent: item.id, + binder: item.id, kind: LifetimeBinderKind::Item, span: generics.span, }, @@ -1619,7 +1697,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { &generics.params, ItemRibKind(HasGenericParams::Yes), LifetimeRibKind::Generics { - parent: item.id, + binder: item.id, kind: LifetimeBinderKind::Function, span: generics.span, }, @@ -1651,7 +1729,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { &generics.params, ItemRibKind(HasGenericParams::Yes), LifetimeRibKind::Generics { - parent: item.id, + binder: item.id, kind: LifetimeBinderKind::Item, span: generics.span, }, @@ -1672,7 +1750,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { &generics.params, AssocItemRibKind, LifetimeRibKind::Generics { - parent: item.id, + binder: item.id, span: generics.span, kind, }, @@ -1740,7 +1818,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { &generics.params, ItemRibKind(HasGenericParams::Yes), LifetimeRibKind::Generics { - parent: item.id, + binder: item.id, kind: LifetimeBinderKind::Item, span: generics.span, }, @@ -1810,6 +1888,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { F: FnOnce(&mut Self), { debug!("with_generic_param_rib"); + let LifetimeRibKind::Generics { binder, span: generics_span, kind: generics_kind, .. } + = lifetime_kind else { panic!() }; + let mut function_type_rib = Rib::new(kind); let mut function_value_rib = Rib::new(kind); let mut function_lifetime_rib = LifetimeRib::new(lifetime_kind); @@ -1878,8 +1959,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { GenericParamKind::Type { .. } => (&mut function_type_rib, DefKind::TyParam), GenericParamKind::Const { .. } => (&mut function_value_rib, DefKind::ConstParam), GenericParamKind::Lifetime => { - let LifetimeRibKind::Generics { parent, .. } = lifetime_kind else { panic!() }; - let res = LifetimeRes::Param { param: def_id, binder: parent }; + let res = LifetimeRes::Param { param: def_id, binder }; self.record_lifetime_res(param.id, res); function_lifetime_rib.bindings.insert(ident, (param.id, res)); continue; @@ -1899,6 +1979,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.ribs[TypeNS].pop(); self.ribs[ValueNS].pop(); self.lifetime_ribs.pop(); + + if let LifetimeBinderKind::BareFnType + | LifetimeBinderKind::WhereBound + | LifetimeBinderKind::Function + | LifetimeBinderKind::ImplBlock = generics_kind + { + self.maybe_report_lifetime_uses(generics_span, params) + } } fn with_label_rib(&mut self, kind: RibKind<'a>, f: impl FnOnce(&mut Self)) { @@ -2025,7 +2113,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ) { debug!("resolve_implementation"); // If applicable, create a rib for the type parameters. - self.with_generic_param_rib(&generics.params, ItemRibKind(HasGenericParams::Yes), LifetimeRibKind::Generics { span: generics.span, parent: item_id, kind: LifetimeBinderKind::ImplBlock }, |this| { + self.with_generic_param_rib(&generics.params, ItemRibKind(HasGenericParams::Yes), LifetimeRibKind::Generics { span: generics.span, binder: item_id, kind: LifetimeBinderKind::ImplBlock }, |this| { // Dummy self type for better errors if `Self` is used in the trait path. this.with_self_rib(Res::SelfTy { trait_: None, alias_to: None }, |this| { this.with_lifetime_rib(LifetimeRibKind::AnonymousCreateParameter(item_id), |this| { @@ -2052,7 +2140,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { this.visit_generics(generics); // Resolve the items within the impl. - this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough(item_id), + this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough(item_id,false), |this| { this.with_current_self_type(self_type, |this| { this.with_self_rib_ns(ValueNS, Res::SelfCtor(item_def_id), |this| { @@ -2097,7 +2185,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { this.with_generic_param_rib( &generics.params, AssocItemRibKind, - LifetimeRibKind::Generics { parent: item.id, span: generics.span, kind: LifetimeBinderKind::Function }, + LifetimeRibKind::Generics { binder: item.id, span: generics.span, kind: LifetimeBinderKind::Function }, |this| { // If this is a trait impl, ensure the method // exists in trait @@ -2126,7 +2214,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { this.with_generic_param_rib( &generics.params, AssocItemRibKind, - LifetimeRibKind::Generics { parent: item.id, span: generics.span, kind: LifetimeBinderKind::Item }, + LifetimeRibKind::Generics { binder: item.id, span: generics.span, kind: LifetimeBinderKind::Item }, |this| { // If this is a trait impl, ensure the type // exists in trait @@ -3105,6 +3193,13 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ); } + fn resolve_inline_const(&mut self, constant: &'ast AnonConst) { + debug!("resolve_anon_const {constant:?}"); + self.with_constant_rib(IsRepeatExpr::No, HasGenericParams::Yes, None, |this| { + visit::walk_anon_const(this, constant); + }); + } + fn resolve_expr(&mut self, expr: &'ast Expr, parent: Option<&'ast Expr>) { // First, record candidate traits for this expression if it could // result in the invocation of a method call. @@ -3261,7 +3356,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { }); } ExprKind::ConstBlock(ref ct) => { - self.resolve_anon_const(ct, IsRepeatExpr::No); + self.resolve_inline_const(ct); } ExprKind::Index(ref elem, ref idx) => { self.resolve_expr(elem, Some(expr)); diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 4f07d0076f1..673b2e3a55a 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1,16 +1,17 @@ use crate::diagnostics::{ImportSuggestion, LabelSuggestion, TypoSuggestion}; use crate::late::lifetimes::{ElisionFailureInfo, LifetimeContext}; use crate::late::{AliasPossibility, LateResolutionVisitor, RibKind}; -use crate::late::{LifetimeBinderKind, LifetimeRibKind}; +use crate::late::{LifetimeBinderKind, LifetimeRibKind, LifetimeUseSet}; use crate::path_names_to_string; use crate::{Module, ModuleKind, ModuleOrUniformRoot}; use crate::{PathResult, PathSource, Segment}; -use rustc_ast::visit::{FnCtxt, FnKind}; +use rustc_ast::visit::{FnCtxt, FnKind, LifetimeCtxt}; use rustc_ast::{ self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind, NodeId, Path, Ty, TyKind, }; +use rustc_ast_lowering::ResolverAstLowering; use rustc_ast_pretty::pprust::path_segment_to_string; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ @@ -22,6 +23,7 @@ use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, CtorOf, DefKind}; use rustc_hir::def_id::{DefId, CRATE_DEF_ID, LOCAL_CRATE}; use rustc_hir::PrimTy; +use rustc_session::lint; use rustc_session::parse::feature_err; use rustc_span::edition::Edition; use rustc_span::hygiene::MacroKind; @@ -57,13 +59,13 @@ impl AssocSuggestion { } } -crate enum MissingLifetimeSpot<'tcx> { +pub(crate) enum MissingLifetimeSpot<'tcx> { Generics(&'tcx hir::Generics<'tcx>), HigherRanked { span: Span, span_type: ForLifetimeSpanType }, Static, } -crate enum ForLifetimeSpanType { +pub(crate) enum ForLifetimeSpanType { BoundEmpty, BoundTail, TypeEmpty, @@ -71,14 +73,14 @@ crate enum ForLifetimeSpanType { } impl ForLifetimeSpanType { - crate fn descr(&self) -> &'static str { + pub(crate) fn descr(&self) -> &'static str { match self { Self::BoundEmpty | Self::BoundTail => "bound", Self::TypeEmpty | Self::TypeTail => "type", } } - crate fn suggestion(&self, sugg: &str) -> String { + pub(crate) fn suggestion(&self, sugg: &str) -> String { match self { Self::BoundEmpty | Self::TypeEmpty => format!("for<{}> ", sugg), Self::BoundTail | Self::TypeTail => format!(", {}", sugg), @@ -1219,7 +1221,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { /// Given the target `ident` and `kind`, search for the similarly named associated item /// in `self.current_trait_ref`. - crate fn find_similarly_named_assoc_item( + pub(crate) fn find_similarly_named_assoc_item( &mut self, ident: Symbol, kind: &AssocItemKind, @@ -1727,7 +1729,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } } - crate fn report_missing_type_error( + pub(crate) fn report_missing_type_error( &self, path: &[Segment], ) -> Option<(Span, &'static str, String, Applicability)> { @@ -1807,7 +1809,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { /// Given the target `label`, search the `rib_index`th label rib for similarly named labels, /// optionally returning the closest match and whether it is reachable. - crate fn suggestion_for_label_in_rib( + pub(crate) fn suggestion_for_label_in_rib( &self, rib_index: usize, label: Ident, @@ -1832,7 +1834,77 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { }) } - crate fn emit_undeclared_lifetime_error( + pub(crate) fn maybe_report_lifetime_uses( + &mut self, + generics_span: Span, + params: &[ast::GenericParam], + ) { + for (param_index, param) in params.iter().enumerate() { + let GenericParamKind::Lifetime = param.kind else { continue }; + + let def_id = self.r.local_def_id(param.id); + + let use_set = self.lifetime_uses.remove(&def_id); + debug!( + "Use set for {:?}({:?} at {:?}) is {:?}", + def_id, param.ident, param.ident.span, use_set + ); + + let deletion_span = || { + if params.len() == 1 { + // if sole lifetime, remove the entire `<>` brackets + generics_span + } else if param_index == 0 { + // if removing within `<>` brackets, we also want to + // delete a leading or trailing comma as appropriate + param.span().to(params[param_index + 1].span().shrink_to_lo()) + } else { + // if removing within `<>` brackets, we also want to + // delete a leading or trailing comma as appropriate + params[param_index - 1].span().shrink_to_hi().to(param.span()) + } + }; + match use_set { + Some(LifetimeUseSet::Many) => {} + Some(LifetimeUseSet::One { use_span, use_ctxt }) => { + debug!(?param.ident, ?param.ident.span, ?use_span); + + let elidable = matches!(use_ctxt, LifetimeCtxt::Rptr); + + let deletion_span = deletion_span(); + self.r.lint_buffer.buffer_lint_with_diagnostic( + lint::builtin::SINGLE_USE_LIFETIMES, + param.id, + param.ident.span, + &format!("lifetime parameter `{}` only used once", param.ident), + lint::BuiltinLintDiagnostics::SingleUseLifetime { + param_span: param.ident.span, + use_span: Some((use_span, elidable)), + deletion_span, + }, + ); + } + None => { + debug!(?param.ident, ?param.ident.span); + + let deletion_span = deletion_span(); + self.r.lint_buffer.buffer_lint_with_diagnostic( + lint::builtin::UNUSED_LIFETIMES, + param.id, + param.ident.span, + &format!("lifetime parameter `{}` never used", param.ident), + lint::BuiltinLintDiagnostics::SingleUseLifetime { + param_span: param.ident.span, + use_span: None, + deletion_span, + }, + ); + } + } + } + } + + pub(crate) fn emit_undeclared_lifetime_error( &self, lifetime_ref: &ast::Lifetime, outer_lifetime_ref: Option<Ident>, @@ -1863,7 +1935,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { for rib in self.lifetime_ribs.iter().rev() { match rib.kind { - LifetimeRibKind::Generics { parent: _, span, kind } => { + LifetimeRibKind::Generics { binder: _, span, kind } => { if !span.can_be_used_for_suggestions() && suggest_note { suggest_note = false; // Avoid displaying the same help multiple times. err.span_label( @@ -1928,7 +2000,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { err.emit(); } - crate fn emit_non_static_lt_in_const_generic_error(&self, lifetime_ref: &ast::Lifetime) { + pub(crate) fn emit_non_static_lt_in_const_generic_error(&self, lifetime_ref: &ast::Lifetime) { struct_span_err!( self.r.session, lifetime_ref.ident.span, @@ -1946,7 +2018,10 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { /// Non-static lifetimes are prohibited in anonymous constants under `min_const_generics`. /// This function will emit an error if `generic_const_exprs` is not enabled, the body identified by /// `body_id` is an anonymous constant and `lifetime_ref` is non-static. - crate fn maybe_emit_forbidden_non_static_lifetime_error(&self, lifetime_ref: &ast::Lifetime) { + pub(crate) fn maybe_emit_forbidden_non_static_lifetime_error( + &self, + lifetime_ref: &ast::Lifetime, + ) { let feature_active = self.r.session.features_untracked().generic_const_exprs; if !feature_active { feature_err( @@ -1961,7 +2036,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } impl<'tcx> LifetimeContext<'_, 'tcx> { - crate fn report_missing_lifetime_specifiers( + pub(crate) fn report_missing_lifetime_specifiers( &self, spans: Vec<Span>, count: usize, @@ -1976,7 +2051,7 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { } /// Returns whether to add `'static` lifetime to the suggested lifetime list. - crate fn report_elision_failure( + pub(crate) fn report_elision_failure( &mut self, diag: &mut Diagnostic, params: &[ElisionFailureInfo], @@ -2054,7 +2129,10 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { } } - crate fn is_trait_ref_fn_scope(&mut self, trait_ref: &'tcx hir::PolyTraitRef<'tcx>) -> bool { + pub(crate) fn is_trait_ref_fn_scope( + &mut self, + trait_ref: &'tcx hir::PolyTraitRef<'tcx>, + ) -> bool { if let def::Res::Def(_, did) = trait_ref.trait_ref.path.res { if [ self.tcx.lang_items().fn_once_trait(), @@ -2075,7 +2153,7 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { false } - crate fn add_missing_lifetime_specifiers_label( + pub(crate) fn add_missing_lifetime_specifiers_label( &self, err: &mut Diagnostic, mut spans_with_counts: Vec<(Span, usize)>, diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index 50428811fff..2fe65441ac9 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -9,20 +9,19 @@ use crate::late::diagnostics::{ForLifetimeSpanType, MissingLifetimeSpot}; use rustc_ast::walk_list; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; -use rustc_errors::{struct_span_err, Applicability, Diagnostic}; +use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefIdMap, LocalDefId}; use rustc_hir::hir_id::ItemLocalId; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{GenericArg, GenericParam, LifetimeName, Node, ParamName, QPath}; +use rustc_hir::{GenericArg, GenericParam, LifetimeName, Node, ParamName}; use rustc_hir::{GenericParamKind, HirIdMap, HirIdSet}; use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_lifetime::*; use rustc_middle::ty::{self, DefIdTree, GenericParamDefKind, TyCtxt}; use rustc_middle::{bug, span_bug}; -use rustc_session::lint; use rustc_span::def_id::DefId; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; @@ -33,13 +32,6 @@ use std::mem::take; use tracing::{debug, span, Level}; -// This counts the no of times a lifetime is used -#[derive(Clone, Copy, Debug)] -pub enum LifetimeUseSet<'tcx> { - One(&'tcx hir::Lifetime), - Many, -} - trait RegionExt { fn early(hir_map: Map<'_>, index: &mut u32, param: &GenericParam<'_>) -> (ParamName, Region); @@ -159,8 +151,8 @@ struct NamedRegionMap { scope_for_path: Option<FxHashMap<LocalDefId, FxHashMap<ItemLocalId, LifetimeScopeForPath>>>, } -crate struct LifetimeContext<'a, 'tcx> { - crate tcx: TyCtxt<'tcx>, +pub(crate) struct LifetimeContext<'a, 'tcx> { + pub(crate) tcx: TyCtxt<'tcx>, map: &'a mut NamedRegionMap, scope: ScopeRef<'a>, @@ -175,11 +167,9 @@ crate struct LifetimeContext<'a, 'tcx> { /// Cache for cross-crate per-definition object lifetime defaults. xcrate_object_lifetime_defaults: DefIdMap<Vec<ObjectLifetimeDefault>>, - lifetime_uses: &'a mut DefIdMap<LifetimeUseSet<'tcx>>, - /// When encountering an undefined named lifetime, we will suggest introducing it in these /// places. - crate missing_named_lifetime_spots: Vec<MissingLifetimeSpot<'tcx>>, + pub(crate) missing_named_lifetime_spots: Vec<MissingLifetimeSpot<'tcx>>, } #[derive(Debug)] @@ -197,11 +187,6 @@ enum Scope<'a> { /// we should use for an early-bound region? next_early_index: u32, - /// Flag is set to true if, in this binder, `'_` would be - /// equivalent to a "single-use region". This is true on - /// impls, but not other kinds of items. - track_lifetime_uses: bool, - /// Whether or not this binder would serve as the parent /// binder for opaque types introduced within. For example: /// @@ -297,7 +282,6 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { Scope::Binder { lifetimes, next_early_index, - track_lifetime_uses, opaque_type_parent, scope_type, hir_id, @@ -307,7 +291,6 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { .debug_struct("Binder") .field("lifetimes", lifetimes) .field("next_early_index", next_early_index) - .field("track_lifetime_uses", track_lifetime_uses) .field("opaque_type_parent", opaque_type_parent) .field("scope_type", scope_type) .field("hir_id", hir_id) @@ -352,14 +335,14 @@ enum Elide { } #[derive(Clone, Debug)] -crate struct ElisionFailureInfo { +pub(crate) struct ElisionFailureInfo { /// Where we can find the argument pattern. - crate parent: Option<hir::BodyId>, + pub(crate) parent: Option<hir::BodyId>, /// The index of the argument in the original definition. - crate index: usize, - crate lifetime_count: usize, - crate have_bound_regions: bool, - crate span: Span, + pub(crate) index: usize, + pub(crate) lifetime_count: usize, + pub(crate) have_bound_regions: bool, + pub(crate) span: Span, } type ScopeRef<'a> = &'a Scope<'a>; @@ -401,7 +384,7 @@ pub fn provide(providers: &mut ty::query::Providers) { /// The reason for this separate call is to resolve what would otherwise /// be a cycle. Consider this example: /// -/// ```rust +/// ```ignore UNSOLVED (maybe @jackh726 knows what lifetime parameter to give Sub) /// trait Base<'a> { /// type BaseItem; /// } @@ -453,7 +436,6 @@ fn do_resolve( trait_definition_only, labels_in_fn: vec![], xcrate_object_lifetime_defaults: Default::default(), - lifetime_uses: &mut Default::default(), missing_named_lifetime_spots: vec![], }; visitor.visit_item(item); @@ -697,7 +679,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { lifetimes: FxIndexMap::default(), next_early_index: self.next_early_index(), s: self.scope, - track_lifetime_uses: true, opaque_type_parent: false, scope_type: BinderScopeType::Normal, allow_late_bound: true, @@ -796,9 +777,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { | hir::ItemKind::Impl(hir::Impl { ref generics, .. }) => { self.missing_named_lifetime_spots.push(generics.into()); - // Impls permit `'_` to be used and it is equivalent to "some fresh lifetime name". - // This is not true for other kinds of items. - let track_lifetime_uses = matches!(item.kind, hir::ItemKind::Impl { .. }); // These kinds of items have only early-bound lifetime parameters. let mut index = if sub_items_have_self_param(&item.kind) { 1 // Self comes before lifetimes @@ -825,7 +803,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { lifetimes, next_early_index: index + non_lifetime_count, opaque_type_parent: true, - track_lifetime_uses, scope_type: BinderScopeType::Normal, s: ROOT_SCOPE, allow_late_bound: false, @@ -892,7 +869,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { lifetimes, s: self.scope, next_early_index, - track_lifetime_uses: true, opaque_type_parent: false, scope_type: BinderScopeType::Normal, allow_late_bound: true, @@ -1025,6 +1001,20 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } self.uninsert_lifetime_on_error(lifetime, def.unwrap()); } + if let hir::Node::Item(hir::Item { + kind: hir::ItemKind::OpaqueTy { .. }, .. + }) = self.tcx.hir().get(parent_id) + { + if !self.trait_definition_only { + let mut err = self.tcx.sess.struct_span_err( + lifetime.span, + "higher kinded lifetime bounds on nested opaque types are not supported yet", + ); + err.span_note(self.tcx.def_span(def_id), "lifetime declared here"); + err.emit(); + } + self.uninsert_lifetime_on_error(lifetime, def.unwrap()); + } } // We want to start our early-bound indices at the end of the parent scope, @@ -1039,11 +1029,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { match param.kind { GenericParamKind::Lifetime { .. } => { let (name, reg) = Region::early(self.tcx.hir(), &mut index, ¶m); - let Region::EarlyBound(_, def_id) = reg else { - bug!(); - }; - // We cannot predict what lifetimes are unused in opaque type. - self.lifetime_uses.insert(def_id, LifetimeUseSet::Many); if let hir::ParamName::Plain(Ident { name: kw::UnderscoreLifetime, .. @@ -1073,7 +1058,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { lifetimes, next_early_index, s: this.scope, - track_lifetime_uses: true, opaque_type_parent: false, scope_type: BinderScopeType::Normal, allow_late_bound: false, @@ -1094,7 +1078,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { lifetimes, next_early_index, s: self.scope, - track_lifetime_uses: true, opaque_type_parent: false, scope_type: BinderScopeType::Normal, allow_late_bound: false, @@ -1154,7 +1137,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { lifetimes, next_early_index: index + non_lifetime_count, s: self.scope, - track_lifetime_uses: true, opaque_type_parent: true, scope_type: BinderScopeType::Normal, allow_late_bound: false, @@ -1224,7 +1206,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { lifetimes, next_early_index: index + non_lifetime_count, s: self.scope, - track_lifetime_uses: true, opaque_type_parent: true, scope_type: BinderScopeType::Normal, allow_late_bound: true, @@ -1369,7 +1350,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { lifetimes, s: this.scope, next_early_index, - track_lifetime_uses: true, opaque_type_parent: false, scope_type: BinderScopeType::Normal, allow_late_bound: true, @@ -1443,7 +1423,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { lifetimes: FxIndexMap::default(), s: self.scope, next_early_index: self.next_early_index(), - track_lifetime_uses: true, opaque_type_parent: false, scope_type, allow_late_bound: true, @@ -1496,7 +1475,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { lifetimes, s: self.scope, next_early_index, - track_lifetime_uses: true, opaque_type_parent: false, scope_type, allow_late_bound: true, @@ -1798,7 +1776,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { where F: for<'b> FnOnce(ScopeRef<'_>, &mut LifetimeContext<'b, 'tcx>), { - let LifetimeContext { tcx, map, lifetime_uses, .. } = self; + let LifetimeContext { tcx, map, .. } = self; let labels_in_fn = take(&mut self.labels_in_fn); let xcrate_object_lifetime_defaults = take(&mut self.xcrate_object_lifetime_defaults); let missing_named_lifetime_spots = take(&mut self.missing_named_lifetime_spots); @@ -1809,303 +1787,18 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { trait_definition_only: self.trait_definition_only, labels_in_fn, xcrate_object_lifetime_defaults, - lifetime_uses, missing_named_lifetime_spots, }; let span = tracing::debug_span!("scope", scope = ?TruncatedScopeDebug(&this.scope)); { let _enter = span.enter(); f(self.scope, &mut this); - if !self.trait_definition_only { - this.check_uses_for_lifetimes_defined_by_scope(); - } } self.labels_in_fn = this.labels_in_fn; self.xcrate_object_lifetime_defaults = this.xcrate_object_lifetime_defaults; self.missing_named_lifetime_spots = this.missing_named_lifetime_spots; } - /// helper method to determine the span to remove when suggesting the - /// deletion of a lifetime - fn lifetime_deletion_span(&self, name: Ident, generics: &hir::Generics<'_>) -> Option<Span> { - generics.params.iter().enumerate().find_map(|(i, param)| { - if param.name.ident() == name { - if generics.params.len() == 1 { - // if sole lifetime, remove the entire `<>` brackets - Some(generics.span) - } else { - // if removing within `<>` brackets, we also want to - // delete a leading or trailing comma as appropriate - if i >= generics.params.len() - 1 { - Some(generics.params[i - 1].span.shrink_to_hi().to(param.span)) - } else { - Some(param.span.to(generics.params[i + 1].span.shrink_to_lo())) - } - } - } else { - None - } - }) - } - - // helper method to issue suggestions from `fn rah<'a>(&'a T)` to `fn rah(&T)` - // or from `fn rah<'a>(T<'a>)` to `fn rah(T<'_>)` - fn suggest_eliding_single_use_lifetime( - &self, - err: &mut Diagnostic, - def_id: DefId, - lifetime: &hir::Lifetime, - ) { - let name = lifetime.name.ident(); - let remove_decl = self - .tcx - .parent(def_id) - .as_local() - .and_then(|parent_def_id| self.tcx.hir().get_generics(parent_def_id)) - .and_then(|generics| self.lifetime_deletion_span(name, generics)); - - let mut remove_use = None; - let mut elide_use = None; - let mut find_arg_use_span = |inputs: &[hir::Ty<'_>]| { - for input in inputs { - match input.kind { - hir::TyKind::Rptr(lt, _) => { - if lt.name.ident() == name { - // include the trailing whitespace between the lifetime and type names - let lt_through_ty_span = lifetime.span.to(input.span.shrink_to_hi()); - remove_use = Some( - self.tcx - .sess - .source_map() - .span_until_non_whitespace(lt_through_ty_span), - ); - break; - } - } - hir::TyKind::Path(QPath::Resolved(_, path)) => { - let last_segment = &path.segments[path.segments.len() - 1]; - let generics = last_segment.args(); - for arg in generics.args.iter() { - if let GenericArg::Lifetime(lt) = arg { - if lt.name.ident() == name { - elide_use = Some(lt.span); - break; - } - } - } - break; - } - _ => {} - } - } - }; - if let Node::Lifetime(hir_lifetime) = self.tcx.hir().get(lifetime.hir_id) { - if let Some(parent) = - self.tcx.hir().find_by_def_id(self.tcx.hir().get_parent_item(hir_lifetime.hir_id)) - { - match parent { - Node::Item(item) => { - if let hir::ItemKind::Fn(sig, _, _) = &item.kind { - find_arg_use_span(sig.decl.inputs); - } - } - Node::ImplItem(impl_item) => { - if let hir::ImplItemKind::Fn(sig, _) = &impl_item.kind { - find_arg_use_span(sig.decl.inputs); - } - } - _ => {} - } - } - } - - let msg = "elide the single-use lifetime"; - match (remove_decl, remove_use, elide_use) { - (Some(decl_span), Some(use_span), None) => { - // if both declaration and use deletion spans start at the same - // place ("start at" because the latter includes trailing - // whitespace), then this is an in-band lifetime - if decl_span.shrink_to_lo() == use_span.shrink_to_lo() { - err.span_suggestion( - use_span, - msg, - String::new(), - Applicability::MachineApplicable, - ); - } else { - err.multipart_suggestion( - msg, - vec![(decl_span, String::new()), (use_span, String::new())], - Applicability::MachineApplicable, - ); - } - } - (Some(decl_span), None, Some(use_span)) => { - err.multipart_suggestion( - msg, - vec![(decl_span, String::new()), (use_span, "'_".to_owned())], - Applicability::MachineApplicable, - ); - } - _ => {} - } - } - - fn check_uses_for_lifetimes_defined_by_scope(&mut self) { - let Scope::Binder { lifetimes: defined_by, .. } = self.scope else { - debug!("check_uses_for_lifetimes_defined_by_scope: not in a binder scope"); - return; - }; - - let def_ids: Vec<_> = defined_by - .values() - .flat_map(|region| match region { - Region::EarlyBound(_, def_id) - | Region::LateBound(_, _, def_id) - | Region::Free(_, def_id) => Some(*def_id), - - Region::LateBoundAnon(..) | Region::Static => None, - }) - .collect(); - - 'lifetimes: for def_id in def_ids { - debug!("check_uses_for_lifetimes_defined_by_scope: def_id = {:?}", def_id); - - let lifetimeuseset = self.lifetime_uses.remove(&def_id); - - debug!( - "check_uses_for_lifetimes_defined_by_scope: lifetimeuseset = {:?}", - lifetimeuseset - ); - - match lifetimeuseset { - Some(LifetimeUseSet::One(lifetime)) => { - debug!(?def_id); - if let Some((id, span, name)) = - match self.tcx.hir().get_by_def_id(def_id.expect_local()) { - Node::Lifetime(hir_lifetime) => Some(( - hir_lifetime.hir_id, - hir_lifetime.span, - hir_lifetime.name.ident(), - )), - Node::GenericParam(param) => { - Some((param.hir_id, param.span, param.name.ident())) - } - _ => None, - } - { - debug!("id = {:?} span = {:?} name = {:?}", id, span, name); - if name.name == kw::UnderscoreLifetime { - continue; - } - - let parent_def_id = self.tcx.parent(def_id); - if let Some(def_id) = parent_def_id.as_local() { - // lifetimes in `derive` expansions don't count (Issue #53738) - if self - .tcx - .get_attrs(def_id.to_def_id()) - .iter() - .any(|attr| attr.has_name(sym::automatically_derived)) - { - continue; - } - - // opaque types generated when desugaring an async function can have a single - // use lifetime even if it is explicitly denied (Issue #77175) - if let hir::Node::Item(hir::Item { - kind: hir::ItemKind::OpaqueTy(ref opaque), - .. - }) = self.tcx.hir().get_by_def_id(def_id) - { - if !matches!(opaque.origin, hir::OpaqueTyOrigin::AsyncFn(..)) { - continue 'lifetimes; - } - // We want to do this only if the lifetime identifier is already defined - // in the async function that generated this. Otherwise it could be - // an opaque type defined by the developer and we still want this - // lint to fail compilation - for p in opaque.generics.params { - if defined_by.contains_key(&p.name) { - continue 'lifetimes; - } - } - } - } - - self.tcx.struct_span_lint_hir( - lint::builtin::SINGLE_USE_LIFETIMES, - id, - span, - |lint| { - let mut err = lint.build(&format!( - "lifetime parameter `{}` only used once", - name - )); - if span == lifetime.span { - // spans are the same for in-band lifetime declarations - err.span_label(span, "this lifetime is only used here"); - } else { - err.span_label(span, "this lifetime..."); - err.span_label(lifetime.span, "...is used only here"); - } - self.suggest_eliding_single_use_lifetime( - &mut err, def_id, lifetime, - ); - err.emit(); - }, - ); - } - } - Some(LifetimeUseSet::Many) => { - debug!("not one use lifetime"); - } - None => { - if let Some((id, span, name)) = - match self.tcx.hir().get_by_def_id(def_id.expect_local()) { - Node::Lifetime(hir_lifetime) => Some(( - hir_lifetime.hir_id, - hir_lifetime.span, - hir_lifetime.name.ident(), - )), - Node::GenericParam(param) => { - Some((param.hir_id, param.span, param.name.ident())) - } - _ => None, - } - { - debug!("id ={:?} span = {:?} name = {:?}", id, span, name); - self.tcx.struct_span_lint_hir( - lint::builtin::UNUSED_LIFETIMES, - id, - span, - |lint| { - let mut err = lint - .build(&format!("lifetime parameter `{}` never used", name)); - let parent_def_id = self.tcx.parent(def_id); - if let Some(generics) = - self.tcx.hir().get_generics(parent_def_id.expect_local()) - { - let unused_lt_span = - self.lifetime_deletion_span(name, generics); - if let Some(span) = unused_lt_span { - err.span_suggestion( - span, - "elide the unused lifetime", - String::new(), - Applicability::MachineApplicable, - ); - } - } - err.emit(); - }, - ); - } - } - } - } - } - /// Visits self by adding a scope and handling recursive walk over the contents with `walk`. /// /// Handles visiting fns and methods. These are a bit complicated because we must distinguish @@ -2195,7 +1888,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { next_early_index, s: self.scope, opaque_type_parent: true, - track_lifetime_uses: false, scope_type: BinderScopeType::Normal, allow_late_bound: true, }; @@ -2546,7 +2238,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { /// Returns all the late-bound vars that come into scope from supertrait HRTBs, based on the /// associated type name and starting trait. /// For example, imagine we have - /// ```rust + /// ```ignore (illustrative) /// trait Foo<'a, 'b> { /// type As; /// } @@ -3192,41 +2884,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } } - /// Returns `true` if, in the current scope, replacing `'_` would be - /// equivalent to a single-use lifetime. - fn track_lifetime_uses(&self) -> bool { - let mut scope = self.scope; - loop { - match *scope { - Scope::Root => break false, - - // Inside of items, it depends on the kind of item. - Scope::Binder { track_lifetime_uses, .. } => break track_lifetime_uses, - - // Inside a body, `'_` will use an inference variable, - // should be fine. - Scope::Body { .. } => break true, - - // A lifetime only used in a fn argument could as well - // be replaced with `'_`, as that would generate a - // fresh name, too. - Scope::Elision { elide: Elide::FreshLateAnon(..), .. } => break true, - - // In the return type or other such place, `'_` is not - // going to make a fresh name, so we cannot - // necessarily replace a single-use lifetime with - // `'_`. - Scope::Elision { - elide: Elide::Exact(_) | Elide::Error(_) | Elide::Forbid, .. - } => break false, - - Scope::ObjectLifetimeDefault { s, .. } - | Scope::Supertrait { s, .. } - | Scope::TraitRefBoundary { s, .. } => scope = s, - } - } - } - #[tracing::instrument(level = "debug", skip(self))] fn insert_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime, def: Region) { debug!( @@ -3234,27 +2891,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { span = ?self.tcx.sess.source_map().span_to_diagnostic_string(lifetime_ref.span) ); self.map.defs.insert(lifetime_ref.hir_id, def); - - match def { - Region::LateBoundAnon(..) | Region::Static => { - // These are anonymous lifetimes or lifetimes that are not declared. - } - - Region::Free(_, def_id) - | Region::LateBound(_, _, def_id) - | Region::EarlyBound(_, def_id) => { - // A lifetime declared by the user. - let track_lifetime_uses = self.track_lifetime_uses(); - debug!(?track_lifetime_uses); - if track_lifetime_uses && !self.lifetime_uses.contains_key(&def_id) { - debug!("first use of {:?}", def_id); - self.lifetime_uses.insert(def_id, LifetimeUseSet::One(lifetime_ref)); - } else { - debug!("many uses of {:?}", def_id); - self.lifetime_uses.insert(def_id, LifetimeUseSet::Many); - } - } - } } /// Sometimes we resolve a lifetime, but later find that it is an diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 62485beac47..73c8a9d28bd 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -9,7 +9,6 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(box_patterns)] #![feature(drain_filter)] -#![feature(crate_visibility_modifier)] #![feature(if_let_guard)] #![feature(let_chains)] #![feature(let_else)] @@ -59,7 +58,7 @@ use rustc_span::{Span, DUMMY_SP}; use smallvec::{smallvec, SmallVec}; use std::cell::{Cell, RefCell}; use std::collections::BTreeSet; -use std::{cmp, fmt, mem, ptr}; +use std::{cmp, fmt, ptr}; use tracing::debug; use diagnostics::{ImportSuggestion, LabelSuggestion, Suggestion}; @@ -966,8 +965,6 @@ pub struct Resolver<'a> { registered_attrs: FxHashSet<Ident>, registered_tools: RegisteredTools, macro_use_prelude: FxHashMap<Symbol, &'a NameBinding<'a>>, - /// FIXME: The only user of this is a doc link resolution hack for rustdoc. - all_macro_rules: FxHashMap<Symbol, Res>, macro_map: FxHashMap<DefId, Lrc<SyntaxExtension>>, dummy_ext_bang: Lrc<SyntaxExtension>, dummy_ext_derive: Lrc<SyntaxExtension>, @@ -975,6 +972,7 @@ pub struct Resolver<'a> { local_macro_def_scopes: FxHashMap<LocalDefId, Module<'a>>, ast_transform_scopes: FxHashMap<LocalExpnId, Module<'a>>, unused_macros: FxHashMap<LocalDefId, (NodeId, Ident)>, + unused_macro_rules: FxHashMap<(LocalDefId, usize), (Ident, Span)>, proc_macro_stubs: FxHashSet<LocalDefId>, /// Traces collected during macro resolution and validated when it's complete. single_segment_macro_resolutions: @@ -1359,7 +1357,6 @@ impl<'a> Resolver<'a> { registered_attrs, registered_tools, macro_use_prelude: FxHashMap::default(), - all_macro_rules: Default::default(), macro_map: FxHashMap::default(), dummy_ext_bang: Lrc::new(SyntaxExtension::dummy_bang(session.edition())), dummy_ext_derive: Lrc::new(SyntaxExtension::dummy_derive(session.edition())), @@ -1374,6 +1371,7 @@ impl<'a> Resolver<'a> { potentially_unused_imports: Vec::new(), struct_constructors: Default::default(), unused_macros: Default::default(), + unused_macro_rules: Default::default(), proc_macro_stubs: Default::default(), single_segment_macro_resolutions: Default::default(), multi_segment_macro_resolutions: Default::default(), @@ -1910,11 +1908,6 @@ impl<'a> Resolver<'a> { } } - // For rustdoc. - pub fn take_all_macro_rules(&mut self) -> FxHashMap<Symbol, Res> { - mem::take(&mut self.all_macro_rules) - } - /// For rustdoc. /// For local modules returns only reexports, for external modules returns all children. pub fn module_children_or_reexports(&self, def_id: DefId) -> Vec<ModChild> { @@ -1926,8 +1919,12 @@ impl<'a> Resolver<'a> { } /// For rustdoc. - pub fn macro_rules_scope(&self, def_id: LocalDefId) -> MacroRulesScopeRef<'a> { - *self.macro_rules_scopes.get(&def_id).expect("not a `macro_rules` item") + pub fn macro_rules_scope(&self, def_id: LocalDefId) -> (MacroRulesScopeRef<'a>, Res) { + let scope = *self.macro_rules_scopes.get(&def_id).expect("not a `macro_rules` item"); + match scope.get() { + MacroRulesScope::Binding(mb) => (scope, mb.binding.res()), + _ => unreachable!(), + } } /// Retrieves the span of the given `DefId` if `DefId` is in the local crate. diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 19a9c1b99fc..7e6375968ae 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -22,7 +22,8 @@ use rustc_hir::def::{self, DefKind, NonMacroAttrKind}; use rustc_hir::def_id::{CrateNum, LocalDefId}; use rustc_middle::middle::stability; use rustc_middle::ty::RegisteredTools; -use rustc_session::lint::builtin::{LEGACY_DERIVE_HELPERS, SOFT_UNSTABLE, UNUSED_MACROS}; +use rustc_session::lint::builtin::{LEGACY_DERIVE_HELPERS, SOFT_UNSTABLE}; +use rustc_session::lint::builtin::{UNUSED_MACROS, UNUSED_MACRO_RULES}; use rustc_session::lint::BuiltinLintDiagnostics; use rustc_session::parse::feature_err; use rustc_session::Session; @@ -40,10 +41,10 @@ type Res = def::Res<NodeId>; /// Not modularized, can shadow previous `macro_rules` bindings, etc. #[derive(Debug)] pub struct MacroRulesBinding<'a> { - crate binding: &'a NameBinding<'a>, + pub(crate) binding: &'a NameBinding<'a>, /// `macro_rules` scope into which the `macro_rules` item was planted. - crate parent_macro_rules_scope: MacroRulesScopeRef<'a>, - crate ident: Ident, + pub(crate) parent_macro_rules_scope: MacroRulesScopeRef<'a>, + pub(crate) ident: Ident, } /// The scope introduced by a `macro_rules!` macro. @@ -73,7 +74,10 @@ pub(crate) type MacroRulesScopeRef<'a> = Interned<'a, Cell<MacroRulesScope<'a>>> /// Macro namespace is separated into two sub-namespaces, one for bang macros and /// one for attribute-like macros (attributes, derives). /// We ignore resolutions from one sub-namespace when searching names in scope for another. -crate fn sub_namespace_match(candidate: Option<MacroKind>, requirement: Option<MacroKind>) -> bool { +pub(crate) fn sub_namespace_match( + candidate: Option<MacroKind>, + requirement: Option<MacroKind>, +) -> bool { #[derive(PartialEq)] enum SubNS { Bang, @@ -139,7 +143,7 @@ fn registered_idents( registered } -crate fn registered_attrs_and_tools( +pub(crate) fn registered_attrs_and_tools( sess: &Session, attrs: &[ast::Attribute], ) -> (FxHashSet<Ident>, FxHashSet<Ident>) { @@ -311,6 +315,11 @@ impl<'a> ResolverExpand for Resolver<'a> { Ok(ext) } + fn record_macro_rule_usage(&mut self, id: NodeId, rule_i: usize) { + let did = self.local_def_id(id); + self.unused_macro_rules.remove(&(did, rule_i)); + } + fn check_unused_macros(&mut self) { for (_, &(node_id, ident)) in self.unused_macros.iter() { self.lint_buffer.buffer_lint( @@ -320,6 +329,23 @@ impl<'a> ResolverExpand for Resolver<'a> { &format!("unused macro definition: `{}`", ident.as_str()), ); } + for (&(def_id, arm_i), &(ident, rule_span)) in self.unused_macro_rules.iter() { + if self.unused_macros.contains_key(&def_id) { + // We already lint the entire macro as unused + continue; + } + let node_id = self.def_id_to_node_id[def_id]; + self.lint_buffer.buffer_lint( + UNUSED_MACRO_RULES, + node_id, + rule_span, + &format!( + "{} rule of macro `{}` is never used", + crate::diagnostics::ordinalize(arm_i + 1), + ident.as_str() + ), + ); + } } fn has_derive_copy(&self, expn_id: LocalExpnId) -> bool { @@ -628,7 +654,7 @@ impl<'a> Resolver<'a> { res.map(|res| (self.get_macro(res), res)) } - crate fn finalize_macro_resolutions(&mut self) { + pub(crate) fn finalize_macro_resolutions(&mut self) { let check_consistency = |this: &mut Self, path: &[Segment], span, @@ -816,7 +842,7 @@ impl<'a> Resolver<'a> { } } - crate fn check_reserved_macro_name(&mut self, ident: Ident, res: Res) { + pub(crate) fn check_reserved_macro_name(&mut self, ident: Ident, res: Res) { // Reserve some names that are not quite covered by the general check // performed on `Resolver::builtin_attrs`. if ident.name == sym::cfg || ident.name == sym::cfg_attr { @@ -830,10 +856,15 @@ impl<'a> Resolver<'a> { } } - /// Compile the macro into a `SyntaxExtension` and possibly replace - /// its expander to a pre-defined one for built-in macros. - crate fn compile_macro(&mut self, item: &ast::Item, edition: Edition) -> SyntaxExtension { - let mut result = compile_declarative_macro( + /// Compile the macro into a `SyntaxExtension` and its rule spans. + /// + /// Possibly replace its expander to a pre-defined one for built-in macros. + pub(crate) fn compile_macro( + &mut self, + item: &ast::Item, + edition: Edition, + ) -> (SyntaxExtension, Vec<Span>) { + let (mut result, mut rule_spans) = compile_declarative_macro( &self.session, self.session.features_untracked(), item, @@ -849,6 +880,7 @@ impl<'a> Resolver<'a> { match mem::replace(builtin_macro, BuiltinMacroState::AlreadySeen(item.span)) { BuiltinMacroState::NotYetSeen(ext) => { result.kind = ext; + rule_spans = Vec::new(); if item.id != ast::DUMMY_NODE_ID { self.builtin_macro_kinds .insert(self.local_def_id(item.id), result.macro_kind()); @@ -871,6 +903,6 @@ impl<'a> Resolver<'a> { } } - result + (result, rule_spans) } } diff --git a/compiler/rustc_save_analysis/src/dump_visitor.rs b/compiler/rustc_save_analysis/src/dump_visitor.rs index e1c9ecc055f..fe417f45e88 100644 --- a/compiler/rustc_save_analysis/src/dump_visitor.rs +++ b/compiler/rustc_save_analysis/src/dump_visitor.rs @@ -780,13 +780,18 @@ impl<'tcx> DumpVisitor<'tcx> { variant: &'tcx ty::VariantDef, rest: Option<&'tcx hir::Expr<'tcx>>, ) { - if let Some(struct_lit_data) = self.save_ctxt.get_expr_data(ex) { + if let Some(_ex_res_data) = self.save_ctxt.get_expr_data(ex) { if let hir::QPath::Resolved(_, path) = path { self.write_sub_paths_truncated(path); } - down_cast_data!(struct_lit_data, RefData, ex.span); + // For MyEnum::MyVariant, get_expr_data gives us MyEnum, not MyVariant. + // For recording the span's ref id, we want MyVariant. if !generated_code(ex.span) { - self.dumper.dump_ref(struct_lit_data); + let sub_span = path.last_segment_span(); + let span = self.save_ctxt.span_from_span(sub_span); + let reff = + Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(variant.def_id) }; + self.dumper.dump_ref(reff); } for field in fields { diff --git a/compiler/rustc_serialize/src/collection_impls.rs b/compiler/rustc_serialize/src/collection_impls.rs index dee6dc010fe..761e988360a 100644 --- a/compiler/rustc_serialize/src/collection_impls.rs +++ b/compiler/rustc_serialize/src/collection_impls.rs @@ -171,16 +171,6 @@ where } } -impl<E: Encoder, T, S> Encodable<E> for &HashSet<T, S> -where - T: Encodable<E> + Eq, - S: BuildHasher, -{ - fn encode(&self, s: &mut E) -> Result<(), E::Error> { - (**self).encode(s) - } -} - impl<D: Decoder, T, S> Decodable<D> for HashSet<T, S> where T: Decodable<D> + Hash + Eq, diff --git a/compiler/rustc_serialize/src/serialize.rs b/compiler/rustc_serialize/src/serialize.rs index 7d6b8c760ff..36e575b2427 100644 --- a/compiler/rustc_serialize/src/serialize.rs +++ b/compiler/rustc_serialize/src/serialize.rs @@ -268,6 +268,15 @@ direct_serialize_impls! { char emit_char read_char } +impl<S: Encoder, T: ?Sized> Encodable<S> for &T +where + T: Encodable<S>, +{ + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + (**self).encode(s) + } +} + impl<S: Encoder> Encodable<S> for ! { fn encode(&self, _s: &mut S) -> Result<(), S::Error> { unreachable!() @@ -298,12 +307,6 @@ impl<S: Encoder> Encodable<S> for str { } } -impl<S: Encoder> Encodable<S> for &str { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_str(self) - } -} - impl<S: Encoder> Encodable<S> for String { fn encode(&self, s: &mut S) -> Result<(), S::Error> { s.emit_str(&self[..]) diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 530c1a06f8f..997f361737b 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1912,7 +1912,7 @@ fn select_debuginfo( } } -crate fn parse_assert_incr_state( +pub(crate) fn parse_assert_incr_state( opt_assertion: &Option<String>, error_format: ErrorOutputType, ) -> Option<IncrementalStateAssertion> { @@ -1937,33 +1937,27 @@ fn parse_native_lib_kind( }; let kind = match kind { - "dylib" => NativeLibKind::Dylib { as_needed: None }, - "framework" => NativeLibKind::Framework { as_needed: None }, "static" => NativeLibKind::Static { bundle: None, whole_archive: None }, "static-nobundle" => { early_warn( error_format, "library kind `static-nobundle` has been superseded by specifying \ - `-bundle` on library kind `static`. Try `static:-bundle`", + modifier `-bundle` with library kind `static`. Try `static:-bundle`", ); - if modifiers.is_some() { - early_error( - error_format, - "linking modifier can't be used with library kind `static-nobundle`", - ) - } if !nightly_options::match_is_nightly_build(matches) { early_error( error_format, - "library kind `static-nobundle` are currently unstable and only accepted on \ - the nightly compiler", + "library kind `static-nobundle` is unstable \ + and only accepted on the nightly compiler", ); } NativeLibKind::Static { bundle: Some(false), whole_archive: None } } - s => early_error( + "dylib" => NativeLibKind::Dylib { as_needed: None }, + "framework" => NativeLibKind::Framework { as_needed: None }, + _ => early_error( error_format, - &format!("unknown library kind `{s}`, expected one of dylib, framework, or static"), + &format!("unknown library kind `{kind}`, expected one of: static, dylib, framework"), ), }; match modifiers { @@ -1978,21 +1972,6 @@ fn parse_native_lib_modifiers( error_format: ErrorOutputType, matches: &getopts::Matches, ) -> (NativeLibKind, Option<bool>) { - let report_unstable_modifier = |modifier| { - if !nightly_options::is_unstable_enabled(matches) { - let why = if nightly_options::match_is_nightly_build(matches) { - " and only accepted on the nightly compiler" - } else { - ", the `-Z unstable-options` flag must also be passed to use it" - }; - early_error( - error_format, - &format!("{modifier} linking modifier is currently unstable{why}"), - ) - } - }; - - let mut has_duplicate_modifiers = false; let mut verbatim = None; for modifier in modifiers.split(',') { let (modifier, value) = match modifier.strip_prefix(&['+', '-']) { @@ -2000,56 +1979,63 @@ fn parse_native_lib_modifiers( None => early_error( error_format, "invalid linking modifier syntax, expected '+' or '-' prefix \ - before one of: bundle, verbatim, whole-archive, as-needed", + before one of: bundle, verbatim, whole-archive, as-needed", ), }; + let report_unstable_modifier = || { + if !nightly_options::is_unstable_enabled(matches) { + let why = if nightly_options::match_is_nightly_build(matches) { + " and only accepted on the nightly compiler" + } else { + ", the `-Z unstable-options` flag must also be passed to use it" + }; + early_error( + error_format, + &format!("linking modifier `{modifier}` is unstable{why}"), + ) + } + }; + let assign_modifier = |dst: &mut Option<bool>| { + if dst.is_some() { + let msg = format!("multiple `{modifier}` modifiers in a single `-l` option"); + early_error(error_format, &msg) + } else { + *dst = Some(value); + } + }; match (modifier, &mut kind) { ("bundle", NativeLibKind::Static { bundle, .. }) => { - report_unstable_modifier(modifier); - if bundle.is_some() { - has_duplicate_modifiers = true; - } - *bundle = Some(value); + report_unstable_modifier(); + assign_modifier(bundle) } ("bundle", _) => early_error( error_format, - "bundle linking modifier is only compatible with \ - `static` linking kind", + "linking modifier `bundle` is only compatible with `static` linking kind", ), ("verbatim", _) => { - report_unstable_modifier(modifier); - if verbatim.is_some() { - has_duplicate_modifiers = true; - } - verbatim = Some(value); + report_unstable_modifier(); + assign_modifier(&mut verbatim) } ("whole-archive", NativeLibKind::Static { whole_archive, .. }) => { - if whole_archive.is_some() { - has_duplicate_modifiers = true; - } - *whole_archive = Some(value); + assign_modifier(whole_archive) } ("whole-archive", _) => early_error( error_format, - "whole-archive linking modifier is only compatible with \ - `static` linking kind", + "linking modifier `whole-archive` is only compatible with `static` linking kind", ), ("as-needed", NativeLibKind::Dylib { as_needed }) | ("as-needed", NativeLibKind::Framework { as_needed }) => { - report_unstable_modifier(modifier); - if as_needed.is_some() { - has_duplicate_modifiers = true; - } - *as_needed = Some(value); + report_unstable_modifier(); + assign_modifier(as_needed) } ("as-needed", _) => early_error( error_format, - "as-needed linking modifier is only compatible with \ - `dylib` and `framework` linking kinds", + "linking modifier `as-needed` is only compatible with \ + `dylib` and `framework` linking kinds", ), // Note: this error also excludes the case with empty modifier @@ -2057,15 +2043,12 @@ fn parse_native_lib_modifiers( _ => early_error( error_format, &format!( - "unrecognized linking modifier `{modifier}`, expected one \ - of: bundle, verbatim, whole-archive, as-needed" + "unknown linking modifier `{modifier}`, expected one \ + of: bundle, verbatim, whole-archive, as-needed" ), ), } } - if has_duplicate_modifiers { - report_unstable_modifier("duplicating") - } (kind, verbatim) } @@ -2093,6 +2076,9 @@ fn parse_libs(matches: &getopts::Matches, error_format: ErrorOutputType) -> Vec< None => (name, None), Some((name, new_name)) => (name.to_string(), Some(new_name.to_owned())), }; + if name.is_empty() { + early_error(error_format, "library name must not be empty"); + } NativeLib { name, new_name, kind, verbatim } }) .collect() @@ -2769,7 +2755,7 @@ impl PpMode { /// `Hash` implementation for `DepTrackingHash`. It's important though that /// we have an opt-in scheme here, so one is hopefully forced to think about /// how the hash should be calculated when adding a new command-line argument. -crate mod dep_tracking { +pub(crate) mod dep_tracking { use super::{ BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, ErrorOutputType, InstrumentCoverage, LdImpl, LinkerPluginLto, LocationDetail, LtoCli, OomStrategy, OptLevel, @@ -2947,7 +2933,7 @@ crate mod dep_tracking { } // This is a stable hash because BTreeMap is a sorted container - crate fn stable_hash( + pub(crate) fn stable_hash( sub_hashes: BTreeMap<&'static str, &dyn DepTrackingHash>, hasher: &mut DefaultHasher, error_format: ErrorOutputType, diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index 054b18b6b63..f84a154950f 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -1,4 +1,3 @@ -#![feature(crate_visibility_modifier)] #![feature(if_let_guard)] #![feature(let_chains)] #![cfg_attr(bootstrap, feature(derive_default_enum))] diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 14e918660dd..12e00ef5114 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -421,12 +421,12 @@ mod desc { } mod parse { - crate use super::*; + pub(crate) use super::*; use std::str::FromStr; /// This is for boolean options that don't take a value and start with /// `no-`. This style of option is deprecated. - crate fn parse_no_flag(slot: &mut bool, v: Option<&str>) -> bool { + pub(crate) fn parse_no_flag(slot: &mut bool, v: Option<&str>) -> bool { match v { None => { *slot = true; @@ -437,7 +437,7 @@ mod parse { } /// Use this for any boolean option that has a static default. - crate fn parse_bool(slot: &mut bool, v: Option<&str>) -> bool { + pub(crate) fn parse_bool(slot: &mut bool, v: Option<&str>) -> bool { match v { Some("y") | Some("yes") | Some("on") | None => { *slot = true; @@ -454,7 +454,7 @@ mod parse { /// Use this for any boolean option that lacks a static default. (The /// actions taken when such an option is not specified will depend on /// other factors, such as other options, or target options.) - crate fn parse_opt_bool(slot: &mut Option<bool>, v: Option<&str>) -> bool { + pub(crate) fn parse_opt_bool(slot: &mut Option<bool>, v: Option<&str>) -> bool { match v { Some("y") | Some("yes") | Some("on") | None => { *slot = Some(true); @@ -469,7 +469,7 @@ mod parse { } /// Use this for any string option that has a static default. - crate fn parse_string(slot: &mut String, v: Option<&str>) -> bool { + pub(crate) fn parse_string(slot: &mut String, v: Option<&str>) -> bool { match v { Some(s) => { *slot = s.to_string(); @@ -480,7 +480,7 @@ mod parse { } /// Use this for any string option that lacks a static default. - crate fn parse_opt_string(slot: &mut Option<String>, v: Option<&str>) -> bool { + pub(crate) fn parse_opt_string(slot: &mut Option<String>, v: Option<&str>) -> bool { match v { Some(s) => { *slot = Some(s.to_string()); @@ -491,7 +491,7 @@ mod parse { } /// Parse an optional language identifier, e.g. `en-US` or `zh-CN`. - crate fn parse_opt_langid(slot: &mut Option<LanguageIdentifier>, v: Option<&str>) -> bool { + pub(crate) fn parse_opt_langid(slot: &mut Option<LanguageIdentifier>, v: Option<&str>) -> bool { match v { Some(s) => { *slot = rustc_errors::LanguageIdentifier::from_str(s).ok(); @@ -501,7 +501,7 @@ mod parse { } } - crate fn parse_opt_pathbuf(slot: &mut Option<PathBuf>, v: Option<&str>) -> bool { + pub(crate) fn parse_opt_pathbuf(slot: &mut Option<PathBuf>, v: Option<&str>) -> bool { match v { Some(s) => { *slot = Some(PathBuf::from(s)); @@ -511,7 +511,7 @@ mod parse { } } - crate fn parse_string_push(slot: &mut Vec<String>, v: Option<&str>) -> bool { + pub(crate) fn parse_string_push(slot: &mut Vec<String>, v: Option<&str>) -> bool { match v { Some(s) => { slot.push(s.to_string()); @@ -521,7 +521,7 @@ mod parse { } } - crate fn parse_list(slot: &mut Vec<String>, v: Option<&str>) -> bool { + pub(crate) fn parse_list(slot: &mut Vec<String>, v: Option<&str>) -> bool { match v { Some(s) => { slot.extend(s.split_whitespace().map(|s| s.to_string())); @@ -531,7 +531,10 @@ mod parse { } } - crate fn parse_list_with_polarity(slot: &mut Vec<(String, bool)>, v: Option<&str>) -> bool { + pub(crate) fn parse_list_with_polarity( + slot: &mut Vec<(String, bool)>, + v: Option<&str>, + ) -> bool { match v { Some(s) => { for s in s.split(",") { @@ -544,7 +547,7 @@ mod parse { } } - crate fn parse_location_detail(ld: &mut LocationDetail, v: Option<&str>) -> bool { + pub(crate) fn parse_location_detail(ld: &mut LocationDetail, v: Option<&str>) -> bool { if let Some(v) = v { ld.line = false; ld.file = false; @@ -563,7 +566,7 @@ mod parse { } } - crate fn parse_opt_comma_list(slot: &mut Option<Vec<String>>, v: Option<&str>) -> bool { + pub(crate) fn parse_opt_comma_list(slot: &mut Option<Vec<String>>, v: Option<&str>) -> bool { match v { Some(s) => { let mut v: Vec<_> = s.split(',').map(|s| s.to_string()).collect(); @@ -575,7 +578,7 @@ mod parse { } } - crate fn parse_threads(slot: &mut usize, v: Option<&str>) -> bool { + pub(crate) fn parse_threads(slot: &mut usize, v: Option<&str>) -> bool { match v.and_then(|s| s.parse().ok()) { Some(0) => { *slot = std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get); @@ -590,7 +593,7 @@ mod parse { } /// Use this for any numeric option that has a static default. - crate fn parse_number<T: Copy + FromStr>(slot: &mut T, v: Option<&str>) -> bool { + pub(crate) fn parse_number<T: Copy + FromStr>(slot: &mut T, v: Option<&str>) -> bool { match v.and_then(|s| s.parse().ok()) { Some(i) => { *slot = i; @@ -601,7 +604,10 @@ mod parse { } /// Use this for any numeric option that lacks a static default. - crate fn parse_opt_number<T: Copy + FromStr>(slot: &mut Option<T>, v: Option<&str>) -> bool { + pub(crate) fn parse_opt_number<T: Copy + FromStr>( + slot: &mut Option<T>, + v: Option<&str>, + ) -> bool { match v { Some(s) => { *slot = s.parse().ok(); @@ -611,7 +617,7 @@ mod parse { } } - crate fn parse_passes(slot: &mut Passes, v: Option<&str>) -> bool { + pub(crate) fn parse_passes(slot: &mut Passes, v: Option<&str>) -> bool { match v { Some("all") => { *slot = Passes::All; @@ -629,7 +635,10 @@ mod parse { } } - crate fn parse_opt_panic_strategy(slot: &mut Option<PanicStrategy>, v: Option<&str>) -> bool { + pub(crate) fn parse_opt_panic_strategy( + slot: &mut Option<PanicStrategy>, + v: Option<&str>, + ) -> bool { match v { Some("unwind") => *slot = Some(PanicStrategy::Unwind), Some("abort") => *slot = Some(PanicStrategy::Abort), @@ -638,7 +647,7 @@ mod parse { true } - crate fn parse_panic_strategy(slot: &mut PanicStrategy, v: Option<&str>) -> bool { + pub(crate) fn parse_panic_strategy(slot: &mut PanicStrategy, v: Option<&str>) -> bool { match v { Some("unwind") => *slot = PanicStrategy::Unwind, Some("abort") => *slot = PanicStrategy::Abort, @@ -647,7 +656,7 @@ mod parse { true } - crate fn parse_oom_strategy(slot: &mut OomStrategy, v: Option<&str>) -> bool { + pub(crate) fn parse_oom_strategy(slot: &mut OomStrategy, v: Option<&str>) -> bool { match v { Some("panic") => *slot = OomStrategy::Panic, Some("abort") => *slot = OomStrategy::Abort, @@ -656,7 +665,7 @@ mod parse { true } - crate fn parse_relro_level(slot: &mut Option<RelroLevel>, v: Option<&str>) -> bool { + pub(crate) fn parse_relro_level(slot: &mut Option<RelroLevel>, v: Option<&str>) -> bool { match v { Some(s) => match s.parse::<RelroLevel>() { Ok(level) => *slot = Some(level), @@ -667,7 +676,7 @@ mod parse { true } - crate fn parse_sanitizers(slot: &mut SanitizerSet, v: Option<&str>) -> bool { + pub(crate) fn parse_sanitizers(slot: &mut SanitizerSet, v: Option<&str>) -> bool { if let Some(v) = v { for s in v.split(',') { *slot |= match s { @@ -687,7 +696,7 @@ mod parse { } } - crate fn parse_sanitizer_memory_track_origins(slot: &mut usize, v: Option<&str>) -> bool { + pub(crate) fn parse_sanitizer_memory_track_origins(slot: &mut usize, v: Option<&str>) -> bool { match v { Some("2") | None => { *slot = 2; @@ -705,7 +714,7 @@ mod parse { } } - crate fn parse_strip(slot: &mut Strip, v: Option<&str>) -> bool { + pub(crate) fn parse_strip(slot: &mut Strip, v: Option<&str>) -> bool { match v { Some("none") => *slot = Strip::None, Some("debuginfo") => *slot = Strip::Debuginfo, @@ -715,7 +724,7 @@ mod parse { true } - crate fn parse_cfguard(slot: &mut CFGuard, v: Option<&str>) -> bool { + pub(crate) fn parse_cfguard(slot: &mut CFGuard, v: Option<&str>) -> bool { if v.is_some() { let mut bool_arg = None; if parse_opt_bool(&mut bool_arg, v) { @@ -733,7 +742,7 @@ mod parse { true } - crate fn parse_cfprotection(slot: &mut CFProtection, v: Option<&str>) -> bool { + pub(crate) fn parse_cfprotection(slot: &mut CFProtection, v: Option<&str>) -> bool { if v.is_some() { let mut bool_arg = None; if parse_opt_bool(&mut bool_arg, v) { @@ -752,7 +761,7 @@ mod parse { true } - crate fn parse_linker_flavor(slot: &mut Option<LinkerFlavor>, v: Option<&str>) -> bool { + pub(crate) fn parse_linker_flavor(slot: &mut Option<LinkerFlavor>, v: Option<&str>) -> bool { match v.and_then(LinkerFlavor::from_str) { Some(lf) => *slot = Some(lf), _ => return false, @@ -760,7 +769,10 @@ mod parse { true } - crate fn parse_optimization_fuel(slot: &mut Option<(String, u64)>, v: Option<&str>) -> bool { + pub(crate) fn parse_optimization_fuel( + slot: &mut Option<(String, u64)>, + v: Option<&str>, + ) -> bool { match v { None => false, Some(s) => { @@ -779,7 +791,7 @@ mod parse { } } - crate fn parse_unpretty(slot: &mut Option<String>, v: Option<&str>) -> bool { + pub(crate) fn parse_unpretty(slot: &mut Option<String>, v: Option<&str>) -> bool { match v { None => false, Some(s) if s.split('=').count() <= 2 => { @@ -790,7 +802,7 @@ mod parse { } } - crate fn parse_mir_spanview(slot: &mut Option<MirSpanview>, v: Option<&str>) -> bool { + pub(crate) fn parse_mir_spanview(slot: &mut Option<MirSpanview>, v: Option<&str>) -> bool { if v.is_some() { let mut bool_arg = None; if parse_opt_bool(&mut bool_arg, v) { @@ -813,7 +825,7 @@ mod parse { true } - crate fn parse_instrument_coverage( + pub(crate) fn parse_instrument_coverage( slot: &mut Option<InstrumentCoverage>, v: Option<&str>, ) -> bool { @@ -844,7 +856,7 @@ mod parse { true } - crate fn parse_treat_err_as_bug(slot: &mut Option<NonZeroUsize>, v: Option<&str>) -> bool { + pub(crate) fn parse_treat_err_as_bug(slot: &mut Option<NonZeroUsize>, v: Option<&str>) -> bool { match v { Some(s) => { *slot = s.parse().ok(); @@ -857,7 +869,7 @@ mod parse { } } - crate fn parse_lto(slot: &mut LtoCli, v: Option<&str>) -> bool { + pub(crate) fn parse_lto(slot: &mut LtoCli, v: Option<&str>) -> bool { if v.is_some() { let mut bool_arg = None; if parse_opt_bool(&mut bool_arg, v) { @@ -875,7 +887,7 @@ mod parse { true } - crate fn parse_linker_plugin_lto(slot: &mut LinkerPluginLto, v: Option<&str>) -> bool { + pub(crate) fn parse_linker_plugin_lto(slot: &mut LinkerPluginLto, v: Option<&str>) -> bool { if v.is_some() { let mut bool_arg = None; if parse_opt_bool(&mut bool_arg, v) { @@ -895,7 +907,10 @@ mod parse { true } - crate fn parse_switch_with_opt_path(slot: &mut SwitchWithOptPath, v: Option<&str>) -> bool { + pub(crate) fn parse_switch_with_opt_path( + slot: &mut SwitchWithOptPath, + v: Option<&str>, + ) -> bool { *slot = match v { None => SwitchWithOptPath::Enabled(None), Some(path) => SwitchWithOptPath::Enabled(Some(PathBuf::from(path))), @@ -903,7 +918,10 @@ mod parse { true } - crate fn parse_merge_functions(slot: &mut Option<MergeFunctions>, v: Option<&str>) -> bool { + pub(crate) fn parse_merge_functions( + slot: &mut Option<MergeFunctions>, + v: Option<&str>, + ) -> bool { match v.and_then(|s| MergeFunctions::from_str(s).ok()) { Some(mergefunc) => *slot = Some(mergefunc), _ => return false, @@ -911,7 +929,7 @@ mod parse { true } - crate fn parse_relocation_model(slot: &mut Option<RelocModel>, v: Option<&str>) -> bool { + pub(crate) fn parse_relocation_model(slot: &mut Option<RelocModel>, v: Option<&str>) -> bool { match v.and_then(|s| RelocModel::from_str(s).ok()) { Some(relocation_model) => *slot = Some(relocation_model), None if v == Some("default") => *slot = None, @@ -920,7 +938,7 @@ mod parse { true } - crate fn parse_code_model(slot: &mut Option<CodeModel>, v: Option<&str>) -> bool { + pub(crate) fn parse_code_model(slot: &mut Option<CodeModel>, v: Option<&str>) -> bool { match v.and_then(|s| CodeModel::from_str(s).ok()) { Some(code_model) => *slot = Some(code_model), _ => return false, @@ -928,7 +946,7 @@ mod parse { true } - crate fn parse_tls_model(slot: &mut Option<TlsModel>, v: Option<&str>) -> bool { + pub(crate) fn parse_tls_model(slot: &mut Option<TlsModel>, v: Option<&str>) -> bool { match v.and_then(|s| TlsModel::from_str(s).ok()) { Some(tls_model) => *slot = Some(tls_model), _ => return false, @@ -936,7 +954,7 @@ mod parse { true } - crate fn parse_symbol_mangling_version( + pub(crate) fn parse_symbol_mangling_version( slot: &mut Option<SymbolManglingVersion>, v: Option<&str>, ) -> bool { @@ -948,7 +966,7 @@ mod parse { true } - crate fn parse_src_file_hash( + pub(crate) fn parse_src_file_hash( slot: &mut Option<SourceFileHashAlgorithm>, v: Option<&str>, ) -> bool { @@ -959,7 +977,7 @@ mod parse { true } - crate fn parse_target_feature(slot: &mut String, v: Option<&str>) -> bool { + pub(crate) fn parse_target_feature(slot: &mut String, v: Option<&str>) -> bool { match v { Some(s) => { if !slot.is_empty() { @@ -972,7 +990,7 @@ mod parse { } } - crate fn parse_wasi_exec_model(slot: &mut Option<WasiExecModel>, v: Option<&str>) -> bool { + pub(crate) fn parse_wasi_exec_model(slot: &mut Option<WasiExecModel>, v: Option<&str>) -> bool { match v { Some("command") => *slot = Some(WasiExecModel::Command), Some("reactor") => *slot = Some(WasiExecModel::Reactor), @@ -981,7 +999,10 @@ mod parse { true } - crate fn parse_split_debuginfo(slot: &mut Option<SplitDebuginfo>, v: Option<&str>) -> bool { + pub(crate) fn parse_split_debuginfo( + slot: &mut Option<SplitDebuginfo>, + v: Option<&str>, + ) -> bool { match v.and_then(|s| SplitDebuginfo::from_str(s).ok()) { Some(e) => *slot = Some(e), _ => return false, @@ -989,7 +1010,7 @@ mod parse { true } - crate fn parse_split_dwarf_kind(slot: &mut SplitDwarfKind, v: Option<&str>) -> bool { + pub(crate) fn parse_split_dwarf_kind(slot: &mut SplitDwarfKind, v: Option<&str>) -> bool { match v.and_then(|s| SplitDwarfKind::from_str(s).ok()) { Some(e) => *slot = e, _ => return false, @@ -997,7 +1018,7 @@ mod parse { true } - crate fn parse_gcc_ld(slot: &mut Option<LdImpl>, v: Option<&str>) -> bool { + pub(crate) fn parse_gcc_ld(slot: &mut Option<LdImpl>, v: Option<&str>) -> bool { match v { None => *slot = None, Some("lld") => *slot = Some(LdImpl::Lld), @@ -1006,7 +1027,7 @@ mod parse { true } - crate fn parse_stack_protector(slot: &mut StackProtector, v: Option<&str>) -> bool { + pub(crate) fn parse_stack_protector(slot: &mut StackProtector, v: Option<&str>) -> bool { match v.and_then(|s| StackProtector::from_str(s).ok()) { Some(ssp) => *slot = ssp, _ => return false, @@ -1014,7 +1035,10 @@ mod parse { true } - crate fn parse_branch_protection(slot: &mut Option<BranchProtection>, v: Option<&str>) -> bool { + pub(crate) fn parse_branch_protection( + slot: &mut Option<BranchProtection>, + v: Option<&str>, + ) -> bool { match v { Some(s) => { let slot = slot.get_or_insert_default(); diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index e933fe1cb24..6fb87e15a33 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -289,12 +289,26 @@ impl ParseSess { self.proc_macro_quoted_spans.lock().clone() } + pub fn create_err<'a>( + &'a self, + err: impl SessionDiagnostic<'a>, + ) -> DiagnosticBuilder<'a, ErrorGuaranteed> { + err.into_diagnostic(self) + } + pub fn emit_err<'a>(&'a self, err: impl SessionDiagnostic<'a>) -> ErrorGuaranteed { - err.into_diagnostic(self).emit() + self.create_err(err).emit() + } + + pub fn create_warning<'a>( + &'a self, + warning: impl SessionDiagnostic<'a, ()>, + ) -> DiagnosticBuilder<'a, ()> { + warning.into_diagnostic(self) } pub fn emit_warning<'a>(&'a self, warning: impl SessionDiagnostic<'a, ()>) { - warning.into_diagnostic(self).emit() + self.create_warning(warning).emit() } pub fn struct_err( diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index e8279f6fed2..b2c23cda6aa 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -413,9 +413,21 @@ impl Session { pub fn err(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed { self.diagnostic().err(msg) } + pub fn create_err<'a>( + &'a self, + err: impl SessionDiagnostic<'a>, + ) -> DiagnosticBuilder<'a, ErrorGuaranteed> { + self.parse_sess.create_err(err) + } pub fn emit_err<'a>(&'a self, err: impl SessionDiagnostic<'a>) -> ErrorGuaranteed { self.parse_sess.emit_err(err) } + pub fn create_warning<'a>( + &'a self, + err: impl SessionDiagnostic<'a, ()>, + ) -> DiagnosticBuilder<'a, ()> { + self.parse_sess.create_warning(err) + } pub fn emit_warning<'a>(&'a self, warning: impl SessionDiagnostic<'a, ()>) { self.parse_sess.emit_warning(warning) } diff --git a/compiler/rustc_session/src/utils.rs b/compiler/rustc_session/src/utils.rs index db755ccd1d5..bda7b314308 100644 --- a/compiler/rustc_session/src/utils.rs +++ b/compiler/rustc_session/src/utils.rs @@ -1,13 +1,7 @@ -use crate::parse::ParseSess; use crate::session::Session; -use rustc_ast::token::{self, Delimiter, Nonterminal, Token}; -use rustc_ast::tokenstream::CanSynthesizeMissingTokens; -use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree}; use rustc_data_structures::profiling::VerboseTimingGuard; use std::path::{Path, PathBuf}; -pub type NtToTokenstream = fn(&Nonterminal, &ParseSess, CanSynthesizeMissingTokens) -> TokenStream; - impl Session { pub fn timer<'a>(&'a self, what: &'static str) -> VerboseTimingGuard<'a> { self.prof.verbose_generic_activity(what) @@ -94,55 +88,3 @@ impl CanonicalizedPath { &self.original } } - -// FIXME: Find a better spot for this - it needs to be accessible from `rustc_ast_lowering`, -// and needs to access `ParseSess -pub struct FlattenNonterminals<'a> { - pub parse_sess: &'a ParseSess, - pub synthesize_tokens: CanSynthesizeMissingTokens, - pub nt_to_tokenstream: NtToTokenstream, -} - -impl<'a> FlattenNonterminals<'a> { - pub fn process_token_stream(&mut self, tokens: TokenStream) -> TokenStream { - fn can_skip(stream: &TokenStream) -> bool { - stream.trees().all(|tree| match tree { - TokenTree::Token(token) => !matches!(token.kind, token::Interpolated(_)), - TokenTree::Delimited(_, _, inner) => can_skip(&inner), - }) - } - - if can_skip(&tokens) { - return tokens; - } - - tokens.into_trees().flat_map(|tree| self.process_token_tree(tree).into_trees()).collect() - } - - pub fn process_token_tree(&mut self, tree: TokenTree) -> TokenStream { - match tree { - TokenTree::Token(token) => self.process_token(token), - TokenTree::Delimited(span, delim, tts) => { - TokenTree::Delimited(span, delim, self.process_token_stream(tts)).into() - } - } - } - - pub fn process_token(&mut self, token: Token) -> TokenStream { - match token.kind { - token::Interpolated(nt) if let token::NtIdent(ident, is_raw) = *nt => { - TokenTree::Token(Token::new(token::Ident(ident.name, is_raw), ident.span)).into() - } - token::Interpolated(nt) => { - let tts = (self.nt_to_tokenstream)(&nt, self.parse_sess, self.synthesize_tokens); - TokenTree::Delimited( - DelimSpan::from_single(token.span), - Delimiter::Invisible, - self.process_token_stream(tts), - ) - .into() - } - _ => TokenTree::Token(token).into(), - } - } -} diff --git a/compiler/rustc_span/src/def_id.rs b/compiler/rustc_span/src/def_id.rs index d5f806308cf..3976c062221 100644 --- a/compiler/rustc_span/src/def_id.rs +++ b/compiler/rustc_span/src/def_id.rs @@ -279,8 +279,14 @@ impl DefId { } #[inline] + #[track_caller] pub fn expect_local(self) -> LocalDefId { - self.as_local().unwrap_or_else(|| panic!("DefId::expect_local: `{:?}` isn't local", self)) + // NOTE: `match` below is required to apply `#[track_caller]`, + // i.e. don't use closures. + match self.as_local() { + Some(local_def_id) => local_def_id, + None => panic!("DefId::expect_local: `{:?}` isn't local", self), + } } #[inline] diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 447b73fa3c3..59f2badbabb 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -352,7 +352,7 @@ pub struct HygieneData { } impl HygieneData { - crate fn new(edition: Edition) -> Self { + pub(crate) fn new(edition: Edition) -> Self { let root_data = ExpnData::default( ExpnKind::Root, DUMMY_SP, @@ -668,17 +668,17 @@ impl SyntaxContext { } #[inline] - crate fn as_u32(self) -> u32 { + pub(crate) fn as_u32(self) -> u32 { self.0 } #[inline] - crate fn from_u32(raw: u32) -> SyntaxContext { + pub(crate) fn from_u32(raw: u32) -> SyntaxContext { SyntaxContext(raw) } /// Extend a syntax context with a given expansion and transparency. - crate fn apply_mark(self, expn_id: ExpnId, transparency: Transparency) -> SyntaxContext { + pub(crate) fn apply_mark(self, expn_id: ExpnId, transparency: Transparency) -> SyntaxContext { HygieneData::with(|data| data.apply_mark(self, expn_id, transparency)) } @@ -686,7 +686,7 @@ impl SyntaxContext { /// context up one macro definition level. That is, if we have a nested macro /// definition as follows: /// - /// ```rust + /// ```ignore (illustrative) /// macro_rules! f { /// macro_rules! g { /// ... @@ -710,6 +710,7 @@ impl SyntaxContext { /// For example, consider the following three resolutions of `f`: /// /// ```rust + /// #![feature(decl_macro)] /// mod foo { pub fn f() {} } // `f`'s `SyntaxContext` is empty. /// m!(f); /// macro m($f:ident) { @@ -746,7 +747,8 @@ impl SyntaxContext { /// via a glob import with the given `SyntaxContext`. /// For example: /// - /// ```rust + /// ```compile_fail,E0425 + /// #![feature(decl_macro)] /// m!(f); /// macro m($i:ident) { /// mod foo { @@ -786,7 +788,7 @@ impl SyntaxContext { /// Undo `glob_adjust` if possible: /// - /// ```rust + /// ```ignore (illustrative) /// if let Some(privacy_checking_scope) = self.reverse_glob_adjust(expansion, glob_ctxt) { /// assert!(self.glob_adjust(expansion, glob_ctxt) == Some(privacy_checking_scope)); /// } diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 7357cebf62e..8737e45487e 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -15,7 +15,6 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(array_windows)] -#![feature(crate_visibility_modifier)] #![feature(let_else)] #![feature(if_let_guard)] #![feature(negative_impls)] @@ -335,8 +334,8 @@ impl fmt::Display for FileNameDisplay<'_> { } } -impl FileNameDisplay<'_> { - pub fn to_string_lossy(&self) -> Cow<'_, str> { +impl<'a> FileNameDisplay<'a> { + pub fn to_string_lossy(&self) -> Cow<'a, str> { match self.inner { FileName::Real(ref inner) => inner.to_string_lossy(self.display_pref), _ => Cow::from(format!("{}", self)), @@ -1153,7 +1152,7 @@ impl FromStr for SourceFileHashAlgorithm { } /// The hash of the on-disk source file used for debug info. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] #[derive(HashStable_Generic, Encodable, Decodable)] pub struct SourceFileHash { pub kind: SourceFileHashAlgorithm, diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index 460b5c18fc1..020ae3ad0c7 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -1058,10 +1058,11 @@ impl SourceMap { /// Tries to find the span of the semicolon of a macro call statement. /// The input must be the *call site* span of a statement from macro expansion. - /// - /// v output - /// mac!(); - /// ^^^^^^ input + /// ```ignore (illustrative) + /// // v output + /// mac!(); + /// // ^^^^^^ input + /// ``` pub fn mac_call_stmt_semi_span(&self, mac_call: Span) -> Option<Span> { let span = self.span_extend_while(mac_call, char::is_whitespace).ok()?; let span = span.shrink_to_hi().with_hi(BytePos(span.hi().0.checked_add(1)?)); @@ -1097,28 +1098,45 @@ impl FilePathMapping { /// The return value is the remapped path and a boolean indicating whether /// the path was affected by the mapping. pub fn map_prefix(&self, path: PathBuf) -> (PathBuf, bool) { - // NOTE: We are iterating over the mapping entries from last to first - // because entries specified later on the command line should - // take precedence. - for &(ref from, ref to) in self.mapping.iter().rev() { - if let Ok(rest) = path.strip_prefix(from) { - let remapped = if rest.as_os_str().is_empty() { - // This is subtle, joining an empty path onto e.g. `foo/bar` will - // result in `foo/bar/`, that is, there'll be an additional directory - // separator at the end. This can lead to duplicated directory separators - // in remapped paths down the line. - // So, if we have an exact match, we just return that without a call - // to `Path::join()`. - to.clone() - } else { - to.join(rest) - }; + if path.as_os_str().is_empty() { + // Exit early if the path is empty and therefore there's nothing to remap. + // This is mostly to reduce spam for `RUSTC_LOG=[remap_path_prefix]`. + return (path, false); + } - return (remapped, true); + return remap_path_prefix(&self.mapping, path); + + #[instrument(level = "debug", skip(mapping))] + fn remap_path_prefix(mapping: &[(PathBuf, PathBuf)], path: PathBuf) -> (PathBuf, bool) { + // NOTE: We are iterating over the mapping entries from last to first + // because entries specified later on the command line should + // take precedence. + for &(ref from, ref to) in mapping.iter().rev() { + debug!("Trying to apply {:?} => {:?}", from, to); + + if let Ok(rest) = path.strip_prefix(from) { + let remapped = if rest.as_os_str().is_empty() { + // This is subtle, joining an empty path onto e.g. `foo/bar` will + // result in `foo/bar/`, that is, there'll be an additional directory + // separator at the end. This can lead to duplicated directory separators + // in remapped paths down the line. + // So, if we have an exact match, we just return that without a call + // to `Path::join()`. + to.clone() + } else { + to.join(rest) + }; + debug!("Match - remapped {:?} => {:?}", path, remapped); + + return (remapped, true); + } else { + debug!("No match - prefix {:?} does not match {:?}", from, path); + } } - } - (path, false) + debug!("Path {:?} was not remapped", path); + (path, false) + } } fn map_filename_prefix(&self, file: &FileName) -> (FileName, bool) { @@ -1139,4 +1157,83 @@ impl FilePathMapping { other => (other.clone(), false), } } + + /// Expand a relative path to an absolute path with remapping taken into account. + /// Use this when absolute paths are required (e.g. debuginfo or crate metadata). + /// + /// The resulting `RealFileName` will have its `local_path` portion erased if + /// possible (i.e. if there's also a remapped path). + pub fn to_embeddable_absolute_path( + &self, + file_path: RealFileName, + working_directory: &RealFileName, + ) -> RealFileName { + match file_path { + // Anything that's already remapped we don't modify, except for erasing + // the `local_path` portion. + RealFileName::Remapped { local_path: _, virtual_name } => { + RealFileName::Remapped { + // We do not want any local path to be exported into metadata + local_path: None, + // We use the remapped name verbatim, even if it looks like a relative + // path. The assumption is that the user doesn't want us to further + // process paths that have gone through remapping. + virtual_name, + } + } + + RealFileName::LocalPath(unmapped_file_path) => { + // If no remapping has been applied yet, try to do so + let (new_path, was_remapped) = self.map_prefix(unmapped_file_path); + if was_remapped { + // It was remapped, so don't modify further + return RealFileName::Remapped { local_path: None, virtual_name: new_path }; + } + + if new_path.is_absolute() { + // No remapping has applied to this path and it is absolute, + // so the working directory cannot influence it either, so + // we are done. + return RealFileName::LocalPath(new_path); + } + + debug_assert!(new_path.is_relative()); + let unmapped_file_path_rel = new_path; + + match working_directory { + RealFileName::LocalPath(unmapped_working_dir_abs) => { + let file_path_abs = unmapped_working_dir_abs.join(unmapped_file_path_rel); + + // Although neither `working_directory` nor the file name were subject + // to path remapping, the concatenation between the two may be. Hence + // we need to do a remapping here. + let (file_path_abs, was_remapped) = self.map_prefix(file_path_abs); + if was_remapped { + RealFileName::Remapped { + // Erase the actual path + local_path: None, + virtual_name: file_path_abs, + } + } else { + // No kind of remapping applied to this path, so + // we leave it as it is. + RealFileName::LocalPath(file_path_abs) + } + } + RealFileName::Remapped { + local_path: _, + virtual_name: remapped_working_dir_abs, + } => { + // If working_directory has been remapped, then we emit + // Remapped variant as the expanded path won't be valid + RealFileName::Remapped { + local_path: None, + virtual_name: Path::new(remapped_working_dir_abs) + .join(unmapped_file_path_rel), + } + } + } + } + } + } } diff --git a/compiler/rustc_span/src/source_map/tests.rs b/compiler/rustc_span/src/source_map/tests.rs index 481e015c66c..be827cea874 100644 --- a/compiler/rustc_span/src/source_map/tests.rs +++ b/compiler/rustc_span/src/source_map/tests.rs @@ -313,82 +313,169 @@ impl SourceMapExtension for SourceMap { } } -fn map_path_prefix(mapping: &FilePathMapping, path: &str) -> String { +// Takes a unix-style path and returns a platform specific path. +fn path(p: &str) -> PathBuf { + path_str(p).into() +} + +// Takes a unix-style path and returns a platform specific path. +fn path_str(p: &str) -> String { + #[cfg(not(windows))] + { + return p.into(); + } + + #[cfg(windows)] + { + let mut path = p.replace('/', "\\"); + if let Some(rest) = path.strip_prefix('\\') { + path = ["X:\\", rest].concat(); + } + + path + } +} + +fn map_path_prefix(mapping: &FilePathMapping, p: &str) -> String { // It's important that we convert to a string here because that's what // later stages do too (e.g. in the backend), and comparing `Path` values // won't catch some differences at the string level, e.g. "abc" and "abc/" // compare as equal. - mapping.map_prefix(path.into()).0.to_string_lossy().to_string() + mapping.map_prefix(path(p)).0.to_string_lossy().to_string() } -#[cfg(unix)] #[test] fn path_prefix_remapping() { // Relative to relative { - let mapping = &FilePathMapping::new(vec![("abc/def".into(), "foo".into())]); + let mapping = &FilePathMapping::new(vec![(path("abc/def"), path("foo"))]); - assert_eq!(map_path_prefix(mapping, "abc/def/src/main.rs"), "foo/src/main.rs"); - assert_eq!(map_path_prefix(mapping, "abc/def"), "foo"); + assert_eq!(map_path_prefix(mapping, "abc/def/src/main.rs"), path_str("foo/src/main.rs")); + assert_eq!(map_path_prefix(mapping, "abc/def"), path_str("foo")); } // Relative to absolute { - let mapping = &FilePathMapping::new(vec![("abc/def".into(), "/foo".into())]); + let mapping = &FilePathMapping::new(vec![(path("abc/def"), path("/foo"))]); - assert_eq!(map_path_prefix(mapping, "abc/def/src/main.rs"), "/foo/src/main.rs"); - assert_eq!(map_path_prefix(mapping, "abc/def"), "/foo"); + assert_eq!(map_path_prefix(mapping, "abc/def/src/main.rs"), path_str("/foo/src/main.rs")); + assert_eq!(map_path_prefix(mapping, "abc/def"), path_str("/foo")); } // Absolute to relative { - let mapping = &FilePathMapping::new(vec![("/abc/def".into(), "foo".into())]); + let mapping = &FilePathMapping::new(vec![(path("/abc/def"), path("foo"))]); - assert_eq!(map_path_prefix(mapping, "/abc/def/src/main.rs"), "foo/src/main.rs"); - assert_eq!(map_path_prefix(mapping, "/abc/def"), "foo"); + assert_eq!(map_path_prefix(mapping, "/abc/def/src/main.rs"), path_str("foo/src/main.rs")); + assert_eq!(map_path_prefix(mapping, "/abc/def"), path_str("foo")); } // Absolute to absolute { - let mapping = &FilePathMapping::new(vec![("/abc/def".into(), "/foo".into())]); + let mapping = &FilePathMapping::new(vec![(path("/abc/def"), path("/foo"))]); - assert_eq!(map_path_prefix(mapping, "/abc/def/src/main.rs"), "/foo/src/main.rs"); - assert_eq!(map_path_prefix(mapping, "/abc/def"), "/foo"); + assert_eq!(map_path_prefix(mapping, "/abc/def/src/main.rs"), path_str("/foo/src/main.rs")); + assert_eq!(map_path_prefix(mapping, "/abc/def"), path_str("/foo")); } } -#[cfg(windows)] #[test] -fn path_prefix_remapping_from_relative2() { - // Relative to relative - { - let mapping = &FilePathMapping::new(vec![("abc\\def".into(), "foo".into())]); +fn path_prefix_remapping_expand_to_absolute() { + // "virtual" working directory is relative path + let mapping = + &FilePathMapping::new(vec![(path("/foo"), path("FOO")), (path("/bar"), path("BAR"))]); + let working_directory = path("/foo"); + let working_directory = RealFileName::Remapped { + local_path: Some(working_directory.clone()), + virtual_name: mapping.map_prefix(working_directory).0, + }; + + assert_eq!(working_directory.remapped_path_if_available(), path("FOO")); + + // Unmapped absolute path + assert_eq!( + mapping.to_embeddable_absolute_path( + RealFileName::LocalPath(path("/foo/src/main.rs")), + &working_directory + ), + RealFileName::Remapped { local_path: None, virtual_name: path("FOO/src/main.rs") } + ); - assert_eq!(map_path_prefix(mapping, "abc\\def\\src\\main.rs"), "foo\\src\\main.rs"); - assert_eq!(map_path_prefix(mapping, "abc\\def"), "foo"); - } + // Unmapped absolute path with unrelated working directory + assert_eq!( + mapping.to_embeddable_absolute_path( + RealFileName::LocalPath(path("/bar/src/main.rs")), + &working_directory + ), + RealFileName::Remapped { local_path: None, virtual_name: path("BAR/src/main.rs") } + ); - // Relative to absolute - { - let mapping = &FilePathMapping::new(vec![("abc\\def".into(), "X:\\foo".into())]); + // Unmapped absolute path that does not match any prefix + assert_eq!( + mapping.to_embeddable_absolute_path( + RealFileName::LocalPath(path("/quux/src/main.rs")), + &working_directory + ), + RealFileName::LocalPath(path("/quux/src/main.rs")), + ); - assert_eq!(map_path_prefix(mapping, "abc\\def\\src\\main.rs"), "X:\\foo\\src\\main.rs"); - assert_eq!(map_path_prefix(mapping, "abc\\def"), "X:\\foo"); - } + // Unmapped relative path + assert_eq!( + mapping.to_embeddable_absolute_path( + RealFileName::LocalPath(path("src/main.rs")), + &working_directory + ), + RealFileName::Remapped { local_path: None, virtual_name: path("FOO/src/main.rs") } + ); - // Absolute to relative - { - let mapping = &FilePathMapping::new(vec![("X:\\abc\\def".into(), "foo".into())]); + // Unmapped relative path with `./` + assert_eq!( + mapping.to_embeddable_absolute_path( + RealFileName::LocalPath(path("./src/main.rs")), + &working_directory + ), + RealFileName::Remapped { local_path: None, virtual_name: path("FOO/src/main.rs") } + ); - assert_eq!(map_path_prefix(mapping, "X:\\abc\\def\\src\\main.rs"), "foo\\src\\main.rs"); - assert_eq!(map_path_prefix(mapping, "X:\\abc\\def"), "foo"); - } + // Unmapped relative path that does not match any prefix + assert_eq!( + mapping.to_embeddable_absolute_path( + RealFileName::LocalPath(path("quux/src/main.rs")), + &RealFileName::LocalPath(path("/abc")), + ), + RealFileName::LocalPath(path("/abc/quux/src/main.rs")), + ); - // Absolute to absolute - { - let mapping = &FilePathMapping::new(vec![("X:\\abc\\def".into(), "X:\\foo".into())]); + // Already remapped absolute path + assert_eq!( + mapping.to_embeddable_absolute_path( + RealFileName::Remapped { + local_path: Some(path("/foo/src/main.rs")), + virtual_name: path("FOO/src/main.rs"), + }, + &working_directory + ), + RealFileName::Remapped { local_path: None, virtual_name: path("FOO/src/main.rs") } + ); - assert_eq!(map_path_prefix(mapping, "X:\\abc\\def\\src\\main.rs"), "X:\\foo\\src\\main.rs"); - assert_eq!(map_path_prefix(mapping, "X:\\abc\\def"), "X:\\foo"); - } + // Already remapped absolute path, with unrelated working directory + assert_eq!( + mapping.to_embeddable_absolute_path( + RealFileName::Remapped { + local_path: Some(path("/bar/src/main.rs")), + virtual_name: path("BAR/src/main.rs"), + }, + &working_directory + ), + RealFileName::Remapped { local_path: None, virtual_name: path("BAR/src/main.rs") } + ); + + // Already remapped relative path + assert_eq!( + mapping.to_embeddable_absolute_path( + RealFileName::Remapped { local_path: None, virtual_name: path("XYZ/src/main.rs") }, + &working_directory + ), + RealFileName::Remapped { local_path: None, virtual_name: path("XYZ/src/main.rs") } + ); } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index e7149f4a672..5c9c16350e4 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1079,6 +1079,7 @@ symbols! { ptr_null, ptr_null_mut, ptr_offset_from, + ptr_offset_from_unsigned, pub_macro_rules, pub_restricted, pure, @@ -1189,6 +1190,7 @@ symbols! { rustc_error, rustc_evaluate_where_clauses, rustc_expected_cgu_reuse, + rustc_has_incoherent_inherent_impls, rustc_if_this_changed, rustc_inherit_overflow_checks, rustc_insignificant_dtor, @@ -1406,6 +1408,7 @@ symbols! { thread_local_macro, thumb2, thumb_mode: "thumb-mode", + tmm_reg, todo_macro, tool_attributes, tool_lints, diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index 0ff0267d0ce..ee0994c9ad6 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -96,8 +96,10 @@ #[macro_use] extern crate rustc_middle; +use rustc_hir::def::DefKind; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::mir::mono::{InstantiationMode, MonoItem}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::subst::SubstsRef; @@ -175,7 +177,11 @@ fn compute_symbol_name<'tcx>( } // FIXME(eddyb) Precompute a custom symbol name based on attributes. - let attrs = tcx.codegen_fn_attrs(def_id); + let attrs = if tcx.def_kind(def_id).has_codegen_attrs() { + tcx.codegen_fn_attrs(def_id) + } else { + CodegenFnAttrs::EMPTY + }; // Foreign items by default use no mangling for their symbol name. There's a // few exceptions to this rule though: @@ -213,20 +219,25 @@ fn compute_symbol_name<'tcx>( return tcx.item_name(def_id).to_string(); } - let avoid_cross_crate_conflicts = - // If this is an instance of a generic function, we also hash in - // the ID of the instantiating crate. This avoids symbol conflicts - // in case the same instances is emitted in two crates of the same - // project. - is_generic(substs) || + // If we're dealing with an instance of a function that's inlined from + // another crate but we're marking it as globally shared to our + // compilation (aka we're not making an internal copy in each of our + // codegen units) then this symbol may become an exported (but hidden + // visibility) symbol. This means that multiple crates may do the same + // and we want to be sure to avoid any symbol conflicts here. + let is_globally_shared_function = matches!( + tcx.def_kind(instance.def_id()), + DefKind::Fn | DefKind::AssocFn | DefKind::Closure | DefKind::Generator | DefKind::Ctor(..) + ) && matches!( + MonoItem::Fn(instance).instantiation_mode(tcx), + InstantiationMode::GloballyShared { may_conflict: true } + ); - // If we're dealing with an instance of a function that's inlined from - // another crate but we're marking it as globally shared to our - // compilation (aka we're not making an internal copy in each of our - // codegen units) then this symbol may become an exported (but hidden - // visibility) symbol. This means that multiple crates may do the same - // and we want to be sure to avoid any symbol conflicts here. - matches!(MonoItem::Fn(instance).instantiation_mode(tcx), InstantiationMode::GloballyShared { may_conflict: true }); + // If this is an instance of a generic function, we also hash in + // the ID of the instantiating crate. This avoids symbol conflicts + // in case the same instances is emitted in two crates of the same + // project. + let avoid_cross_crate_conflicts = is_generic(substs) || is_globally_shared_function; let instantiating_crate = if avoid_cross_crate_conflicts { Some(compute_instantiating_crate()) } else { None }; diff --git a/compiler/rustc_symbol_mangling/src/test.rs b/compiler/rustc_symbol_mangling/src/test.rs index 37d1cffa2a5..7249ce04c15 100644 --- a/compiler/rustc_symbol_mangling/src/test.rs +++ b/compiler/rustc_symbol_mangling/src/test.rs @@ -49,27 +49,26 @@ struct SymbolNamesTest<'tcx> { impl SymbolNamesTest<'_> { fn process_attrs(&mut self, def_id: LocalDefId) { let tcx = self.tcx; - for attr in tcx.get_attrs(def_id.to_def_id()).iter() { - if attr.has_name(SYMBOL_NAME) { - let def_id = def_id.to_def_id(); - let instance = Instance::new( - def_id, - tcx.erase_regions(InternalSubsts::identity_for_item(tcx, def_id)), - ); - let mangled = tcx.symbol_name(instance); - tcx.sess.span_err(attr.span, &format!("symbol-name({})", mangled)); - if let Ok(demangling) = rustc_demangle::try_demangle(mangled.name) { - tcx.sess.span_err(attr.span, &format!("demangling({})", demangling)); - tcx.sess.span_err(attr.span, &format!("demangling-alt({:#})", demangling)); - } - } else if attr.has_name(DEF_PATH) { - let path = with_no_trimmed_paths!(tcx.def_path_str(def_id.to_def_id())); - tcx.sess.span_err(attr.span, &format!("def-path({})", path)); + // The formatting of `tag({})` is chosen so that tests can elect + // to test the entirety of the string, if they choose, or else just + // some subset. + for attr in tcx.get_attrs(def_id.to_def_id(), SYMBOL_NAME) { + let def_id = def_id.to_def_id(); + let instance = Instance::new( + def_id, + tcx.erase_regions(InternalSubsts::identity_for_item(tcx, def_id)), + ); + let mangled = tcx.symbol_name(instance); + tcx.sess.span_err(attr.span, &format!("symbol-name({})", mangled)); + if let Ok(demangling) = rustc_demangle::try_demangle(mangled.name) { + tcx.sess.span_err(attr.span, &format!("demangling({})", demangling)); + tcx.sess.span_err(attr.span, &format!("demangling-alt({:#})", demangling)); } + } - // (*) The formatting of `tag({})` is chosen so that tests can elect - // to test the entirety of the string, if they choose, or else just - // some subset. + for attr in tcx.get_attrs(def_id.to_def_id(), DEF_PATH) { + let path = with_no_trimmed_paths!(tcx.def_path_str(def_id.to_def_id())); + tcx.sess.span_err(attr.span, &format!("def-path({})", path)); } } } diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index c8fdf363f05..dc1946bcdc2 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -9,7 +9,9 @@ use rustc_middle::mir::interpret::ConstValue; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::print::{Print, Printer}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst}; -use rustc_middle::ty::{self, FloatTy, Instance, IntTy, Ty, TyCtxt, TypeFoldable, UintTy}; +use rustc_middle::ty::{ + self, EarlyBinder, FloatTy, Instance, IntTy, Ty, TyCtxt, TypeFoldable, UintTy, +}; use rustc_span::symbol::kw; use rustc_target::abi::call::FnAbi; use rustc_target::abi::Integer; @@ -297,7 +299,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { let mut param_env = self.tcx.param_env_reveal_all_normalized(impl_def_id); if !substs.is_empty() { - param_env = param_env.subst(self.tcx, substs); + param_env = EarlyBinder(param_env).subst(self.tcx, substs); } match &mut impl_trait_ref { @@ -788,7 +790,8 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { // These should never show up as `path_append` arguments. DefPathData::CrateRoot - | DefPathData::Misc + | DefPathData::Use + | DefPathData::GlobalAsm | DefPathData::Impl | DefPathData::MacroNs(_) | DefPathData::LifetimeNs(_) => { diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs index 0e8fd9cc93f..a2cd3c4c468 100644 --- a/compiler/rustc_target/src/abi/mod.rs +++ b/compiler/rustc_target/src/abi/mod.rs @@ -276,12 +276,19 @@ impl ToJson for Endian { } /// Size of a type in bytes. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Encodable, Decodable)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)] #[derive(HashStable_Generic)] pub struct Size { raw: u64, } +// This is debug-printed a lot in larger structs, don't waste too much space there +impl fmt::Debug for Size { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Size({} bytes)", self.bytes()) + } +} + impl Size { pub const ZERO: Size = Size { raw: 0 }; @@ -485,12 +492,19 @@ impl Step for Size { } /// Alignment of a type in bytes (always a power of two). -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Encodable, Decodable)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)] #[derive(HashStable_Generic)] pub struct Align { pow2: u8, } +// This is debug-printed a lot in larger structs, don't waste too much space there +impl fmt::Debug for Align { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Align({} bytes)", self.bytes()) + } +} + impl Align { pub const ONE: Align = Align { pow2: 0 }; diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index 6bc807c7c44..df8ccc42a77 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -912,6 +912,7 @@ impl InlineAsmClobberAbi { mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7, st0, st1, st2, st3, st4, st5, st6, st7, + tmm0, tmm1, tmm2, tmm3, tmm4, tmm5, tmm6, tmm7, } }, InlineAsmClobberAbi::X86_64Win => clobbered_regs! { @@ -931,6 +932,7 @@ impl InlineAsmClobberAbi { mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7, st0, st1, st2, st3, st4, st5, st6, st7, + tmm0, tmm1, tmm2, tmm3, tmm4, tmm5, tmm6, tmm7, } }, InlineAsmClobberAbi::AArch64 => clobbered_regs! { diff --git a/compiler/rustc_target/src/asm/x86.rs b/compiler/rustc_target/src/asm/x86.rs index 854674c7f2f..e35035fd25a 100644 --- a/compiler/rustc_target/src/asm/x86.rs +++ b/compiler/rustc_target/src/asm/x86.rs @@ -17,6 +17,7 @@ def_reg_class! { kreg0, mmx_reg, x87_reg, + tmm_reg, } } @@ -41,6 +42,7 @@ impl X86InlineAsmRegClass { Self::xmm_reg | Self::ymm_reg | Self::zmm_reg => &['x', 'y', 'z'], Self::kreg | Self::kreg0 => &[], Self::mmx_reg | Self::x87_reg => &[], + Self::tmm_reg => &[], } } @@ -80,6 +82,7 @@ impl X86InlineAsmRegClass { }, Self::kreg | Self::kreg0 => None, Self::mmx_reg | Self::x87_reg => None, + Self::tmm_reg => None, } } @@ -98,6 +101,7 @@ impl X86InlineAsmRegClass { Self::zmm_reg => Some(('z', "zmm0")), Self::kreg | Self::kreg0 => None, Self::mmx_reg | Self::x87_reg => None, + Self::tmm_reg => None, } } @@ -135,6 +139,7 @@ impl X86InlineAsmRegClass { }, Self::kreg0 => &[], Self::mmx_reg | Self::x87_reg => &[], + Self::tmm_reg => &[], } } } @@ -320,6 +325,14 @@ def_regs! { st5: x87_reg = ["st(5)"], st6: x87_reg = ["st(6)"], st7: x87_reg = ["st(7)"], + tmm0: tmm_reg = ["tmm0"] % x86_64_only, + tmm1: tmm_reg = ["tmm1"] % x86_64_only, + tmm2: tmm_reg = ["tmm2"] % x86_64_only, + tmm3: tmm_reg = ["tmm3"] % x86_64_only, + tmm4: tmm_reg = ["tmm4"] % x86_64_only, + tmm5: tmm_reg = ["tmm5"] % x86_64_only, + tmm6: tmm_reg = ["tmm6"] % x86_64_only, + tmm7: tmm_reg = ["tmm7"] % x86_64_only, #error = ["bp", "bpl", "ebp", "rbp"] => "the frame pointer cannot be used as an operand for inline asm", #error = ["sp", "spl", "esp", "rsp"] => diff --git a/compiler/rustc_target/src/spec/aarch64_pc_windows_gnullvm.rs b/compiler/rustc_target/src/spec/aarch64_pc_windows_gnullvm.rs new file mode 100644 index 00000000000..59c6a95c2c5 --- /dev/null +++ b/compiler/rustc_target/src/spec/aarch64_pc_windows_gnullvm.rs @@ -0,0 +1,16 @@ +use crate::spec::Target; + +pub fn target() -> Target { + let mut base = super::windows_gnullvm_base::opts(); + base.max_atomic_width = Some(64); + base.features = "+neon,+fp-armv8".into(); + base.linker = Some("aarch64-w64-mingw32-clang".into()); + + Target { + llvm_target: "aarch64-pc-windows-gnu".into(), + pointer_width: 64, + data_layout: "e-m:w-p:64:64-i32:32-i64:64-i128:128-n32:64-S128".into(), + arch: "aarch64".into(), + options: base, + } +} diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 965a3c10983..832eeec3e8b 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -82,6 +82,7 @@ mod uefi_msvc_base; mod vxworks_base; mod wasm_base; mod windows_gnu_base; +mod windows_gnullvm_base; mod windows_msvc_base; mod windows_uwp_gnu_base; mod windows_uwp_msvc_base; @@ -939,6 +940,9 @@ supported_targets! { ("i686-uwp-windows-gnu", i686_uwp_windows_gnu), ("x86_64-uwp-windows-gnu", x86_64_uwp_windows_gnu), + ("aarch64-pc-windows-gnullvm", aarch64_pc_windows_gnullvm), + ("x86_64-pc-windows-gnullvm", x86_64_pc_windows_gnullvm), + ("aarch64-pc-windows-msvc", aarch64_pc_windows_msvc), ("aarch64-uwp-windows-msvc", aarch64_uwp_windows_msvc), ("x86_64-pc-windows-msvc", x86_64_pc_windows_msvc), diff --git a/compiler/rustc_target/src/spec/windows_gnullvm_base.rs b/compiler/rustc_target/src/spec/windows_gnullvm_base.rs new file mode 100644 index 00000000000..9f9f8be8718 --- /dev/null +++ b/compiler/rustc_target/src/spec/windows_gnullvm_base.rs @@ -0,0 +1,52 @@ +use crate::spec::{cvs, LinkArgs, LinkerFlavor, TargetOptions}; + +pub fn opts() -> TargetOptions { + let pre_link_args = LinkArgs::from([( + LinkerFlavor::Gcc, + vec![ + // We cannot use `-nodefaultlibs` because compiler-rt has to be passed + // as a path since it's not added to linker search path by the default. + // There were attemts to make it behave like libgcc (so one can just use -l<name>) + // but LLVM maintainers rejected it: https://reviews.llvm.org/D51440 + "-nolibc".into(), + "--unwindlib=none".into(), + ], + )]); + let late_link_args = LinkArgs::from([( + LinkerFlavor::Gcc, + // Order of `late_link_args*` does not matter with LLD. + vec![ + "-lmingw32".into(), + "-lmingwex".into(), + "-lmsvcrt".into(), + "-lkernel32".into(), + "-luser32".into(), + ], + )]); + + TargetOptions { + os: "windows".into(), + env: "gnu".into(), + vendor: "pc".into(), + abi: "llvm".into(), + linker: Some("clang".into()), + dynamic_linking: true, + executables: true, + dll_prefix: "".into(), + dll_suffix: ".dll".into(), + exe_suffix: ".exe".into(), + families: cvs!["windows"], + is_like_windows: true, + allows_weak_linkage: false, + pre_link_args, + late_link_args, + abi_return_struct_as_int: true, + emit_debug_gdb_scripts: false, + requires_uwtable: true, + eh_frame_header: false, + no_default_libraries: false, + has_thread_local: true, + + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/x86_64_pc_windows_gnullvm.rs b/compiler/rustc_target/src/spec/x86_64_pc_windows_gnullvm.rs new file mode 100644 index 00000000000..b5ff63e0532 --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_pc_windows_gnullvm.rs @@ -0,0 +1,19 @@ +use crate::spec::{LinkerFlavor, Target}; + +pub fn target() -> Target { + let mut base = super::windows_gnullvm_base::opts(); + base.cpu = "x86-64".into(); + let gcc_pre_link_args = base.pre_link_args.entry(LinkerFlavor::Gcc).or_default(); + gcc_pre_link_args.push("-m64".into()); + base.max_atomic_width = Some(64); + base.linker = Some("x86_64-w64-mingw32-clang".into()); + + Target { + llvm_target: "x86_64-pc-windows-gnu".into(), + pointer_width: 64, + data_layout: "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .into(), + arch: "x86_64".into(), + options: base, + } +} diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index 0dd497448ca..f46e8ff0004 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -14,7 +14,6 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(box_patterns)] #![feature(control_flow_enum)] -#![feature(crate_visibility_modifier)] #![cfg_attr(bootstrap, feature(derive_default_enum))] #![feature(drain_filter)] #![feature(hash_drain_filter)] diff --git a/compiler/rustc_trait_selection/src/opaque_types.rs b/compiler/rustc_trait_selection/src/opaque_types.rs index 4b949ff8b95..452b0d73c97 100644 --- a/compiler/rustc_trait_selection/src/opaque_types.rs +++ b/compiler/rustc_trait_selection/src/opaque_types.rs @@ -1,11 +1,15 @@ use crate::traits; +use crate::traits::error_reporting::InferCtxtExt as _; +use crate::traits::TraitEngineExt as _; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; +use rustc_hir::OpaqueTyOrigin; use rustc_infer::infer::error_reporting::unexpected_hidden_region_diagnostic; -use rustc_infer::infer::InferCtxt; +use rustc_infer::infer::{InferCtxt, TyCtxtInferExt as _}; +use rustc_infer::traits::{Obligation, ObligationCause, TraitEngine}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts}; -use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt}; +use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey, ToPredicate, Ty, TyCtxt}; use rustc_span::Span; pub trait InferCtxtExt<'tcx> { @@ -13,6 +17,7 @@ pub trait InferCtxtExt<'tcx> { &self, opaque_type_key: OpaqueTypeKey<'tcx>, instantiated_ty: OpaqueHiddenType<'tcx>, + origin: OpaqueTyOrigin, ) -> Ty<'tcx>; } @@ -22,11 +27,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { /// (*), computes the "definition type" for an opaque type /// definition -- that is, the inferred value of `Foo1<'x>` or /// `Foo2<'x>` that we would conceptually use in its definition: - /// - /// type Foo1<'x> = impl Bar<'x> = AAA; <-- this type AAA - /// type Foo2<'x> = impl Bar<'x> = BBB; <-- or this type BBB - /// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. } - /// + /// ```ignore (illustrative) + /// type Foo1<'x> = impl Bar<'x> = AAA; // <-- this type AAA + /// type Foo2<'x> = impl Bar<'x> = BBB; // <-- or this type BBB + /// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. } + /// ``` /// Note that these values are defined in terms of a distinct set of /// generic parameters (`'x` instead of `'a`) from C1 or C2. The main /// purpose of this function is to do that translation. @@ -45,6 +50,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { &self, opaque_type_key: OpaqueTypeKey<'tcx>, instantiated_ty: OpaqueHiddenType<'tcx>, + origin: OpaqueTyOrigin, ) -> Ty<'tcx> { if self.is_tainted_by_errors() { return self.tcx.ty_error(); @@ -76,8 +82,171 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { )); debug!(?definition_ty); - definition_ty + if !check_opaque_type_parameter_valid( + self.tcx, + opaque_type_key, + origin, + instantiated_ty.span, + ) { + return self.tcx.ty_error(); + } + + // Only check this for TAIT. RPIT already supports `src/test/ui/impl-trait/nested-return-type2.rs` + // on stable and we'd break that. + if let OpaqueTyOrigin::TyAlias = origin { + // This logic duplicates most of `check_opaque_meets_bounds`. + // FIXME(oli-obk): Also do region checks here and then consider removing `check_opaque_meets_bounds` entirely. + let param_env = self.tcx.param_env(def_id); + let body_id = self.tcx.local_def_id_to_hir_id(def_id.as_local().unwrap()); + self.tcx.infer_ctxt().enter(move |infcx| { + // Require the hidden type to be well-formed with only the generics of the opaque type. + // Defining use functions may have more bounds than the opaque type, which is ok, as long as the + // hidden type is well formed even without those bounds. + let predicate = + ty::Binder::dummy(ty::PredicateKind::WellFormed(definition_ty.into())) + .to_predicate(infcx.tcx); + let mut fulfillment_cx = <dyn TraitEngine<'tcx>>::new(infcx.tcx); + + // Require that the hidden type actually fulfills all the bounds of the opaque type, even without + // the bounds that the function supplies. + match infcx.register_hidden_type( + OpaqueTypeKey { def_id, substs: id_substs }, + ObligationCause::misc(instantiated_ty.span, body_id), + param_env, + definition_ty, + origin, + ) { + Ok(infer_ok) => { + for obligation in infer_ok.obligations { + fulfillment_cx.register_predicate_obligation(&infcx, obligation); + } + } + Err(err) => { + infcx + .report_mismatched_types( + &ObligationCause::misc(instantiated_ty.span, body_id), + self.tcx.mk_opaque(def_id, id_substs), + definition_ty, + err, + ) + .emit(); + } + } + + fulfillment_cx.register_predicate_obligation( + &infcx, + Obligation::misc(instantiated_ty.span, body_id, param_env, predicate), + ); + + // Check that all obligations are satisfied by the implementation's + // version. + let errors = fulfillment_cx.select_all_or_error(&infcx); + + let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); + + if errors.is_empty() { + definition_ty + } else { + infcx.report_fulfillment_errors(&errors, None, false); + self.tcx.ty_error() + } + }) + } else { + definition_ty + } + } +} + +fn check_opaque_type_parameter_valid( + tcx: TyCtxt<'_>, + opaque_type_key: OpaqueTypeKey<'_>, + origin: OpaqueTyOrigin, + span: Span, +) -> bool { + match origin { + // No need to check return position impl trait (RPIT) + // because for type and const parameters they are correct + // by construction: we convert + // + // fn foo<P0..Pn>() -> impl Trait + // + // into + // + // type Foo<P0...Pn> + // fn foo<P0..Pn>() -> Foo<P0...Pn>. + // + // For lifetime parameters we convert + // + // fn foo<'l0..'ln>() -> impl Trait<'l0..'lm> + // + // into + // + // type foo::<'p0..'pn>::Foo<'q0..'qm> + // fn foo<l0..'ln>() -> foo::<'static..'static>::Foo<'l0..'lm>. + // + // which would error here on all of the `'static` args. + OpaqueTyOrigin::FnReturn(..) | OpaqueTyOrigin::AsyncFn(..) => return true, + // Check these + OpaqueTyOrigin::TyAlias => {} + } + let opaque_generics = tcx.generics_of(opaque_type_key.def_id); + let mut seen_params: FxHashMap<_, Vec<_>> = FxHashMap::default(); + for (i, arg) in opaque_type_key.substs.iter().enumerate() { + let arg_is_param = match arg.unpack() { + GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)), + GenericArgKind::Lifetime(lt) if lt.is_static() => { + tcx.sess + .struct_span_err(span, "non-defining opaque type use in defining scope") + .span_label( + tcx.def_span(opaque_generics.param_at(i, tcx).def_id), + "cannot use static lifetime; use a bound lifetime \ + instead or remove the lifetime parameter from the \ + opaque type", + ) + .emit(); + return false; + } + GenericArgKind::Lifetime(lt) => { + matches!(*lt, ty::ReEarlyBound(_) | ty::ReFree(_)) + } + GenericArgKind::Const(ct) => matches!(ct.val(), ty::ConstKind::Param(_)), + }; + + if arg_is_param { + seen_params.entry(arg).or_default().push(i); + } else { + // Prevent `fn foo() -> Foo<u32>` from being defining. + let opaque_param = opaque_generics.param_at(i, tcx); + tcx.sess + .struct_span_err(span, "non-defining opaque type use in defining scope") + .span_note( + tcx.def_span(opaque_param.def_id), + &format!( + "used non-generic {} `{}` for generic parameter", + opaque_param.kind.descr(), + arg, + ), + ) + .emit(); + return false; + } + } + + for (_, indices) in seen_params { + if indices.len() > 1 { + let descr = opaque_generics.param_at(indices[0], tcx).kind.descr(); + let spans: Vec<_> = indices + .into_iter() + .map(|i| tcx.def_span(opaque_generics.param_at(i, tcx).def_id)) + .collect(); + tcx.sess + .struct_span_err(span, "non-defining opaque type use in defining scope") + .span_note(spans, &format!("{} used multiple times", descr)) + .emit(); + return false; + } } + true } struct ReverseMapper<'tcx> { @@ -331,7 +500,7 @@ impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> { /// Requires that trait definitions have been processed so that we can /// elaborate predicates and walk supertraits. #[instrument(skip(tcx, predicates), level = "debug")] -crate fn required_region_bounds<'tcx>( +pub(crate) fn required_region_bounds<'tcx>( tcx: TyCtxt<'tcx>, erased_self_ty: Ty<'tcx>, predicates: impl Iterator<Item = ty::Predicate<'tcx>>, diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index d95512bb88f..79e9635f90e 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -255,9 +255,9 @@ impl<'tcx> AutoTraitFinder<'tcx> { /// `FulfillmentContext` will drive `SelectionContext` to consider that impl before giving up. /// If we were to rely on `FulfillmentContext`s decision, we might end up synthesizing an impl /// like this: - /// - /// impl<T> Send for Foo<T> where T: IntoIterator - /// + /// ```ignore (illustrative) + /// impl<T> Send for Foo<T> where T: IntoIterator + /// ``` /// While it might be technically true that Foo implements Send where `T: IntoIterator`, /// the bound is overly restrictive - it's really only necessary that `T: Iterator`. /// @@ -420,10 +420,10 @@ impl<'tcx> AutoTraitFinder<'tcx> { /// two trait predicates that differ only in their region parameters: /// one containing a HRTB lifetime parameter, and one containing a 'normal' /// lifetime parameter. For example: - /// - /// T as MyTrait<'a> - /// T as MyTrait<'static> - /// + /// ```ignore (illustrative) + /// T as MyTrait<'a> + /// T as MyTrait<'static> + /// ``` /// If we put both of these predicates in our computed `ParamEnv`, we'll /// confuse `SelectionContext`, since it will (correctly) view both as being applicable. /// diff --git a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs index 93c2f202545..592b0ab477a 100644 --- a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs @@ -17,7 +17,7 @@ pub struct FulfillmentContext<'tcx> { } impl FulfillmentContext<'_> { - crate fn new() -> Self { + pub(crate) fn new() -> Self { FulfillmentContext { obligations: FxIndexSet::default(), relationships: FxHashMap::default(), diff --git a/compiler/rustc_trait_selection/src/traits/codegen.rs b/compiler/rustc_trait_selection/src/traits/codegen.rs index c76a6542ca1..6ca630b74cc 100644 --- a/compiler/rustc_trait_selection/src/traits/codegen.rs +++ b/compiler/rustc_trait_selection/src/traits/codegen.rs @@ -3,13 +3,12 @@ // seems likely that they should eventually be merged into more // general routines. -use crate::infer::{InferCtxt, TyCtxtInferExt}; +use crate::infer::TyCtxtInferExt; use crate::traits::{ FulfillmentContext, ImplSource, Obligation, ObligationCause, SelectionContext, TraitEngine, Unimplemented, }; -use rustc_errors::ErrorGuaranteed; -use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::traits::CodegenObligationError; use rustc_middle::ty::{self, TyCtxt}; /// Attempts to resolve an obligation to an `ImplSource`. The result is @@ -23,7 +22,7 @@ use rustc_middle::ty::{self, TyCtxt}; pub fn codegen_fulfill_obligation<'tcx>( tcx: TyCtxt<'tcx>, (param_env, trait_ref): (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>), -) -> Result<&'tcx ImplSource<'tcx, ()>, ErrorGuaranteed> { +) -> Result<&'tcx ImplSource<'tcx, ()>, CodegenObligationError> { // Remove any references to regions; this helps improve caching. let trait_ref = tcx.erase_regions(trait_ref); // We expect the input to be fully normalized. @@ -40,37 +39,8 @@ pub fn codegen_fulfill_obligation<'tcx>( let selection = match selcx.select(&obligation) { Ok(Some(selection)) => selection, - Ok(None) => { - // Ambiguity can happen when monomorphizing during trans - // expands to some humongous type that never occurred - // statically -- this humongous type can then overflow, - // leading to an ambiguous result. So report this as an - // overflow bug, since I believe this is the only case - // where ambiguity can result. - let reported = infcx.tcx.sess.delay_span_bug( - rustc_span::DUMMY_SP, - &format!( - "encountered ambiguity selecting `{:?}` during codegen, presuming due to \ - overflow or prior type error", - trait_ref - ), - ); - return Err(reported); - } - Err(Unimplemented) => { - // This can trigger when we probe for the source of a `'static` lifetime requirement - // on a trait object: `impl Foo for dyn Trait {}` has an implicit `'static` bound. - // This can also trigger when we have a global bound that is not actually satisfied, - // but was included during typeck due to the trivial_bounds feature. - let guar = infcx.tcx.sess.delay_span_bug( - rustc_span::DUMMY_SP, - &format!( - "Encountered error `Unimplemented` selecting `{:?}` during codegen", - trait_ref - ), - ); - return Err(guar); - } + Ok(None) => return Err(CodegenObligationError::Ambiguity), + Err(Unimplemented) => return Err(CodegenObligationError::Unimplemented), Err(e) => { bug!("Encountered error `{:?}` selecting `{:?}` during codegen", e, trait_ref) } @@ -85,7 +55,17 @@ pub fn codegen_fulfill_obligation<'tcx>( let impl_source = selection.map(|predicate| { fulfill_cx.register_predicate_obligation(&infcx, predicate); }); - let impl_source = drain_fulfillment_cx_or_panic(&infcx, &mut fulfill_cx, impl_source); + + // In principle, we only need to do this so long as `impl_source` + // contains unbound type parameters. It could be a slight + // optimization to stop iterating early. + let errors = fulfill_cx.select_all_or_error(&infcx); + if !errors.is_empty() { + return Err(CodegenObligationError::FulfillmentError); + } + + let impl_source = infcx.resolve_vars_if_possible(impl_source); + let impl_source = infcx.tcx.erase_regions(impl_source); // Opaque types may have gotten their hidden types constrained, but we can ignore them safely // as they will get constrained elsewhere, too. @@ -95,42 +75,3 @@ pub fn codegen_fulfill_obligation<'tcx>( Ok(&*tcx.arena.alloc(impl_source)) }) } - -// # Global Cache - -/// Finishes processes any obligations that remain in the -/// fulfillment context, and then returns the result with all type -/// variables removed and regions erased. Because this is intended -/// for use outside of type inference, if any errors occur, -/// it will panic. It is used during normalization and other cases -/// where processing the obligations in `fulfill_cx` may cause -/// type inference variables that appear in `result` to be -/// unified, and hence we need to process those obligations to get -/// the complete picture of the type. -fn drain_fulfillment_cx_or_panic<'tcx, T>( - infcx: &InferCtxt<'_, 'tcx>, - fulfill_cx: &mut FulfillmentContext<'tcx>, - result: T, -) -> T -where - T: TypeFoldable<'tcx>, -{ - debug!("drain_fulfillment_cx_or_panic()"); - - // In principle, we only need to do this so long as `result` - // contains unbound type parameters. It could be a slight - // optimization to stop iterating early. - let errors = fulfill_cx.select_all_or_error(infcx); - if !errors.is_empty() { - infcx.tcx.sess.delay_span_bug( - rustc_span::DUMMY_SP, - &format!( - "Encountered errors `{:?}` resolving bounds outside of type inference", - errors - ), - ); - } - - let result = infcx.resolve_vars_if_possible(result); - infcx.tcx.erase_regions(result) -} diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 40c3f4047ae..a7893c0193f 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -88,8 +88,8 @@ where impl2_ref.iter().flat_map(|tref| tref.substs.types()), ) .any(|(ty1, ty2)| { - let t1 = fast_reject::simplify_type(tcx, ty1, TreatParams::AsPlaceholders); - let t2 = fast_reject::simplify_type(tcx, ty2, TreatParams::AsPlaceholders); + let t1 = fast_reject::simplify_type(tcx, ty1, TreatParams::AsInfer); + let t2 = fast_reject::simplify_type(tcx, ty2, TreatParams::AsInfer); if let (Some(t1), Some(t2)) = (t1, t2) { // Simplified successfully @@ -135,8 +135,8 @@ fn with_fresh_ty_vars<'cx, 'tcx>( let header = ty::ImplHeader { impl_def_id, - self_ty: tcx.type_of(impl_def_id).subst(tcx, impl_substs), - trait_ref: tcx.impl_trait_ref(impl_def_id).subst(tcx, impl_substs), + self_ty: tcx.bound_type_of(impl_def_id).subst(tcx, impl_substs), + trait_ref: tcx.bound_impl_trait_ref(impl_def_id).map(|i| i.subst(tcx, impl_substs)), predicates: tcx.predicates_of(impl_def_id).instantiate(tcx, impl_substs).predicates, }; @@ -547,7 +547,7 @@ pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanChe /// 2. They ground negative reasoning for coherence. If a user wants to /// write both a conditional blanket impl and a specific impl, we need to /// make sure they do not overlap. For example, if we write -/// ``` +/// ```ignore (illustrative) /// impl<T> IntoIterator for Vec<T> /// impl<T: Iterator> IntoIterator for T /// ``` @@ -645,7 +645,7 @@ fn orphan_check_trait_ref<'tcx>( .substs .types() .flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate)) - .find(|ty| ty_is_local_constructor(*ty, in_crate)); + .find(|&ty| ty_is_local_constructor(tcx, ty, in_crate)); debug!("orphan_check_trait_ref: uncovered ty local_type: `{:?}`", local_type); @@ -677,7 +677,7 @@ fn contained_non_local_types<'tcx>( ty: Ty<'tcx>, in_crate: InCrate, ) -> Vec<Ty<'tcx>> { - if ty_is_local_constructor(ty, in_crate) { + if ty_is_local_constructor(tcx, ty, in_crate) { Vec::new() } else { match fundamental_ty_inner_tys(tcx, ty) { @@ -730,7 +730,7 @@ fn def_id_is_local(def_id: DefId, in_crate: InCrate) -> bool { } } -fn ty_is_local_constructor(ty: Ty<'_>, in_crate: InCrate) -> bool { +fn ty_is_local_constructor(tcx: TyCtxt<'_>, ty: Ty<'_>, in_crate: InCrate) -> bool { debug!("ty_is_local_constructor({:?})", ty); match *ty.kind() { @@ -789,11 +789,6 @@ fn ty_is_local_constructor(ty: Ty<'_>, in_crate: InCrate) -> bool { false } - ty::Closure(..) => { - // Similar to the `Opaque` case (#83613). - false - } - ty::Dynamic(ref tt, ..) => { if let Some(principal) = tt.principal() { def_id_is_local(principal.def_id(), in_crate) @@ -804,8 +799,20 @@ fn ty_is_local_constructor(ty: Ty<'_>, in_crate: InCrate) -> bool { ty::Error(_) => true, - ty::Generator(..) | ty::GeneratorWitness(..) => { - bug!("ty_is_local invoked on unexpected type: {:?}", ty) + // These variants should never appear during coherence checking because they + // cannot be named directly. + // + // They could be indirectly used through an opaque type. While using opaque types + // in impls causes an error, this path can still be hit afterwards. + // + // See `test/ui/coherence/coherence-with-closure.rs` for an example where this + // could happens. + ty::Closure(..) | ty::Generator(..) | ty::GeneratorWitness(..) => { + tcx.sess.delay_span_bug( + DUMMY_SP, + format!("ty_is_local invoked on closure or generator: {:?}", ty), + ); + true } } } diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 5daa9796d12..27ce08ea045 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -19,7 +19,7 @@ use rustc_middle::mir::interpret::{ use rustc_middle::thir; use rustc_middle::thir::abstract_const::{self, Node, NodeId, NotConstEvaluatable}; use rustc_middle::ty::subst::{Subst, SubstsRef}; -use rustc_middle::ty::{self, DelaySpanBugEmitted, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, DelaySpanBugEmitted, EarlyBinder, TyCtxt, TypeFoldable}; use rustc_session::lint; use rustc_span::def_id::LocalDefId; use rustc_span::Span; @@ -263,8 +263,10 @@ impl<'tcx> AbstractConst<'tcx> { pub fn root(self, tcx: TyCtxt<'tcx>) -> Node<'tcx> { let node = self.inner.last().copied().unwrap(); match node { - Node::Leaf(leaf) => Node::Leaf(leaf.subst(tcx, self.substs)), - Node::Cast(kind, operand, ty) => Node::Cast(kind, operand, ty.subst(tcx, self.substs)), + Node::Leaf(leaf) => Node::Leaf(EarlyBinder(leaf).subst(tcx, self.substs)), + Node::Cast(kind, operand, ty) => { + Node::Cast(kind, operand, EarlyBinder(ty).subst(tcx, self.substs)) + } // Don't perform substitution on the following as they can't directly contain generic params Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => node, } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 7a3579eb1cc..266fcc777ef 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -2,11 +2,10 @@ pub mod on_unimplemented; pub mod suggestions; use super::{ - DerivedObligationCause, EvaluationResult, FulfillmentContext, FulfillmentError, - FulfillmentErrorCode, ImplDerivedObligationCause, MismatchedProjectionTypes, Obligation, - ObligationCause, ObligationCauseCode, OnUnimplementedDirective, OnUnimplementedNote, - OutputTypeParameterMismatch, Overflow, PredicateObligation, SelectionContext, SelectionError, - TraitNotObjectSafe, + EvaluationResult, FulfillmentContext, FulfillmentError, FulfillmentErrorCode, + MismatchedProjectionTypes, Obligation, ObligationCause, ObligationCauseCode, + OnUnimplementedDirective, OnUnimplementedNote, OutputTypeParameterMismatch, Overflow, + PredicateObligation, SelectionContext, SelectionError, TraitNotObjectSafe, }; use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode}; @@ -684,42 +683,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let mut code = obligation.cause.code(); let mut trait_pred = trait_predicate; let mut peeled = false; - loop { - match &*code { - ObligationCauseCode::FunctionArgumentObligation { - parent_code, - .. - } => { - code = &parent_code; - } - ObligationCauseCode::ImplDerivedObligation( - box ImplDerivedObligationCause { - derived: - DerivedObligationCause { - parent_code, - parent_trait_pred, - }, - .. - }, - ) - | ObligationCauseCode::BuiltinDerivedObligation( - DerivedObligationCause { - parent_code, - parent_trait_pred, - }, - ) - | ObligationCauseCode::DerivedObligation( - DerivedObligationCause { - parent_code, - parent_trait_pred, - }, - ) => { - peeled = true; - code = &parent_code; - trait_pred = *parent_trait_pred; - } - _ => break, - }; + while let Some((parent_code, parent_trait_pred)) = code.parent() { + code = parent_code; + if let Some(parent_trait_pred) = parent_trait_pred { + trait_pred = parent_trait_pred; + peeled = true; + } } let def_id = trait_pred.def_id(); // Mention *all* the `impl`s for the *top most* obligation, the @@ -1415,8 +1384,7 @@ trait InferCtxtPrivExt<'hir, 'tcx> { fn mk_trait_obligation_with_new_self_ty( &self, param_env: ty::ParamEnv<'tcx>, - trait_ref: ty::PolyTraitPredicate<'tcx>, - new_self_ty: Ty<'tcx>, + trait_ref_and_ty: ty::Binder<'tcx, (ty::TraitPredicate<'tcx>, Ty<'tcx>)>, ) -> PredicateObligation<'tcx>; fn maybe_report_ambiguity( @@ -1954,14 +1922,11 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { fn mk_trait_obligation_with_new_self_ty( &self, param_env: ty::ParamEnv<'tcx>, - trait_ref: ty::PolyTraitPredicate<'tcx>, - new_self_ty: Ty<'tcx>, + trait_ref_and_ty: ty::Binder<'tcx, (ty::TraitPredicate<'tcx>, Ty<'tcx>)>, ) -> PredicateObligation<'tcx> { - assert!(!new_self_ty.has_escaping_bound_vars()); - - let trait_pred = trait_ref.map_bound_ref(|tr| ty::TraitPredicate { + let trait_pred = trait_ref_and_ty.map_bound_ref(|(tr, new_self_ty)| ty::TraitPredicate { trait_ref: ty::TraitRef { - substs: self.tcx.mk_substs_trait(new_self_ty, &tr.trait_ref.substs[1..]), + substs: self.tcx.mk_substs_trait(*new_self_ty, &tr.trait_ref.substs[1..]), ..tr.trait_ref }, ..*tr diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index 9e9c230aebb..4263a6fdf18 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -45,7 +45,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { self.tcx.for_each_relevant_impl(trait_ref.def_id, trait_self_ty, |def_id| { let impl_substs = self.fresh_substs_for_item(obligation.cause.span, def_id); - let impl_trait_ref = tcx.impl_trait_ref(def_id).unwrap().subst(tcx, impl_substs); + let impl_trait_ref = tcx.bound_impl_trait_ref(def_id).unwrap().subst(tcx, impl_substs); let impl_self_ty = impl_trait_ref.self_ty(); diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index bfb8ce6f105..a51e6e58f67 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1,6 +1,6 @@ use super::{ - DerivedObligationCause, EvaluationResult, ImplDerivedObligationCause, Obligation, - ObligationCause, ObligationCauseCode, PredicateObligation, SelectionContext, + EvaluationResult, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, + SelectionContext, }; use crate::autoderef::Autoderef; @@ -623,39 +623,26 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let span = obligation.cause.span; let mut real_trait_pred = trait_pred; let mut code = obligation.cause.code(); - loop { - match &code { - ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } => { - code = &parent_code; - } - ObligationCauseCode::ImplDerivedObligation(box ImplDerivedObligationCause { - derived: DerivedObligationCause { parent_code, parent_trait_pred }, - .. - }) - | ObligationCauseCode::BuiltinDerivedObligation(DerivedObligationCause { - parent_code, - parent_trait_pred, - }) - | ObligationCauseCode::DerivedObligation(DerivedObligationCause { - parent_code, - parent_trait_pred, - }) => { - code = &parent_code; - real_trait_pred = *parent_trait_pred; - } - _ => break, - }; - let Some(real_ty) = real_trait_pred.self_ty().no_bound_vars() else { - continue; - }; + while let Some((parent_code, parent_trait_pred)) = code.parent() { + code = parent_code; + if let Some(parent_trait_pred) = parent_trait_pred { + real_trait_pred = parent_trait_pred; + } + + // Skipping binder here, remapping below + let real_ty = real_trait_pred.self_ty().skip_binder(); if let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() { let mut autoderef = Autoderef::new(self, param_env, body_id, span, base_ty, span); if let Some(steps) = autoderef.find_map(|(ty, steps)| { // Re-add the `&` let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl }); - let obligation = - self.mk_trait_obligation_with_new_self_ty(param_env, real_trait_pred, ty); + + // Remapping bound vars here + let real_trait_pred_and_ty = + real_trait_pred.map_bound(|inner_trait_pred| (inner_trait_pred, ty)); + let obligation = self + .mk_trait_obligation_with_new_self_ty(param_env, real_trait_pred_and_ty); Some(steps).filter(|_| self.predicate_may_hold(&obligation)) }) { if steps > 0 { @@ -676,10 +663,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } } else if real_trait_pred != trait_pred { // This branch addresses #87437. + + // Remapping bound vars here + let real_trait_pred_and_base_ty = + real_trait_pred.map_bound(|inner_trait_pred| (inner_trait_pred, base_ty)); let obligation = self.mk_trait_obligation_with_new_self_ty( param_env, - real_trait_pred, - base_ty, + real_trait_pred_and_base_ty, ); if self.predicate_may_hold(&obligation) { err.span_suggestion_verbose( @@ -737,9 +727,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err: &mut Diagnostic, trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { - let Some(self_ty) = trait_pred.self_ty().no_bound_vars() else { - return false; - }; + // Skipping binder here, remapping below + let self_ty = trait_pred.self_ty().skip_binder(); let (def_id, output_ty, callable) = match *self_ty.kind() { ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig().output(), "closure"), @@ -748,14 +737,15 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { }; let msg = format!("use parentheses to call the {}", callable); - // `mk_trait_obligation_with_new_self_ty` only works for types with no escaping bound - // variables, so bail out if we have any. - let Some(output_ty) = output_ty.no_bound_vars() else { - return false; - }; + // "We should really create a single list of bound vars from the combined vars + // from the predicate and function, but instead we just liberate the function bound vars" + let output_ty = self.tcx.liberate_late_bound_regions(def_id, output_ty); + + // Remapping bound vars here + let trait_pred_and_self = trait_pred.map_bound(|trait_pred| (trait_pred, output_ty)); let new_obligation = - self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_pred, output_ty); + self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_pred_and_self); match self.evaluate_obligation(&new_obligation) { Ok( @@ -859,96 +849,97 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let param_env = obligation.param_env; // Try to apply the original trait binding obligation by borrowing. - let mut try_borrowing = |old_pred: ty::PolyTraitPredicate<'tcx>, - blacklist: &[DefId]| - -> bool { - if blacklist.contains(&old_pred.def_id()) { - return false; - } - - // This is a quick fix to resolve an ICE (#96223). - // This change should probably be deeper. - // As suggested by @jackh726, `mk_trait_obligation_with_new_self_ty` could take a `Binder<(TraitRef, Ty)> - // instead of `Binder<Ty>` leading to some changes to its call places. - let Some(orig_ty) = old_pred.self_ty().no_bound_vars() else { - return false; - }; - let mk_result = |new_ty| { - let obligation = - self.mk_trait_obligation_with_new_self_ty(param_env, old_pred, new_ty); - self.predicate_must_hold_modulo_regions(&obligation) - }; - let imm_result = mk_result(self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, orig_ty)); - let mut_result = mk_result(self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, orig_ty)); - - if imm_result || mut_result { - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { - // We have a very specific type of error, where just borrowing this argument - // might solve the problem. In cases like this, the important part is the - // original type obligation, not the last one that failed, which is arbitrary. - // Because of this, we modify the error to refer to the original obligation and - // return early in the caller. - - let msg = format!( - "the trait bound `{}: {}` is not satisfied", - orig_ty, - old_pred.print_modifiers_and_trait_path(), - ); - if has_custom_message { - err.note(&msg); - } else { - err.message = - vec![(rustc_errors::DiagnosticMessage::Str(msg), Style::NoStyle)]; - } - if snippet.starts_with('&') { - // This is already a literal borrow and the obligation is failing - // somewhere else in the obligation chain. Do not suggest non-sense. - return false; - } - err.span_label( - span, - &format!( - "expected an implementor of trait `{}`", - old_pred.print_modifiers_and_trait_path(), - ), - ); + let mut try_borrowing = + |old_pred: ty::PolyTraitPredicate<'tcx>, blacklist: &[DefId]| -> bool { + if blacklist.contains(&old_pred.def_id()) { + return false; + } + // We map bounds to `&T` and `&mut T` + let trait_pred_and_imm_ref = old_pred.map_bound(|trait_pred| { + ( + trait_pred, + self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()), + ) + }); + let trait_pred_and_mut_ref = old_pred.map_bound(|trait_pred| { + ( + trait_pred, + self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()), + ) + }); - // This if is to prevent a special edge-case - if matches!( - span.ctxt().outer_expn_data().kind, - ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop) - ) { - // We don't want a borrowing suggestion on the fields in structs, - // ``` - // struct Foo { - // the_foos: Vec<Foo> - // } - // ``` - - if imm_result && mut_result { - err.span_suggestions( - span.shrink_to_lo(), - "consider borrowing here", - ["&".to_string(), "&mut ".to_string()].into_iter(), - Applicability::MaybeIncorrect, - ); + let mk_result = |trait_pred_and_new_ty| { + let obligation = + self.mk_trait_obligation_with_new_self_ty(param_env, trait_pred_and_new_ty); + self.predicate_must_hold_modulo_regions(&obligation) + }; + let imm_result = mk_result(trait_pred_and_imm_ref); + let mut_result = mk_result(trait_pred_and_mut_ref); + + if imm_result || mut_result { + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + // We have a very specific type of error, where just borrowing this argument + // might solve the problem. In cases like this, the important part is the + // original type obligation, not the last one that failed, which is arbitrary. + // Because of this, we modify the error to refer to the original obligation and + // return early in the caller. + + let msg = format!("the trait bound `{}` is not satisfied", old_pred); + if has_custom_message { + err.note(&msg); } else { - err.span_suggestion_verbose( - span.shrink_to_lo(), - &format!( - "consider{} borrowing here", - if mut_result { " mutably" } else { "" } - ), - format!("&{}", if mut_result { "mut " } else { "" }), - Applicability::MaybeIncorrect, - ); + err.message = + vec![(rustc_errors::DiagnosticMessage::Str(msg), Style::NoStyle)]; } + if snippet.starts_with('&') { + // This is already a literal borrow and the obligation is failing + // somewhere else in the obligation chain. Do not suggest non-sense. + return false; + } + err.span_label( + span, + &format!( + "expected an implementor of trait `{}`", + old_pred.print_modifiers_and_trait_path(), + ), + ); + + // This if is to prevent a special edge-case + if matches!( + span.ctxt().outer_expn_data().kind, + ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop) + ) { + // We don't want a borrowing suggestion on the fields in structs, + // ``` + // struct Foo { + // the_foos: Vec<Foo> + // } + // ``` + + if imm_result && mut_result { + err.span_suggestions( + span.shrink_to_lo(), + "consider borrowing here", + ["&".to_string(), "&mut ".to_string()].into_iter(), + Applicability::MaybeIncorrect, + ); + } else { + err.span_suggestion_verbose( + span.shrink_to_lo(), + &format!( + "consider{} borrowing here", + if mut_result { " mutably" } else { "" } + ), + format!("&{}", if mut_result { "mut " } else { "" }), + Applicability::MaybeIncorrect, + ); + } + } + return true; } - return true; } - } - return false; - }; + return false; + }; if let ObligationCauseCode::ImplDerivedObligation(cause) = &*code { try_borrowing(cause.derived.parent_trait_pred, &[]) @@ -1009,9 +1000,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { return false; } - let Some(mut suggested_ty) = trait_pred.self_ty().no_bound_vars() else { - return false; - }; + // Skipping binder here, remapping below + let mut suggested_ty = trait_pred.self_ty().skip_binder(); for refs_remaining in 0..refs_number { let ty::Ref(_, inner_ty, _) = suggested_ty.kind() else { @@ -1019,10 +1009,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { }; suggested_ty = *inner_ty; + // Remapping bound vars here + let trait_pred_and_suggested_ty = + trait_pred.map_bound(|trait_pred| (trait_pred, suggested_ty)); + let new_obligation = self.mk_trait_obligation_with_new_self_ty( obligation.param_env, - trait_pred, - suggested_ty, + trait_pred_and_suggested_ty, ); if self.predicate_may_hold(&new_obligation) { @@ -1085,18 +1078,28 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { self.in_progress_typeck_results.map(|t| t.borrow()) && let ty = typeck_results.expr_ty_adjusted(base) && let ty::FnDef(def_id, _substs) = ty.kind() - && let Some(hir::Node::Item(hir::Item { span, ident, .. })) = + && let Some(hir::Node::Item(hir::Item { ident, span, vis_span, .. })) = hir.get_if_local(*def_id) { - err.span_suggestion_verbose( - span.shrink_to_lo(), - &format!( - "alternatively, consider making `fn {}` asynchronous", - ident - ), - "async ".to_string(), - Applicability::MaybeIncorrect, + let msg = format!( + "alternatively, consider making `fn {}` asynchronous", + ident ); + if vis_span.is_empty() { + err.span_suggestion_verbose( + span.shrink_to_lo(), + &msg, + "async ".to_string(), + Applicability::MaybeIncorrect, + ); + } else { + err.span_suggestion_verbose( + vis_span.shrink_to_hi(), + &msg, + " async".to_string(), + Applicability::MaybeIncorrect, + ); + } } } } @@ -1132,26 +1135,21 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { return; } + // Skipping binder here, remapping below if let ty::Ref(region, t_type, mutability) = *trait_pred.skip_binder().self_ty().kind() { - if region.is_late_bound() || t_type.has_escaping_bound_vars() { - // Avoid debug assertion in `mk_obligation_for_def_id`. - // - // If the self type has escaping bound vars then it's not - // going to be the type of an expression, so the suggestion - // probably won't apply anyway. - return; - } - let suggested_ty = match mutability { hir::Mutability::Mut => self.tcx.mk_imm_ref(region, t_type), hir::Mutability::Not => self.tcx.mk_mut_ref(region, t_type), }; + // Remapping bound vars here + let trait_pred_and_suggested_ty = + trait_pred.map_bound(|trait_pred| (trait_pred, suggested_ty)); + let new_obligation = self.mk_trait_obligation_with_new_self_ty( obligation.param_env, - trait_pred, - suggested_ty, + trait_pred_and_suggested_ty, ); let suggested_ty_would_satisfy_obligation = self .evaluate_obligation_no_overflow(&new_obligation) @@ -1202,7 +1200,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // Only suggest this if the expression behind the semicolon implements the predicate && let Some(typeck_results) = self.in_progress_typeck_results && let Some(ty) = typeck_results.borrow().expr_ty_opt(expr) - && self.predicate_may_hold(&self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_pred, ty)) + && self.predicate_may_hold(&self.mk_trait_obligation_with_new_self_ty( + obligation.param_env, trait_pred.map_bound(|trait_pred| (trait_pred, ty)) + )) { err.span_label( expr.span, @@ -1488,7 +1488,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { expected_ref: ty::PolyTraitRef<'tcx>, found: ty::PolyTraitRef<'tcx>, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - crate fn build_fn_sig_string<'tcx>( + pub(crate) fn build_fn_sig_string<'tcx>( tcx: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>, ) -> String { @@ -1659,7 +1659,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { debug!("maybe_note_obligation_cause_for_async_await: code={:?}", code); match code { ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } => { - next_code = Some(parent_code.as_ref()); + next_code = Some(parent_code); } ObligationCauseCode::ImplDerivedObligation(cause) => { let ty = cause.derived.parent_trait_pred.skip_binder().self_ty(); @@ -1690,7 +1690,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { _ => {} } - next_code = Some(cause.derived.parent_code.as_ref()); + next_code = Some(&cause.derived.parent_code); } ObligationCauseCode::DerivedObligation(derived_obligation) | ObligationCauseCode::BuiltinDerivedObligation(derived_obligation) => { @@ -1722,7 +1722,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { _ => {} } - next_code = Some(derived_obligation.parent_code.as_ref()); + next_code = Some(&derived_obligation.parent_code); } _ => break, } @@ -2372,8 +2372,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let is_upvar_tys_infer_tuple = if !matches!(ty.kind(), ty::Tuple(..)) { false } else { - if let ObligationCauseCode::BuiltinDerivedObligation(ref data) = - *data.parent_code + if let ObligationCauseCode::BuiltinDerivedObligation(data) = &*data.parent_code { let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_pred); @@ -2418,7 +2417,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err, &parent_predicate, param_env, - &cause_code.peel_derives(), + cause_code.peel_derives(), obligated_types, seen_requirements, ) @@ -2735,8 +2734,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ); let try_obligation = self.mk_trait_obligation_with_new_self_ty( obligation.param_env, - trait_pred, - normalized_ty.ty().unwrap(), + trait_pred.map_bound(|trait_pred| (trait_pred, normalized_ty.ty().unwrap())), ); debug!("suggest_await_before_try: try_trait_obligation {:?}", try_obligation); if self.predicate_may_hold(&try_obligation) diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 8240f5c542a..81819534e8b 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -432,6 +432,9 @@ pub fn impossible_predicates<'tcx>( debug!("impossible_predicates(predicates={:?})", predicates); let result = tcx.infer_ctxt().enter(|infcx| { + // HACK: Set tainted by errors to gracefully exit in case of overflow. + infcx.set_tainted_by_errors(); + let param_env = ty::ParamEnv::reveal_all(); let mut selcx = SelectionContext::new(&infcx); let mut fulfill_cx = FulfillmentContext::new(); @@ -448,6 +451,9 @@ pub fn impossible_predicates<'tcx>( let errors = fulfill_cx.select_all_or_error(&infcx); + // Clean up after ourselves + let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); + !errors.is_empty() }); debug!("impossible_predicates = {:?}", result); @@ -461,6 +467,14 @@ fn subst_and_check_impossible_predicates<'tcx>( debug!("subst_and_check_impossible_predicates(key={:?})", key); let mut predicates = tcx.predicates_of(key.0).instantiate(tcx, key.1).predicates; + + // Specifically check trait fulfillment to avoid an error when trying to resolve + // associated items. + if let Some(trait_def_id) = tcx.trait_of_item(key.0) { + let trait_ref = ty::TraitRef::from_method(tcx, trait_def_id, key.1); + predicates.push(ty::Binder::dummy(trait_ref).to_poly_trait_predicate().to_predicate(tcx)); + } + predicates.retain(|predicate| !predicate.needs_subst()); let result = impossible_predicates(tcx, predicates); diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index b39310d1294..e3e384798d3 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -18,7 +18,7 @@ use rustc_errors::{FatalError, MultiSpan}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::ty::subst::{GenericArg, InternalSubsts, Subst}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitor}; +use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt, TypeFoldable, TypeVisitor}; use rustc_middle::ty::{Predicate, ToPredicate}; use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY; use rustc_span::symbol::Symbol; @@ -531,7 +531,7 @@ fn receiver_for_self_ty<'tcx>( if param.index == 0 { self_ty.into() } else { tcx.mk_param_from_def(param) } }); - let result = receiver_ty.subst(tcx, substs); + let result = EarlyBinder(receiver_ty).subst(tcx, substs); debug!( "receiver_for_self_ty({:?}, {:?}, {:?}) = {:?}", receiver_ty, self_ty, method_def_id, result @@ -595,7 +595,7 @@ fn object_ty_for_trait<'tcx>( /// - let `Receiver` be the type of the `self` argument, i.e `Self`, `&Self`, `Rc<Self>`, /// - require the following bound: /// -/// ``` +/// ```ignore (not-rust) /// Receiver[Self => T]: DispatchFromDyn<Receiver[Self => dyn Trait]> /// ``` /// @@ -621,13 +621,13 @@ fn object_ty_for_trait<'tcx>( /// Instead, we fudge a little by introducing a new type parameter `U` such that /// `Self: Unsize<U>` and `U: Trait + ?Sized`, and use `U` in place of `dyn Trait`. /// Written as a chalk-style query: -/// -/// forall (U: Trait + ?Sized) { -/// if (Self: Unsize<U>) { -/// Receiver: DispatchFromDyn<Receiver[Self => U]> -/// } +/// ```ignore (not-rust) +/// forall (U: Trait + ?Sized) { +/// if (Self: Unsize<U>) { +/// Receiver: DispatchFromDyn<Receiver[Self => U]> /// } -/// +/// } +/// ``` /// for `self: &'a mut Self`, this means `&'a mut Self: DispatchFromDyn<&'a mut U>` /// for `self: Rc<Self>`, this means `Rc<Self>: DispatchFromDyn<Rc<U>>` /// for `self: Pin<Box<Self>>`, this means `Pin<Box<Self>>: DispatchFromDyn<Pin<Box<U>>>` diff --git a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs index c266eec25aa..7d418198195 100644 --- a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs @@ -175,9 +175,7 @@ impl<'tcx> OnUnimplementedDirective { } pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<Option<Self>, ErrorGuaranteed> { - let attrs = tcx.get_attrs(item_def_id); - - let Some(attr) = tcx.sess.find_by_name(&attrs, sym::rustc_on_unimplemented) else { + let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) else { return Ok(None); }; diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index c7a61cbe25a..a71621a4d52 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -31,7 +31,7 @@ use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::fold::{MaxUniverse, TypeFoldable, TypeFolder}; use rustc_middle::ty::subst::Subst; -use rustc_middle::ty::{self, Term, ToPredicate, Ty, TyCtxt}; +use rustc_middle::ty::{self, EarlyBinder, Term, ToPredicate, Ty, TyCtxt}; use rustc_span::symbol::sym; use std::collections::BTreeMap; @@ -158,9 +158,9 @@ pub(super) enum ProjectAndUnifyResult<'tcx> { } /// Evaluates constraints of the form: -/// -/// for<...> <T as Trait>::U == V -/// +/// ```ignore (not-rust) +/// for<...> <T as Trait>::U == V +/// ``` /// If successful, this may result in additional obligations. Also returns /// the projection cache key used to track these additional obligations. /// @@ -224,9 +224,9 @@ pub(super) fn poly_project_and_unify_type<'cx, 'tcx>( } /// Evaluates constraints of the form: -/// -/// <T as Trait>::U == V -/// +/// ```ignore (not-rust) +/// <T as Trait>::U == V +/// ``` /// If successful, this may result in additional obligations. /// /// See [poly_project_and_unify_type] for an explanation of the return value. @@ -515,7 +515,7 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { } let substs = substs.super_fold_with(self); - let generic_ty = self.tcx().type_of(def_id); + let generic_ty = self.tcx().bound_type_of(def_id); let concrete_ty = generic_ty.subst(self.tcx(), substs); self.depth += 1; let folded_ty = self.fold_ty(concrete_ty); @@ -1258,7 +1258,7 @@ fn assemble_candidates_from_param_env<'cx, 'tcx>( /// In the case of a nested projection like <<A as Foo>::FooT as Bar>::BarT, we may find /// that the definition of `Foo` has some clues: /// -/// ``` +/// ```ignore (illustrative) /// trait Foo { /// type FooT : Bar<BarT=i32> /// } @@ -1276,8 +1276,8 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>( // Check whether the self-type is itself a projection. // If so, extract what we know from the trait and try to come up with a good answer. let bounds = match *obligation.predicate.self_ty().kind() { - ty::Projection(ref data) => tcx.item_bounds(data.item_def_id).subst(tcx, data.substs), - ty::Opaque(def_id, substs) => tcx.item_bounds(def_id).subst(tcx, substs), + ty::Projection(ref data) => tcx.bound_item_bounds(data.item_def_id).subst(tcx, data.substs), + ty::Opaque(def_id, substs) => tcx.bound_item_bounds(def_id).subst(tcx, substs), ty::Infer(ty::TyVar(_)) => { // If the self-type is an inference variable, then it MAY wind up // being a projected type, so induce an ambiguity. @@ -2032,7 +2032,7 @@ fn confirm_impl_candidate<'cx, 'tcx>( Progress { term: err.into(), obligations: nested } } else { assoc_ty_own_obligations(selcx, obligation, &mut nested); - Progress { term: term.subst(tcx, substs), obligations: nested } + Progress { term: EarlyBinder(term).subst(tcx, substs), obligations: nested } } } @@ -2116,7 +2116,7 @@ fn assoc_def( } } -crate trait ProjectionCacheKeyExt<'cx, 'tcx>: Sized { +pub(crate) trait ProjectionCacheKeyExt<'cx, 'tcx>: Sized { fn from_poly_projection_predicate( selcx: &mut SelectionContext<'cx, 'tcx>, predicate: ty::PolyProjectionPredicate<'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index ed0ad5601aa..6a81a7764af 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -217,7 +217,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { self.infcx.report_overflow_error(&obligation, true); } - let generic_ty = self.tcx().type_of(def_id); + let generic_ty = self.tcx().bound_type_of(def_id); let concrete_ty = generic_ty.subst(self.tcx(), substs); self.anon_depth += 1; if concrete_ty == ty { diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index b97ab39d991..d607f4e7642 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -12,22 +12,20 @@ use rustc_index::bit_set::GrowableBitSet; use rustc_infer::infer::InferOk; use rustc_infer::infer::LateBoundRegionConversionTime::HigherRankedType; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst, SubstsRef}; -use rustc_middle::ty::{self, GenericParamDefKind, Ty}; +use rustc_middle::ty::{self, EarlyBinder, GenericParamDefKind, Ty}; use rustc_middle::ty::{ToPolyTraitRef, ToPredicate}; use rustc_span::def_id::DefId; use crate::traits::project::{normalize_with_depth, normalize_with_depth_to}; -use crate::traits::select::TraitObligationExt; use crate::traits::util::{self, closure_trait_ref_and_return_type, predicate_for_trait_def}; use crate::traits::{ - BuiltinDerivedObligation, DerivedObligationCause, ImplDerivedObligation, - ImplDerivedObligationCause, ImplSource, ImplSourceAutoImplData, ImplSourceBuiltinData, - ImplSourceClosureData, ImplSourceConstDestructData, ImplSourceDiscriminantKindData, - ImplSourceFnPointerData, ImplSourceGeneratorData, ImplSourceObjectData, ImplSourcePointeeData, - ImplSourceTraitAliasData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, Normalized, - ObjectCastObligation, Obligation, ObligationCause, OutputTypeParameterMismatch, - PredicateObligation, Selection, SelectionError, TraitNotObjectSafe, TraitObligation, - Unimplemented, VtblSegment, + BuiltinDerivedObligation, ImplDerivedObligation, ImplDerivedObligationCause, ImplSource, + ImplSourceAutoImplData, ImplSourceBuiltinData, ImplSourceClosureData, + ImplSourceConstDestructData, ImplSourceDiscriminantKindData, ImplSourceFnPointerData, + ImplSourceGeneratorData, ImplSourceObjectData, ImplSourcePointeeData, ImplSourceTraitAliasData, + ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, Normalized, ObjectCastObligation, + Obligation, ObligationCause, OutputTypeParameterMismatch, PredicateObligation, Selection, + SelectionError, TraitNotObjectSafe, TraitObligation, Unimplemented, VtblSegment, }; use super::BuiltinImplConditions; @@ -174,7 +172,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { _ => bug!("projection candidate for unexpected type: {:?}", placeholder_self_ty), }; - let candidate_predicate = tcx.item_bounds(def_id)[idx].subst(tcx, substs); + let candidate_predicate = + tcx.bound_item_bounds(def_id).map_bound(|i| i[idx]).subst(tcx, substs); let candidate = candidate_predicate .to_opt_poly_trait_pred() .expect("projection candidate is not a trait predicate") @@ -500,7 +499,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // This maybe belongs in wf, but that can't (doesn't) handle // higher-ranked things. // Prevent, e.g., `dyn Iterator<Item = str>`. - for bound in self.tcx().item_bounds(assoc_type) { + for bound in self.tcx().bound_item_bounds(assoc_type).transpose_iter() { let subst_bound = if defs.count() == 0 { bound.subst(tcx, trait_predicate.trait_ref.substs) @@ -509,9 +508,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { substs.extend(trait_predicate.trait_ref.substs.iter()); let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind; 8]> = smallvec::SmallVec::with_capacity( - bound.kind().bound_vars().len() + defs.count(), + bound.0.kind().bound_vars().len() + defs.count(), ); - bound_vars.extend(bound.kind().bound_vars().into_iter()); + bound_vars.extend(bound.0.kind().bound_vars().into_iter()); InternalSubsts::fill_single(&mut substs, defs, &mut |param, _| match param .kind { @@ -558,7 +557,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let assoc_ty_substs = tcx.intern_substs(&substs); let bound_vars = tcx.mk_bound_variable_kinds(bound_vars.into_iter()); - let bound = bound.kind().skip_binder().subst(tcx, assoc_ty_substs); + let bound = + EarlyBinder(bound.0.kind().skip_binder()).subst(tcx, assoc_ty_substs); tcx.mk_predicate(ty::Binder::bind_with_vars(bound, bound_vars)) }; let normalized_bound = normalize_with_depth_to( @@ -712,9 +712,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// and we desugared it so that the type of the expression is /// `Closure`, and `Closure` expects `i32` as argument. Then it /// is "as if" the compiler generated this impl: - /// - /// impl Fn(i32) for Closure { ... } - /// + /// ```ignore (illustrative) + /// impl Fn(i32) for Closure { ... } + /// ``` /// Now imagine our obligation is `Closure: Fn(usize)`. So far /// we have matched the self type `Closure`. At this point we'll /// compare the `i32` to `usize` and generate an error. @@ -1005,10 +1005,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // The last field of the structure has to exist and contain type/const parameters. let (tail_field, prefix_fields) = def.non_enum_variant().fields.split_last().ok_or(Unimplemented)?; - let tail_field_ty = tcx.type_of(tail_field.did); + let tail_field_ty = tcx.bound_type_of(tail_field.did); let mut unsizing_params = GrowableBitSet::new_empty(); - for arg in tail_field_ty.walk() { + for arg in tail_field_ty.0.walk() { if let Some(i) = maybe_unsizing_param_idx(arg) { unsizing_params.insert(i); } @@ -1126,21 +1126,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let substs = self.rematch_impl(impl_def_id, &new_obligation); debug!(?substs, "impl substs"); - let derived = DerivedObligationCause { - parent_trait_pred: obligation.predicate, - parent_code: obligation.cause.clone_code(), - }; - let derived_code = ImplDerivedObligation(Box::new(ImplDerivedObligationCause { - derived, - impl_def_id, - span: obligation.cause.span, - })); - - let cause = ObligationCause::new( - obligation.cause.span, - obligation.cause.body_id, - derived_code, - ); + let cause = obligation.derived_cause(|derived| { + ImplDerivedObligation(Box::new(ImplDerivedObligationCause { + derived, + impl_def_id, + span: obligation.cause.span, + })) + }); ensure_sufficient_stack(|| { self.vtable_impl( impl_def_id, diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 9e2d0657029..b0b17d0f9e6 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -14,9 +14,9 @@ use super::util; use super::util::{closure_trait_ref_and_return_type, predicate_for_trait_def}; use super::wf; use super::{ - DerivedObligationCause, ErrorReporting, ImplDerivedObligation, ImplDerivedObligationCause, - Normalized, Obligation, ObligationCause, ObligationCauseCode, Overflow, PredicateObligation, - Selection, SelectionError, SelectionResult, TraitObligation, TraitQueryMode, + ErrorReporting, ImplDerivedObligation, ImplDerivedObligationCause, Normalized, Obligation, + ObligationCause, ObligationCauseCode, Overflow, PredicateObligation, Selection, SelectionError, + SelectionResult, TraitObligation, TraitQueryMode, }; use crate::infer::{InferCtxt, InferOk, TypeFreshener}; @@ -38,7 +38,7 @@ use rustc_middle::ty::fold::BottomUpFolder; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::relate::TypeRelation; use rustc_middle::ty::subst::{GenericArgKind, Subst, SubstsRef}; -use rustc_middle::ty::{self, PolyProjectionPredicate, ToPolyTraitRef, ToPredicate}; +use rustc_middle::ty::{self, EarlyBinder, PolyProjectionPredicate, ToPolyTraitRef, ToPredicate}; use rustc_middle::ty::{Ty, TyCtxt, TypeFoldable}; use rustc_span::symbol::sym; @@ -103,22 +103,31 @@ pub struct SelectionContext<'cx, 'tcx> { /// require themselves. freshener: TypeFreshener<'cx, 'tcx>, - /// If `true`, indicates that the evaluation should be conservative - /// and consider the possibility of types outside this crate. + /// During coherence we have to assume that other crates may add + /// additional impls which we currently don't know about. + /// + /// To deal with this evaluation should be conservative + /// and consider the possibility of impls from outside this crate. /// This comes up primarily when resolving ambiguity. Imagine /// there is some trait reference `$0: Bar` where `$0` is an /// inference variable. If `intercrate` is true, then we can never /// say for sure that this reference is not implemented, even if /// there are *no impls at all for `Bar`*, because `$0` could be /// bound to some type that in a downstream crate that implements - /// `Bar`. This is the suitable mode for coherence. Elsewhere, - /// though, we set this to false, because we are only interested - /// in types that the user could actually have written --- in - /// other words, we consider `$0: Bar` to be unimplemented if + /// `Bar`. + /// + /// Outside of coherence we set this to false because we are only + /// interested in types that the user could actually have written. + /// In other words, we consider `$0: Bar` to be unimplemented if /// there is no type that the user could *actually name* that /// would satisfy it. This avoids crippling inference, basically. intercrate: bool, - + /// If `intercrate` is set, we remember predicates which were + /// considered ambiguous because of impls potentially added in other crates. + /// This is used in coherence to give improved diagnostics. + /// We don't do his until we detect a coherence error because it can + /// lead to false overflow results (#47139) and because always + /// computing it may negatively impact performance. intercrate_ambiguity_causes: Option<Vec<IntercrateAmbiguityCause>>, /// The mode that trait queries run in, which informs our error handling @@ -240,11 +249,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - /// Enables tracking of intercrate ambiguity causes. These are - /// used in coherence to give improved diagnostics. We don't do - /// this until we detect a coherence error because it can lead to - /// false overflow results (#47139) and because it costs - /// computation time. + /// Enables tracking of intercrate ambiguity causes. See + /// the documentation of [`Self::intercrate_ambiguity_causes`] for more. pub fn enable_tracking_intercrate_ambiguity_causes(&mut self) { assert!(self.intercrate); assert!(self.intercrate_ambiguity_causes.is_none()); @@ -326,7 +332,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - crate fn select_from_obligation( + pub(crate) fn select_from_obligation( &mut self, obligation: &TraitObligation<'tcx>, ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { @@ -741,39 +747,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if reached_depth >= stack.depth { debug!(?result, "CACHE MISS"); self.insert_evaluation_cache(param_env, fresh_trait_pred, dep_node, result); - - stack.cache().on_completion( - stack.dfn, - |fresh_trait_pred, provisional_result, provisional_dep_node| { - // Create a new `DepNode` that has dependencies on: - // * The `DepNode` for the original evaluation that resulted in a provisional cache - // entry being crated - // * The `DepNode` for the *current* evaluation, which resulted in us completing - // provisional caches entries and inserting them into the evaluation cache - // - // This ensures that when a query reads this entry from the evaluation cache, - // it will end up (transitively) depending on all of the incr-comp dependencies - // created during the evaluation of this trait. For example, evaluating a trait - // will usually require us to invoke `type_of(field_def_id)` to determine the - // constituent types, and we want any queries reading from this evaluation - // cache entry to end up with a transitive `type_of(field_def_id`)` dependency. - // - // By using `in_task`, we're also creating an edge from the *current* query - // to the newly-created `combined_dep_node`. This is probably redundant, - // but it's better to add too many dep graph edges than to add too few - // dep graph edges. - let ((), combined_dep_node) = self.in_task(|this| { - this.tcx().dep_graph.read_index(provisional_dep_node); - this.tcx().dep_graph.read_index(dep_node); - }); - self.insert_evaluation_cache( - param_env, - fresh_trait_pred, - combined_dep_node, - provisional_result.max(result), - ); - }, - ); + stack.cache().on_completion(stack.dfn); } else { debug!(?result, "PROVISIONAL"); debug!( @@ -782,13 +756,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fresh_trait_pred, stack.depth, reached_depth, ); - stack.cache().insert_provisional( - stack.dfn, - reached_depth, - fresh_trait_pred, - result, - dep_node, - ); + stack.cache().insert_provisional(stack.dfn, reached_depth, fresh_trait_pred, result); } Ok(result) @@ -1194,9 +1162,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if let ImplCandidate(def_id) = candidate { if let ty::ImplPolarity::Reservation = tcx.impl_polarity(def_id) { if let Some(intercrate_ambiguity_clauses) = &mut self.intercrate_ambiguity_causes { - let attrs = tcx.get_attrs(def_id); - let attr = tcx.sess.find_by_name(&attrs, sym::rustc_reservation_impl); - let value = attr.and_then(|a| a.value_str()); + let value = tcx + .get_attr(def_id, sym::rustc_reservation_impl) + .and_then(|a| a.value_str()); if let Some(value) = value { debug!( "filter_reservation_impls: \ @@ -1379,7 +1347,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); } }; - let bounds = tcx.item_bounds(def_id).subst(tcx, substs); + let bounds = tcx.bound_item_bounds(def_id).subst(tcx, substs); // The bounds returned by `item_bounds` may contain duplicates after // normalization, so try to deduplicate when possible to avoid @@ -1833,11 +1801,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::Adt(def, substs) => { let sized_crit = def.sized_constraint(self.tcx()); // (*) binder moved here - Where( - obligation.predicate.rebind({ - sized_crit.iter().map(|ty| ty.subst(self.tcx(), substs)).collect() - }), - ) + Where(obligation.predicate.rebind({ + sized_crit.iter().map(|ty| EarlyBinder(*ty).subst(self.tcx(), substs)).collect() + })) } ty::Projection(_) | ty::Param(_) | ty::Opaque(..) => None, @@ -1929,7 +1895,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// /// Here are some (simple) examples: /// - /// ``` + /// ```ignore (illustrative) /// (i32, u32) -> [i32, u32] /// Foo where struct Foo { x: i32, y: u32 } -> [i32, u32] /// Bar<i32> where struct Bar<T> { x: T, y: u32 } -> [i32, u32] @@ -2000,7 +1966,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // We can resolve the `impl Trait` to its concrete type, // which enforces a DAG between the functions requiring // the auto trait bounds in question. - t.rebind(vec![self.tcx().type_of(def_id).subst(self.tcx(), substs)]) + t.rebind(vec![self.tcx().bound_type_of(def_id).subst(self.tcx(), substs)]) } } } @@ -2106,12 +2072,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { impl_def_id: DefId, obligation: &TraitObligation<'tcx>, ) -> Result<Normalized<'tcx, SubstsRef<'tcx>>, ()> { - let impl_trait_ref = self.tcx().impl_trait_ref(impl_def_id).unwrap(); + let impl_trait_ref = self.tcx().bound_impl_trait_ref(impl_def_id).unwrap(); // Before we create the substitutions and everything, first // consider a "quick reject". This avoids creating more types // and so forth that we need to. - if self.fast_reject_trait_refs(obligation, &impl_trait_ref) { + if self.fast_reject_trait_refs(obligation, &impl_trait_ref.0) { return Err(()); } @@ -2182,13 +2148,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let simplified_obligation_ty = fast_reject::simplify_type( self.tcx(), obligation_ty, - TreatParams::AsBoundTypes, - ); - let simplified_impl_ty = fast_reject::simplify_type( - self.tcx(), - impl_ty, - TreatParams::AsPlaceholders, + TreatParams::AsPlaceholder, ); + let simplified_impl_ty = + fast_reject::simplify_type(self.tcx(), impl_ty, TreatParams::AsInfer); simplified_obligation_ty.is_some() && simplified_impl_ty.is_some() @@ -2354,23 +2317,21 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!(?predicates); assert_eq!(predicates.parent, None); let mut obligations = Vec::with_capacity(predicates.predicates.len()); - let parent_code = cause.clone_code(); for (predicate, span) in predicates.predicates { let span = *span; - let derived = - DerivedObligationCause { parent_trait_pred, parent_code: parent_code.clone() }; - let code = ImplDerivedObligation(Box::new(ImplDerivedObligationCause { - derived, - impl_def_id: def_id, - span, - })); - let cause = ObligationCause::new(cause.span, cause.body_id, code); + let cause = cause.clone().derived_cause(parent_trait_pred, |derived| { + ImplDerivedObligation(Box::new(ImplDerivedObligationCause { + derived, + impl_def_id: def_id, + span, + })) + }); let predicate = normalize_with_depth_to( self, param_env, cause.clone(), recursion_depth, - predicate.subst(tcx, substs), + EarlyBinder(*predicate).subst(tcx, substs), &mut obligations, ); obligations.push(Obligation { cause, recursion_depth, param_env, predicate }); @@ -2380,42 +2341,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } -trait TraitObligationExt<'tcx> { - fn derived_cause( - &self, - variant: fn(DerivedObligationCause<'tcx>) -> ObligationCauseCode<'tcx>, - ) -> ObligationCause<'tcx>; -} - -impl<'tcx> TraitObligationExt<'tcx> for TraitObligation<'tcx> { - fn derived_cause( - &self, - variant: fn(DerivedObligationCause<'tcx>) -> ObligationCauseCode<'tcx>, - ) -> ObligationCause<'tcx> { - /*! - * Creates a cause for obligations that are derived from - * `obligation` by a recursive search (e.g., for a builtin - * bound, or eventually a `auto trait Foo`). If `obligation` - * is itself a derived obligation, this is just a clone, but - * otherwise we create a "derived obligation" cause so as to - * keep track of the original root obligation for error - * reporting. - */ - - let obligation = self; - - // NOTE(flaper87): As of now, it keeps track of the whole error - // chain. Ideally, we should have a way to configure this either - // by using -Z verbose or just a CLI argument. - let derived_cause = DerivedObligationCause { - parent_trait_pred: obligation.predicate, - parent_code: obligation.cause.clone_code(), - }; - let derived_code = variant(derived_cause); - ObligationCause::new(obligation.cause.span, obligation.cause.body_id, derived_code) - } -} - impl<'o, 'tcx> TraitObligationStack<'o, 'tcx> { fn list(&'o self) -> TraitObligationStackList<'o, 'tcx> { TraitObligationStackList::with(self) @@ -2531,11 +2456,6 @@ struct ProvisionalEvaluation { from_dfn: usize, reached_depth: usize, result: EvaluationResult, - /// The `DepNodeIndex` created for the `evaluate_stack` call for this provisional - /// evaluation. When we create an entry in the evaluation cache using this provisional - /// cache entry (see `on_completion`), we use this `dep_node` to ensure that future reads from - /// the cache will have all of the necessary incr comp dependencies tracked. - dep_node: DepNodeIndex, } impl<'tcx> Default for ProvisionalEvaluationCache<'tcx> { @@ -2578,7 +2498,6 @@ impl<'tcx> ProvisionalEvaluationCache<'tcx> { reached_depth: usize, fresh_trait_pred: ty::PolyTraitPredicate<'tcx>, result: EvaluationResult, - dep_node: DepNodeIndex, ) { debug!(?from_dfn, ?fresh_trait_pred, ?result, "insert_provisional"); @@ -2604,10 +2523,7 @@ impl<'tcx> ProvisionalEvaluationCache<'tcx> { } } - map.insert( - fresh_trait_pred, - ProvisionalEvaluation { from_dfn, reached_depth, result, dep_node }, - ); + map.insert(fresh_trait_pred, ProvisionalEvaluation { from_dfn, reached_depth, result }); } /// Invoked when the node with dfn `dfn` does not get a successful @@ -2633,8 +2549,7 @@ impl<'tcx> ProvisionalEvaluationCache<'tcx> { /// Invoked when the node at depth `depth` completed without /// depending on anything higher in the stack (if that completion /// was a failure, then `on_failure` should have been invoked - /// already). The callback `op` will be invoked for each - /// provisional entry that we can now confirm. + /// already). /// /// Note that we may still have provisional cache items remaining /// in the cache when this is done. For example, if there is a @@ -2655,19 +2570,26 @@ impl<'tcx> ProvisionalEvaluationCache<'tcx> { /// would be 2, representing the C node, and hence we would /// remove the result for D, which has DFN 3, but not the results for /// A and B, which have DFNs 0 and 1 respectively). - fn on_completion( - &self, - dfn: usize, - mut op: impl FnMut(ty::PolyTraitPredicate<'tcx>, EvaluationResult, DepNodeIndex), - ) { + /// + /// Note that we *do not* attempt to cache these cycle participants + /// in the evaluation cache. Doing so would require carefully computing + /// the correct `DepNode` to store in the cache entry: + /// cycle participants may implicitly depend on query results + /// related to other participants in the cycle, due to our logic + /// which examines the evaluation stack. + /// + /// We used to try to perform this caching, + /// but it lead to multiple incremental compilation ICEs + /// (see #92987 and #96319), and was very hard to understand. + /// Fortunately, removing the caching didn't seem to + /// have a performance impact in practice. + fn on_completion(&self, dfn: usize) { debug!(?dfn, "on_completion"); for (fresh_trait_pred, eval) in self.map.borrow_mut().drain_filter(|_k, eval| eval.from_dfn >= dfn) { debug!(?fresh_trait_pred, ?eval, "on_completion"); - - op(fresh_trait_pred, eval.result, eval.dep_node); } } } diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index 328e0d2e0e9..95f1e224a4c 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -52,7 +52,7 @@ pub struct OverlapError { /// /// For example, consider the following scenario: /// -/// ```rust +/// ```ignore (illustrative) /// trait Foo { ... } /// impl<T, U> Foo for (T, U) { ... } // target impl /// impl<V> Foo for (V, V) { ... } // source impl @@ -64,7 +64,7 @@ pub struct OverlapError { /// where-clauses add some trickiness here, because they can be used to "define" /// an argument indirectly: /// -/// ```rust +/// ```ignore (illustrative) /// impl<'a, I, T: 'a> Iterator for Cloned<I> /// where I: Iterator<Item = &'a T>, T: Clone /// ``` @@ -85,7 +85,7 @@ pub fn translate_substs<'a, 'tcx>( param_env, source_impl, source_substs, target_node ); let source_trait_ref = - infcx.tcx.impl_trait_ref(source_impl).unwrap().subst(infcx.tcx, &source_substs); + infcx.tcx.bound_impl_trait_ref(source_impl).unwrap().subst(infcx.tcx, &source_substs); // translate the Self and Param parts of the substitution, since those // vary across impls @@ -486,7 +486,7 @@ fn report_conflicting_impls( /// Recovers the "impl X for Y" signature from `impl_def_id` and returns it as a /// string. -crate fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Option<String> { +pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Option<String> { use std::fmt::Write; let trait_ref = tcx.impl_trait_ref(impl_def_id)?; diff --git a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs index 8b23dcfe380..930c80e0abb 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs @@ -49,8 +49,7 @@ impl ChildrenExt<'_> for Children { /// Insert an impl into this set of children without comparing to any existing impls. fn insert_blindly(&mut self, tcx: TyCtxt<'_>, impl_def_id: DefId) { let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); - if let Some(st) = - fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsPlaceholders) + if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsInfer) { debug!("insert_blindly: impl_def_id={:?} st={:?}", impl_def_id, st); self.non_blanket_impls.entry(st).or_default().push(impl_def_id) @@ -66,8 +65,7 @@ impl ChildrenExt<'_> for Children { fn remove_existing(&mut self, tcx: TyCtxt<'_>, impl_def_id: DefId) { let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); let vec: &mut Vec<DefId>; - if let Some(st) = - fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsPlaceholders) + if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsInfer) { debug!("remove_existing: impl_def_id={:?} st={:?}", impl_def_id, st); vec = self.non_blanket_impls.get_mut(&st).unwrap(); @@ -316,8 +314,7 @@ impl GraphExt for Graph { let mut parent = trait_def_id; let mut last_lint = None; - let simplified = - fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsPlaceholders); + let simplified = fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsInfer); // Descend the specialization tree, where `parent` is the current parent node. loop { diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index 7543d1f9a7b..f2e31c068a0 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -6,7 +6,7 @@ use smallvec::SmallVec; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::DefId; use rustc_middle::ty::subst::{GenericArg, Subst, SubstsRef}; -use rustc_middle::ty::{self, ImplSubject, ToPredicate, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, EarlyBinder, ImplSubject, ToPredicate, Ty, TyCtxt, TypeFoldable}; use super::{Normalized, Obligation, ObligationCause, PredicateObligation, SelectionContext}; pub use rustc_infer::traits::{self, util::*}; @@ -118,13 +118,14 @@ impl<'tcx> TraitAliasExpander<'tcx> { // Get components of trait alias. let predicates = tcx.super_predicates_of(trait_ref.def_id()); + debug!(?predicates); let items = predicates.predicates.iter().rev().filter_map(|(pred, span)| { pred.subst_supertrait(tcx, &trait_ref) .to_opt_poly_trait_pred() .map(|trait_ref| item.clone_and_push(trait_ref.map_bound(|t| t.trait_ref), *span)) }); - debug!("expand_trait_aliases: items={:?}", items.clone()); + debug!("expand_trait_aliases: items={:?}", items.clone().collect::<Vec<_>>()); self.stack.extend(items); @@ -200,7 +201,7 @@ pub fn impl_subject_and_oblig<'a, 'tcx>( impl_substs: SubstsRef<'tcx>, ) -> (ImplSubject<'tcx>, impl Iterator<Item = PredicateObligation<'tcx>>) { let subject = selcx.tcx().impl_subject(impl_def_id); - let subject = subject.subst(selcx.tcx(), impl_substs); + let subject = EarlyBinder(subject).subst(selcx.tcx(), impl_substs); let Normalized { value: subject, obligations: normalization_obligations1 } = super::normalize(selcx, param_env, ObligationCause::dummy(), subject); diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index b4ed5b95b10..0379b16334c 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -170,7 +170,7 @@ struct WfPredicates<'a, 'tcx> { /// predicates. This is a kind of hack to address #43784. The /// underlying problem in that issue was a trait structure like: /// -/// ``` +/// ```ignore (illustrative) /// trait Foo: Copy { } /// trait Bar: Foo { } /// impl<T: Bar> Foo for T { } @@ -294,30 +294,22 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { let obligations = self.nominal_obligations(trait_ref.def_id, trait_ref.substs); debug!("compute_trait_ref obligations {:?}", obligations); - let cause = self.cause(traits::MiscObligation); let param_env = self.param_env; let depth = self.recursion_depth; let item = self.item; - let extend = |obligation: traits::PredicateObligation<'tcx>| { - let mut cause = cause.clone(); - if let Some(parent_trait_pred) = obligation.predicate.to_opt_poly_trait_pred() { - let derived_cause = traits::DerivedObligationCause { + let extend = |traits::PredicateObligation { predicate, mut cause, .. }| { + if let Some(parent_trait_pred) = predicate.to_opt_poly_trait_pred() { + cause = cause.derived_cause( parent_trait_pred, - parent_code: obligation.cause.clone_code(), - }; - *cause.make_mut_code() = - traits::ObligationCauseCode::DerivedObligation(derived_cause); + traits::ObligationCauseCode::DerivedObligation, + ); } extend_cause_with_original_assoc_item_obligation( - tcx, - trait_ref, - item, - &mut cause, - obligation.predicate, + tcx, trait_ref, item, &mut cause, predicate, ); - traits::Obligation::with_depth(cause, depth, param_env, obligation.predicate) + traits::Obligation::with_depth(cause, depth, param_env, predicate) }; if let Elaborate::All = elaborate { @@ -339,17 +331,17 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { }) .filter(|(_, arg)| !arg.has_escaping_bound_vars()) .map(|(i, arg)| { - let mut new_cause = cause.clone(); + let mut cause = traits::ObligationCause::misc(self.span, self.body_id); // The first subst is the self ty - use the correct span for it. if i == 0 { if let Some(hir::ItemKind::Impl(hir::Impl { self_ty, .. })) = item.map(|i| &i.kind) { - new_cause.span = self_ty.span; + cause.span = self_ty.span; } } traits::Obligation::with_depth( - new_cause, + cause, depth, param_env, ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)).to_predicate(tcx), @@ -575,7 +567,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // generators don't take arguments. } - ty::Closure(_, substs) => { + ty::Closure(did, substs) => { // Only check the upvar types for WF, not the rest // of the types within. This is needed because we // capture the signature and it may not be WF @@ -596,18 +588,26 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // probably always be WF, because it should be // shorthand for something like `where(T: 'a) { // fn(&'a T) }`, as discussed in #25860. - // - // Note that we are also skipping the generic - // types. This is consistent with the `outlives` - // code, but anyway doesn't matter: within the fn + walker.skip_current_subtree(); // subtree handled below + // FIXME(eddyb) add the type to `walker` instead of recursing. + self.compute(substs.as_closure().tupled_upvars_ty().into()); + // Note that we cannot skip the generic types + // types. Normally, within the fn // body where they are created, the generics will // always be WF, and outside of that fn body we // are not directly inspecting closure types // anyway, except via auto trait matching (which // only inspects the upvar types). - walker.skip_current_subtree(); // subtree handled below - // FIXME(eddyb) add the type to `walker` instead of recursing. - self.compute(substs.as_closure().tupled_upvars_ty().into()); + // But when a closure is part of a type-alias-impl-trait + // then the function that created the defining site may + // have had more bounds available than the type alias + // specifies. This may cause us to have a closure in the + // hidden type that is not actually well formed and + // can cause compiler crashes when the user abuses unsafe + // code to procure such a closure. + // See src/test/ui/type-alias-impl-trait/wf_check_closures.rs + let obligations = self.nominal_obligations(did, substs); + self.out.extend(obligations); } ty::FnPtr(_) => { diff --git a/compiler/rustc_traits/src/chalk/db.rs b/compiler/rustc_traits/src/chalk/db.rs index 6424b907478..5b5b8499191 100644 --- a/compiler/rustc_traits/src/chalk/db.rs +++ b/compiler/rustc_traits/src/chalk/db.rs @@ -8,7 +8,9 @@ use rustc_middle::traits::ChalkRustInterner as RustInterner; use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; -use rustc_middle::ty::{self, AssocItemContainer, AssocKind, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{ + self, AssocItemContainer, AssocKind, EarlyBinder, Ty, TyCtxt, TypeFoldable, +}; use rustc_ast::ast; use rustc_attr as attr; @@ -41,7 +43,7 @@ impl<'tcx> RustIrDatabase<'tcx> { let predicates = self.interner.tcx.predicates_defined_on(def_id).predicates; predicates .iter() - .map(|(wc, _)| wc.subst(self.interner.tcx, bound_vars)) + .map(|(wc, _)| EarlyBinder(*wc).subst(self.interner.tcx, bound_vars)) .filter_map(|wc| LowerInto::< Option<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>> >::lower_into(wc, self.interner)).collect() @@ -55,7 +57,7 @@ impl<'tcx> RustIrDatabase<'tcx> { .tcx .explicit_item_bounds(def_id) .iter() - .map(|(bound, _)| bound.subst(self.interner.tcx, &bound_vars)) + .map(|(bound, _)| EarlyBinder(*bound).subst(self.interner.tcx, &bound_vars)) .filter_map(|bound| LowerInto::<Option<_>>::lower_into(bound, self.interner)) .collect() } @@ -272,15 +274,17 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t let (inputs_and_output, iobinders, _) = crate::chalk::lowering::collect_bound_vars( self.interner, self.interner.tcx, - sig.inputs_and_output().subst(self.interner.tcx, bound_vars), + EarlyBinder(sig.inputs_and_output()).subst(self.interner.tcx, bound_vars), ); let argument_types = inputs_and_output[..inputs_and_output.len() - 1] .iter() - .map(|t| t.subst(self.interner.tcx, &bound_vars).lower_into(self.interner)) + .map(|t| { + EarlyBinder(*t).subst(self.interner.tcx, &bound_vars).lower_into(self.interner) + }) .collect(); - let return_type = inputs_and_output[inputs_and_output.len() - 1] + let return_type = EarlyBinder(inputs_and_output[inputs_and_output.len() - 1]) .subst(self.interner.tcx, &bound_vars) .lower_into(self.interner); @@ -306,7 +310,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t let bound_vars = bound_vars_for_item(self.interner.tcx, def_id); let binders = binders_for(self.interner, bound_vars); - let trait_ref = self.interner.tcx.impl_trait_ref(def_id).expect("not an impl"); + let trait_ref = self.interner.tcx.bound_impl_trait_ref(def_id).expect("not an impl"); let trait_ref = trait_ref.subst(self.interner.tcx, bound_vars); let where_clauses = self.where_clauses_for(def_id, bound_vars); @@ -348,10 +352,10 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t let all_impls = self.interner.tcx.all_impls(def_id); let matched_impls = all_impls.filter(|impl_def_id| { use chalk_ir::could_match::CouldMatch; - let trait_ref = self.interner.tcx.impl_trait_ref(*impl_def_id).unwrap(); + let trait_ref = self.interner.tcx.bound_impl_trait_ref(*impl_def_id).unwrap(); let bound_vars = bound_vars_for_item(self.interner.tcx, *impl_def_id); - let self_ty = trait_ref.self_ty(); + let self_ty = trait_ref.map_bound(|t| t.self_ty()); let self_ty = self_ty.subst(self.interner.tcx, bound_vars); let lowered_ty = self_ty.lower_into(self.interner); @@ -463,7 +467,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t let ty = self .interner .tcx - .type_of(def_id) + .bound_type_of(def_id) .subst(self.interner.tcx, bound_vars) .lower_into(self.interner); @@ -506,7 +510,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t .tcx .explicit_item_bounds(opaque_ty_id.0) .iter() - .map(|(bound, _)| bound.subst(self.interner.tcx, &bound_vars)) + .map(|(bound, _)| EarlyBinder(*bound).subst(self.interner.tcx, &bound_vars)) .map(|bound| { bound.fold_with(&mut ReplaceOpaqueTyFolder { tcx: self.interner.tcx, diff --git a/compiler/rustc_traits/src/chalk/lowering.rs b/compiler/rustc_traits/src/chalk/lowering.rs index e3c865ce9e6..2b7ba22c4de 100644 --- a/compiler/rustc_traits/src/chalk/lowering.rs +++ b/compiler/rustc_traits/src/chalk/lowering.rs @@ -44,7 +44,7 @@ use std::collections::btree_map::{BTreeMap, Entry}; use std::ops::ControlFlow; /// Essentially an `Into` with a `&RustInterner` parameter -crate trait LowerInto<'tcx, T> { +pub(crate) trait LowerInto<'tcx, T> { /// Lower a rustc construct (e.g., `ty::TraitPredicate`) to a chalk type, consuming `self`. fn lower_into(self, interner: RustInterner<'tcx>) -> T; } @@ -836,7 +836,7 @@ impl<'tcx> LowerInto<'tcx, chalk_solve::rust_ir::AliasEqBound<RustInterner<'tcx> /// It's important to note that because of prior substitution, we may have /// late-bound regions, even outside of fn contexts, since this is the best way /// to prep types for chalk lowering. -crate fn collect_bound_vars<'tcx, T: TypeFoldable<'tcx>>( +pub(crate) fn collect_bound_vars<'tcx, T: TypeFoldable<'tcx>>( interner: RustInterner<'tcx>, tcx: TyCtxt<'tcx>, ty: Binder<'tcx, T>, @@ -870,14 +870,14 @@ crate fn collect_bound_vars<'tcx, T: TypeFoldable<'tcx>>( (new_ty, binders, named_parameters) } -crate struct BoundVarsCollector<'tcx> { +pub(crate) struct BoundVarsCollector<'tcx> { binder_index: ty::DebruijnIndex, - crate parameters: BTreeMap<u32, chalk_ir::VariableKind<RustInterner<'tcx>>>, - crate named_parameters: Vec<DefId>, + pub(crate) parameters: BTreeMap<u32, chalk_ir::VariableKind<RustInterner<'tcx>>>, + pub(crate) named_parameters: Vec<DefId>, } impl<'tcx> BoundVarsCollector<'tcx> { - crate fn new() -> Self { + pub(crate) fn new() -> Self { BoundVarsCollector { binder_index: ty::INNERMOST, parameters: BTreeMap::new(), @@ -1001,17 +1001,17 @@ impl<'a, 'tcx> TypeFolder<'tcx> for NamedBoundVarSubstitutor<'a, 'tcx> { /// Used to substitute `Param`s with placeholders. We do this since Chalk /// have a notion of `Param`s. -crate struct ParamsSubstitutor<'tcx> { +pub(crate) struct ParamsSubstitutor<'tcx> { tcx: TyCtxt<'tcx>, binder_index: ty::DebruijnIndex, list: Vec<rustc_middle::ty::ParamTy>, next_ty_placeholder: usize, - crate params: rustc_data_structures::fx::FxHashMap<usize, rustc_middle::ty::ParamTy>, - crate named_regions: BTreeMap<DefId, u32>, + pub(crate) params: rustc_data_structures::fx::FxHashMap<usize, rustc_middle::ty::ParamTy>, + pub(crate) named_regions: BTreeMap<DefId, u32>, } impl<'tcx> ParamsSubstitutor<'tcx> { - crate fn new(tcx: TyCtxt<'tcx>, next_ty_placeholder: usize) -> Self { + pub(crate) fn new(tcx: TyCtxt<'tcx>, next_ty_placeholder: usize) -> Self { ParamsSubstitutor { tcx, binder_index: ty::INNERMOST, @@ -1083,13 +1083,13 @@ impl<'tcx> TypeFolder<'tcx> for ParamsSubstitutor<'tcx> { } } -crate struct ReverseParamsSubstitutor<'tcx> { +pub(crate) struct ReverseParamsSubstitutor<'tcx> { tcx: TyCtxt<'tcx>, params: rustc_data_structures::fx::FxHashMap<usize, rustc_middle::ty::ParamTy>, } impl<'tcx> ReverseParamsSubstitutor<'tcx> { - crate fn new( + pub(crate) fn new( tcx: TyCtxt<'tcx>, params: rustc_data_structures::fx::FxHashMap<usize, rustc_middle::ty::ParamTy>, ) -> Self { @@ -1117,14 +1117,14 @@ impl<'tcx> TypeFolder<'tcx> for ReverseParamsSubstitutor<'tcx> { } /// Used to collect `Placeholder`s. -crate struct PlaceholdersCollector { +pub(crate) struct PlaceholdersCollector { universe_index: ty::UniverseIndex, - crate next_ty_placeholder: usize, - crate next_anon_region_placeholder: u32, + pub(crate) next_ty_placeholder: usize, + pub(crate) next_anon_region_placeholder: u32, } impl PlaceholdersCollector { - crate fn new() -> Self { + pub(crate) fn new() -> Self { PlaceholdersCollector { universe_index: ty::UniverseIndex::ROOT, next_ty_placeholder: 0, diff --git a/compiler/rustc_traits/src/chalk/mod.rs b/compiler/rustc_traits/src/chalk/mod.rs index 4e19479da87..59cf37fee9c 100644 --- a/compiler/rustc_traits/src/chalk/mod.rs +++ b/compiler/rustc_traits/src/chalk/mod.rs @@ -3,8 +3,8 @@ //! In order to call `chalk-solve`, this file must convert a `CanonicalChalkEnvironmentAndGoal` into //! a Chalk uncanonical goal. It then calls Chalk, and converts the answer back into rustc solution. -crate mod db; -crate mod lowering; +pub(crate) mod db; +pub(crate) mod lowering; use rustc_data_structures::fx::FxHashMap; @@ -27,11 +27,11 @@ use crate::chalk::lowering::{ParamsSubstitutor, PlaceholdersCollector, ReversePa use chalk_solve::Solution; -crate fn provide(p: &mut Providers) { +pub(crate) fn provide(p: &mut Providers) { *p = Providers { evaluate_goal, ..*p }; } -crate fn evaluate_goal<'tcx>( +pub(crate) fn evaluate_goal<'tcx>( tcx: TyCtxt<'tcx>, obligation: CanonicalChalkEnvironmentAndGoal<'tcx>, ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, ()>>, traits::query::NoSolution> { diff --git a/compiler/rustc_traits/src/dropck_outlives.rs b/compiler/rustc_traits/src/dropck_outlives.rs index e4c22a35423..a20de08b4ef 100644 --- a/compiler/rustc_traits/src/dropck_outlives.rs +++ b/compiler/rustc_traits/src/dropck_outlives.rs @@ -5,7 +5,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::TraitEngineExt as _; use rustc_middle::ty::query::Providers; use rustc_middle::ty::subst::{InternalSubsts, Subst}; -use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt}; +use rustc_middle::ty::{self, EarlyBinder, ParamEnvAnd, Ty, TyCtxt}; use rustc_span::source_map::{Span, DUMMY_SP}; use rustc_trait_selection::traits::query::dropck_outlives::trivial_dropck_outlives; use rustc_trait_selection::traits::query::dropck_outlives::{ @@ -17,7 +17,7 @@ use rustc_trait_selection::traits::{ Normalized, ObligationCause, TraitEngine, TraitEngineExt as _, }; -crate fn provide(p: &mut Providers) { +pub(crate) fn provide(p: &mut Providers) { *p = Providers { dropck_outlives, adt_dtorck_constraint, ..*p }; } @@ -271,9 +271,15 @@ fn dtorck_constraint_for_ty<'tcx>( tcx.at(span).adt_dtorck_constraint(def.did())?; // FIXME: we can try to recursively `dtorck_constraint_on_ty` // there, but that needs some way to handle cycles. - constraints.dtorck_types.extend(dtorck_types.iter().map(|t| t.subst(tcx, substs))); - constraints.outlives.extend(outlives.iter().map(|t| t.subst(tcx, substs))); - constraints.overflows.extend(overflows.iter().map(|t| t.subst(tcx, substs))); + constraints + .dtorck_types + .extend(dtorck_types.iter().map(|t| EarlyBinder(*t).subst(tcx, substs))); + constraints + .outlives + .extend(outlives.iter().map(|t| EarlyBinder(*t).subst(tcx, substs))); + constraints + .overflows + .extend(overflows.iter().map(|t| EarlyBinder(*t).subst(tcx, substs))); } // Objects must be alive in order for their destructor @@ -298,7 +304,7 @@ fn dtorck_constraint_for_ty<'tcx>( } /// Calculates the dtorck constraint for a type. -crate fn adt_dtorck_constraint( +pub(crate) fn adt_dtorck_constraint( tcx: TyCtxt<'_>, def_id: DefId, ) -> Result<&DropckConstraint<'_>, NoSolution> { diff --git a/compiler/rustc_traits/src/evaluate_obligation.rs b/compiler/rustc_traits/src/evaluate_obligation.rs index 2404b7ff4b5..3fc141471b9 100644 --- a/compiler/rustc_traits/src/evaluate_obligation.rs +++ b/compiler/rustc_traits/src/evaluate_obligation.rs @@ -7,7 +7,7 @@ use rustc_trait_selection::traits::{ EvaluationResult, Obligation, ObligationCause, OverflowError, SelectionContext, TraitQueryMode, }; -crate fn provide(p: &mut Providers) { +pub(crate) fn provide(p: &mut Providers) { *p = Providers { evaluate_obligation, ..*p }; } diff --git a/compiler/rustc_traits/src/implied_outlives_bounds.rs b/compiler/rustc_traits/src/implied_outlives_bounds.rs index 90c698db8fb..965324113d5 100644 --- a/compiler/rustc_traits/src/implied_outlives_bounds.rs +++ b/compiler/rustc_traits/src/implied_outlives_bounds.rs @@ -18,7 +18,7 @@ use rustc_trait_selection::traits::FulfillmentContext; use rustc_trait_selection::traits::TraitEngine; use smallvec::{smallvec, SmallVec}; -crate fn provide(p: &mut Providers) { +pub(crate) fn provide(p: &mut Providers) { *p = Providers { implied_outlives_bounds, ..*p }; } diff --git a/compiler/rustc_traits/src/lib.rs b/compiler/rustc_traits/src/lib.rs index 73fd95e98ca..6489bd2202d 100644 --- a/compiler/rustc_traits/src/lib.rs +++ b/compiler/rustc_traits/src/lib.rs @@ -1,7 +1,6 @@ //! New recursive solver modeled on Chalk's recursive solver. Most of //! the guts are broken up into modules; see the comments in those modules. -#![feature(crate_visibility_modifier)] #![feature(let_else)] #![feature(nll)] #![recursion_limit = "256"] diff --git a/compiler/rustc_traits/src/normalize_erasing_regions.rs b/compiler/rustc_traits/src/normalize_erasing_regions.rs index a4aa965ec95..a8a324dec97 100644 --- a/compiler/rustc_traits/src/normalize_erasing_regions.rs +++ b/compiler/rustc_traits/src/normalize_erasing_regions.rs @@ -6,7 +6,7 @@ use rustc_trait_selection::traits::query::normalize::AtExt; use rustc_trait_selection::traits::{Normalized, ObligationCause}; use std::sync::atomic::Ordering; -crate fn provide(p: &mut Providers) { +pub(crate) fn provide(p: &mut Providers) { *p = Providers { try_normalize_generic_arg_after_erasing_regions: |tcx, goal| { debug!("try_normalize_generic_arg_after_erasing_regions(goal={:#?}", goal); diff --git a/compiler/rustc_traits/src/normalize_projection_ty.rs b/compiler/rustc_traits/src/normalize_projection_ty.rs index 1de50bae31b..98bb42c9afd 100644 --- a/compiler/rustc_traits/src/normalize_projection_ty.rs +++ b/compiler/rustc_traits/src/normalize_projection_ty.rs @@ -10,7 +10,7 @@ use rustc_trait_selection::traits::query::{ use rustc_trait_selection::traits::{self, ObligationCause, SelectionContext}; use std::sync::atomic::Ordering; -crate fn provide(p: &mut Providers) { +pub(crate) fn provide(p: &mut Providers) { *p = Providers { normalize_projection_ty, ..*p }; } diff --git a/compiler/rustc_traits/src/type_op.rs b/compiler/rustc_traits/src/type_op.rs index 6fcac9fcdc6..f8bac1d7b26 100644 --- a/compiler/rustc_traits/src/type_op.rs +++ b/compiler/rustc_traits/src/type_op.rs @@ -6,7 +6,9 @@ use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::TraitEngineExt as _; use rustc_middle::ty::query::Providers; use rustc_middle::ty::subst::{GenericArg, Subst, UserSelfTy, UserSubsts}; -use rustc_middle::ty::{self, FnSig, Lift, PolyFnSig, Ty, TyCtxt, TypeFoldable, Variance}; +use rustc_middle::ty::{ + self, EarlyBinder, FnSig, Lift, PolyFnSig, Ty, TyCtxt, TypeFoldable, Variance, +}; use rustc_middle::ty::{ParamEnv, ParamEnvAnd, Predicate, ToPredicate}; use rustc_span::{Span, DUMMY_SP}; use rustc_trait_selection::infer::InferCtxtBuilderExt; @@ -21,7 +23,7 @@ use rustc_trait_selection::traits::query::{Fallible, NoSolution}; use rustc_trait_selection::traits::{Normalized, Obligation, ObligationCause, TraitEngine}; use std::fmt; -crate fn provide(p: &mut Providers) { +pub(crate) fn provide(p: &mut Providers) { *p = Providers { type_op_ascribe_user_type, type_op_eq, @@ -115,7 +117,7 @@ impl<'me, 'tcx> AscribeUserTypeCx<'me, 'tcx> { where T: TypeFoldable<'tcx>, { - value.subst(self.tcx(), substs) + EarlyBinder(value).subst(self.tcx(), substs) } fn relate_mir_and_user_ty( diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 802a59abe5f..17eac2bb2c9 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -1,10 +1,10 @@ use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::infer::TyCtxtInferExt; +use rustc_middle::traits::CodegenObligationError; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{self, Binder, Instance, Ty, TyCtxt, TypeFoldable, TypeVisitor}; use rustc_span::{sym, DUMMY_SP}; -use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; use traits::{translate_substs, Reveal}; @@ -154,12 +154,7 @@ fn inner_resolve_instance<'tcx>( let item_type = tcx.subst_and_normalize_erasing_regions(substs, param_env, ty); let def = match *item_type.kind() { - ty::FnDef(..) - if { - let f = item_type.fn_sig(tcx); - f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic - } => - { + ty::FnDef(def_id, ..) if tcx.is_intrinsic(def_id) => { debug!(" => intrinsic"); ty::InstanceDef::Intrinsic(def.did) } @@ -212,7 +207,22 @@ fn resolve_associated_item<'tcx>( let mut bound_vars_collector = BoundVarsCollector::new(); trait_ref.visit_with(&mut bound_vars_collector); let trait_binder = ty::Binder::bind_with_vars(trait_ref, bound_vars_collector.into_vars(tcx)); - let vtbl = tcx.codegen_fulfill_obligation((param_env, trait_binder))?; + let vtbl = match tcx.codegen_fulfill_obligation((param_env, trait_binder)) { + Ok(vtbl) => vtbl, + Err(CodegenObligationError::Ambiguity) => { + let reported = tcx.sess.delay_span_bug( + tcx.def_span(trait_item_id), + &format!( + "encountered ambiguity selecting `{:?}` during codegen, presuming due to \ + overflow or prior type error", + trait_binder + ), + ); + return Err(reported); + } + Err(CodegenObligationError::Unimplemented) => return Ok(None), + Err(CodegenObligationError::FulfillmentError) => return Ok(None), + }; // Now that we know which impl is being used, we can dispatch to // the actual function: diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs index c5fc4e4c661..9ad44d14d61 100644 --- a/compiler/rustc_ty_utils/src/needs_drop.rs +++ b/compiler/rustc_ty_utils/src/needs_drop.rs @@ -5,7 +5,7 @@ use rustc_hir::def_id::DefId; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::util::{needs_drop_components, AlwaysRequiresDrop}; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt}; use rustc_session::Limit; use rustc_span::{sym, DUMMY_SP}; @@ -204,7 +204,7 @@ fn drop_tys_helper<'tcx>( match subty.kind() { ty::Adt(adt_id, subst) => { for subty in tcx.adt_drop_tys(adt_id.did())? { - vec.push(subty.subst(tcx, subst)); + vec.push(EarlyBinder(subty).subst(tcx, subst)); } } _ => vec.push(subty), @@ -237,7 +237,7 @@ fn drop_tys_helper<'tcx>( Ok(Vec::new()) } else { let field_tys = adt_def.all_fields().map(|field| { - let r = tcx.type_of(field.did).subst(tcx, substs); + let r = tcx.bound_type_of(field.did).subst(tcx, substs); debug!("drop_tys_helper: Subst into {:?} with {:?} gettng {:?}", field, substs, r); r }); diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 6ad71bdb481..23700e653e3 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -2,7 +2,9 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::ty::subst::Subst; -use rustc_middle::ty::{self, Binder, Predicate, PredicateKind, ToPredicate, Ty, TyCtxt}; +use rustc_middle::ty::{ + self, Binder, EarlyBinder, Predicate, PredicateKind, ToPredicate, Ty, TyCtxt, +}; use rustc_span::{sym, Span}; use rustc_trait_selection::traits; @@ -33,7 +35,7 @@ fn sized_constraint_for_ty<'tcx>( debug!("sized_constraint_for_ty({:?}) intermediate = {:?}", ty, adt_tys); adt_tys .iter() - .map(|ty| ty.subst(tcx, substs)) + .map(|ty| EarlyBinder(*ty).subst(tcx, substs)) .flat_map(|ty| sized_constraint_for_ty(tcx, adtdef, ty)) .collect() } @@ -442,7 +444,7 @@ pub fn conservative_is_privately_uninhabited_raw<'tcx>( // one uninhabited field. def.variants().iter().all(|var| { var.fields.iter().any(|field| { - let ty = tcx.type_of(field.did).subst(tcx, substs); + let ty = tcx.bound_type_of(field.did).subst(tcx, substs); tcx.conservative_is_privately_uninhabited(param_env.and(ty)) }) }) diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index b015e40b683..c63e9c31d53 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -122,16 +122,16 @@ rustc_index::newtype_index! { /// A [De Bruijn index][dbi] is a standard means of representing /// regions (and perhaps later types) in a higher-ranked setting. In /// particular, imagine a type like this: - /// - /// for<'a> fn(for<'b> fn(&'b isize, &'a isize), &'a char) - /// ^ ^ | | | - /// | | | | | - /// | +------------+ 0 | | - /// | | | - /// +----------------------------------+ 1 | - /// | | - /// +----------------------------------------------+ 0 - /// + /// ```ignore (illustrative) + /// for<'a> fn(for<'b> fn(&'b isize, &'a isize), &'a char) + /// // ^ ^ | | | + /// // | | | | | + /// // | +------------+ 0 | | + /// // | | | + /// // +----------------------------------+ 1 | + /// // | | + /// // +----------------------------------------------+ 0 + /// ``` /// In this type, there are two binders (the outer fn and the inner /// fn). We need to be able to determine, for any given region, which /// fn type it is bound by, the inner or the outer one. There are @@ -203,7 +203,7 @@ impl DebruijnIndex { /// it will now be bound at INNERMOST. This is an appropriate thing to do /// when moving a region out from inside binders: /// - /// ``` + /// ```ignore (illustrative) /// for<'a> fn(for<'b> for<'c> fn(&'a u32), _) /// // Binder: D3 D2 D1 ^^ /// ``` @@ -471,9 +471,9 @@ impl Variance { /// variance with which the argument appears. /// /// Example 1: - /// - /// *mut Vec<i32> - /// + /// ```ignore (illustrative) + /// *mut Vec<i32> + /// ``` /// Here, the "ambient" variance starts as covariant. `*mut T` is /// invariant with respect to `T`, so the variance in which the /// `Vec<i32>` appears is `Covariant.xform(Invariant)`, which @@ -483,9 +483,9 @@ impl Variance { /// (again) in `Invariant`. /// /// Example 2: - /// - /// fn(*const Vec<i32>, *mut Vec<i32) - /// + /// ```ignore (illustrative) + /// fn(*const Vec<i32>, *mut Vec<i32) + /// ``` /// The ambient variance is covariant. A `fn` type is /// contravariant with respect to its parameters, so the variance /// within which both pointer types appear is diff --git a/compiler/rustc_typeck/src/astconv/errors.rs b/compiler/rustc_typeck/src/astconv/errors.rs index 38cc74a5e37..8fe89c66389 100644 --- a/compiler/rustc_typeck/src/astconv/errors.rs +++ b/compiler/rustc_typeck/src/astconv/errors.rs @@ -1,4 +1,5 @@ use crate::astconv::AstConv; +use crate::errors::{ManualImplementation, MissingTypeParams}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{pluralize, struct_span_err, Applicability, ErrorGuaranteed}; use rustc_hir as hir; @@ -24,65 +25,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { if missing_type_params.is_empty() { return; } - let display = - missing_type_params.iter().map(|n| format!("`{}`", n)).collect::<Vec<_>>().join(", "); - let mut err = struct_span_err!( - self.tcx().sess, + + self.tcx().sess.emit_err(MissingTypeParams { span, - E0393, - "the type parameter{} {} must be explicitly specified", - pluralize!(missing_type_params.len()), - display, - ); - err.span_label( - self.tcx().def_span(def_id), - &format!( - "type parameter{} {} must be specified for this", - pluralize!(missing_type_params.len()), - display, - ), - ); - let mut suggested = false; - if let (Ok(snippet), true) = ( - self.tcx().sess.source_map().span_to_snippet(span), - // Don't suggest setting the type params if there are some already: the order is - // tricky to get right and the user will already know what the syntax is. + def_span: self.tcx().def_span(def_id), + missing_type_params, empty_generic_args, - ) { - if snippet.ends_with('>') { - // The user wrote `Trait<'a, T>` or similar. To provide an accurate suggestion - // we would have to preserve the right order. For now, as clearly the user is - // aware of the syntax, we do nothing. - } 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<Type>`. - err.span_suggestion( - span, - &format!( - "set the type parameter{plural} to the desired type{plural}", - plural = pluralize!(missing_type_params.len()), - ), - format!("{}<{}>", snippet, missing_type_params.join(", ")), - Applicability::HasPlaceholders, - ); - suggested = true; - } - } - if !suggested { - err.span_label( - span, - format!( - "missing reference{} to {}", - pluralize!(missing_type_params.len()), - display, - ), - ); - } - err.note( - "because of the default `Self` reference, type parameters must be \ - specified on object types", - ); - err.emit(); + }); } /// When the code is using the `Fn` traits directly, instead of the `Fn(A) -> B` syntax, emit @@ -172,19 +121,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { if is_impl { let trait_name = self.tcx().def_path_str(trait_def_id); - struct_span_err!( - self.tcx().sess, - span, - E0183, - "manual implementations of `{}` are experimental", - trait_name, - ) - .span_label( - span, - format!("manual implementations of `{}` are experimental", trait_name), - ) - .help("add `#![feature(unboxed_closures)]` to the crate attributes to enable") - .emit(); + self.tcx().sess.emit_err(ManualImplementation { span, trait_name }); } } diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs index 794e711b6c8..dc4bc8fb55a 100644 --- a/compiler/rustc_typeck/src/astconv/generics.rs +++ b/compiler/rustc_typeck/src/astconv/generics.rs @@ -3,7 +3,7 @@ use crate::astconv::{ AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, GenericArgPosition, }; -use crate::errors::AssocTypeBindingNotAllowed; +use crate::errors::{AssocTypeBindingNotAllowed, ExplicitGenericArgsWithImplTrait}; use crate::structured_errors::{GenericArgsInfo, StructuredDiagnostic, WrongNumberOfGenericArgs}; use rustc_ast::ast::ParamKindOrd; use rustc_errors::{struct_span_err, Applicability, Diagnostic, MultiSpan}; @@ -636,30 +636,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { }) .collect::<Vec<_>>(); - let mut err = struct_span_err! { - tcx.sess, - spans.clone(), - E0632, - "cannot provide explicit generic arguments when `impl Trait` is \ - used in argument position" - }; - - for span in spans { - err.span_label(span, "explicit generic argument not allowed"); - } - - err.note( - "see issue #83701 <https://github.com/rust-lang/rust/issues/83701> \ - for more information", - ); - if tcx.sess.is_nightly_build() { - err.help( - "add `#![feature(explicit_generic_args_with_impl_trait)]` \ - to the crate attributes to enable", - ); - } - - err.emit(); + tcx.sess.emit_err(ExplicitGenericArgsWithImplTrait { + spans, + is_nightly_build: tcx.sess.is_nightly_build().then_some(()), + }); } impl_trait diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index fd1b7bfa0b1..96d083bb94f 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -26,7 +26,7 @@ use rustc_hir::lang_items::LangItem; use rustc_hir::{GenericArg, GenericArgs}; use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, Subst, SubstsRef}; use rustc_middle::ty::GenericParamDefKind; -use rustc_middle::ty::{self, Const, DefIdTree, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, Const, DefIdTree, EarlyBinder, Ty, TyCtxt, TypeFoldable}; use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, BARE_TRAIT_OBJECTS}; use rustc_span::edition::Edition; use rustc_span::lev_distance::find_best_match_for_name; @@ -294,9 +294,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// /// Example: /// - /// ``` - /// T: std::ops::Index<usize, Output = u32> - /// ^1 ^^^^^^^^^^^^^^2 ^^^^3 ^^^^^^^^^^^4 + /// ```ignore (illustrative) + /// T: std::ops::Index<usize, Output = u32> + /// // ^1 ^^^^^^^^^^^^^^2 ^^^^3 ^^^^^^^^^^^4 /// ``` /// /// 1. The `self_ty` here would refer to the type `T`. @@ -310,7 +310,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// /// For (generic) associated types /// - /// ``` + /// ```ignore (illustrative) /// <Vec<u8> as Iterable<u8>>::Iter::<'a> /// ``` /// @@ -523,11 +523,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self.astconv .normalize_ty( self.span, - tcx.at(self.span).type_of(param.def_id).subst_spanned( - tcx, - substs, - Some(self.span), - ), + EarlyBinder(tcx.at(self.span).type_of(param.def_id)) + .subst(tcx, substs), ) .into() } @@ -547,8 +544,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { GenericParamDefKind::Const { has_default } => { let ty = tcx.at(self.span).type_of(param.def_id); if !infer_args && has_default { - tcx.const_param_default(param.def_id) - .subst_spanned(tcx, substs.unwrap(), Some(self.span)) + EarlyBinder(tcx.const_param_default(param.def_id)) + .subst(tcx, substs.unwrap()) .into() } else { if infer_args { @@ -643,7 +640,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { assoc_bindings } - crate fn create_substs_for_associated_item( + pub(crate) fn create_substs_for_associated_item( &self, tcx: TyCtxt<'tcx>, span: Span, @@ -756,7 +753,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// /// Example: /// - /// ``` + /// ```ignore (illustrative) /// poly_trait_ref = Iterator<Item = u32> /// self_ty = Foo /// ``` @@ -1021,10 +1018,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// /// Example: /// - /// ``` + /// ```ignore (illustrative) /// fn foo<T: Bar + Baz>() { } - /// ^ ^^^^^^^^^ ast_bounds - /// param_ty + /// // ^ ^^^^^^^^^ ast_bounds + /// // param_ty /// ``` /// /// The `sized_by_default` parameter indicates if, in this context, the `param_ty` should be @@ -1070,6 +1067,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let mut bounds = Bounds::default(); self.add_bounds(param_ty, ast_bounds.iter(), &mut bounds, ty::List::empty()); + debug!(?bounds); bounds } @@ -1297,7 +1295,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { item_segment: &hir::PathSegment<'_>, ) -> Ty<'tcx> { let substs = self.ast_path_substs_for_ty(span, did, item_segment); - self.normalize_ty(span, self.tcx().at(span).type_of(did).subst(self.tcx(), substs)) + self.normalize_ty( + span, + EarlyBinder(self.tcx().at(span).type_of(did)).subst(self.tcx(), substs), + ) } fn conv_object_ty_poly_trait_ref( @@ -1333,8 +1334,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // is used and no 'maybe' bounds are used. let expanded_traits = traits::expand_trait_aliases(tcx, bounds.trait_bounds.iter().map(|&(a, b, _)| (a, b))); - let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) = - expanded_traits.partition(|i| tcx.trait_is_auto(i.trait_ref().def_id())); + let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) = expanded_traits + .filter(|i| i.trait_ref().self_ty().skip_binder() == dummy_self) + .partition(|i| tcx.trait_is_auto(i.trait_ref().def_id())); if regular_traits.len() > 1 { let first_trait = ®ular_traits[0]; let additional_trait = ®ular_traits[1]; @@ -1368,7 +1370,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } if regular_traits.is_empty() && auto_traits.is_empty() { - tcx.sess.emit_err(TraitObjectDeclaredWithNoTraits { span }); + let trait_alias_span = bounds + .trait_bounds + .iter() + .map(|&(trait_ref, _, _)| trait_ref.def_id()) + .find(|&trait_ref| tcx.is_trait_alias(trait_ref)) + .map(|trait_ref| tcx.def_span(trait_ref)); + tcx.sess.emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span }); return tcx.ty_error(); } @@ -2439,7 +2447,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { true, None, ); - self.normalize_ty(span, tcx.at(span).type_of(def_id).subst(tcx, substs)) + EarlyBinder(self.normalize_ty(span, tcx.at(span).type_of(def_id))) + .subst(tcx, substs) } hir::TyKind::Array(ref ty, ref length) => { let length = match length { @@ -2682,7 +2691,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { trait_ref.def_id, )?; - let fn_sig = tcx.fn_sig(assoc.def_id).subst( + let fn_sig = tcx.bound_fn_sig(assoc.def_id).subst( tcx, trait_ref.substs.extend_to(tcx, assoc.def_id, |param, _| tcx.mk_param_from_def(param)), ); diff --git a/compiler/rustc_typeck/src/check/_match.rs b/compiler/rustc_typeck/src/check/_match.rs index 1c7e7c935c4..3632e14385f 100644 --- a/compiler/rustc_typeck/src/check/_match.rs +++ b/compiler/rustc_typeck/src/check/_match.rs @@ -56,6 +56,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut all_arms_diverge = Diverges::WarnedAlways; let expected = orig_expected.adjust_for_branches(self); + debug!(?expected); let mut coercion = { let coerce_first = match expected { @@ -82,13 +83,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::Guard::If(e) => { self.check_expr_has_type_or_error(e, tcx.types.bool, |_| {}); } - hir::Guard::IfLet(pat, e) => { - let scrutinee_ty = self.demand_scrutinee_type( - e, - pat.contains_explicit_ref_binding(), - false, - ); - self.check_pat_top(&pat, scrutinee_ty, None, true); + hir::Guard::IfLet(l) => { + self.check_expr_let(l); } }; } @@ -132,6 +128,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(&arm.body), arm_ty, Some(&mut |err: &mut Diagnostic| { + let Some(ret) = self.ret_type_span else { + return; + }; + let Expectation::IsLast(stmt) = orig_expected else { + return + }; let can_coerce_to_return_ty = match self.ret_coercion.as_ref() { Some(ret_coercion) if self.in_tail_expr => { let ret_ty = ret_coercion.borrow().expected_ty(); @@ -143,38 +145,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } _ => false, }; - if let (Expectation::IsLast(stmt), Some(ret), true) = - (orig_expected, self.ret_type_span, can_coerce_to_return_ty) - { - let semi_span = expr.span.shrink_to_hi().with_hi(stmt.hi()); - let mut ret_span: MultiSpan = semi_span.into(); - ret_span.push_span_label( - expr.span, - "this could be implicitly returned but it is a statement, not a \ - tail expression" - .to_owned(), - ); - ret_span.push_span_label( - ret, - "the `match` arms can conform to this return type".to_owned(), - ); - ret_span.push_span_label( - semi_span, - "the `match` is a statement because of this semicolon, consider \ - removing it" - .to_owned(), - ); - err.span_note( - ret_span, - "you might have meant to return the `match` expression", - ); - err.tool_only_span_suggestion( - semi_span, - "remove this semicolon", - String::new(), - Applicability::MaybeIncorrect, - ); + if !can_coerce_to_return_ty { + return; } + + let semi_span = expr.span.shrink_to_hi().with_hi(stmt.hi()); + let mut ret_span: MultiSpan = semi_span.into(); + ret_span.push_span_label( + expr.span, + "this could be implicitly returned but it is a statement, not a \ + tail expression" + .to_owned(), + ); + ret_span.push_span_label( + ret, + "the `match` arms can conform to this return type".to_owned(), + ); + ret_span.push_span_label( + semi_span, + "the `match` is a statement because of this semicolon, consider \ + removing it" + .to_owned(), + ); + err.span_note( + ret_span, + "you might have meant to return the `match` expression", + ); + err.tool_only_span_suggestion( + semi_span, + "remove this semicolon", + String::new(), + Applicability::MaybeIncorrect, + ); }), false, ); @@ -204,7 +206,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We won't diverge unless the scrutinee or all arms diverge. self.diverges.set(scrut_diverges | all_arms_diverge); - coercion.complete(self) + let match_ty = coercion.complete(self); + debug!(?match_ty); + match_ty } fn get_appropriate_arm_semicolon_removal_span( diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_typeck/src/check/callee.rs index 580fb7c3e0f..0a84d41b4f3 100644 --- a/compiler/rustc_typeck/src/check/callee.rs +++ b/compiler/rustc_typeck/src/check/callee.rs @@ -59,7 +59,7 @@ pub fn check_legal_trait_for_method_call( enum CallStep<'tcx> { Builtin(Ty<'tcx>), - DeferredClosure(ty::FnSig<'tcx>), + DeferredClosure(DefId, ty::FnSig<'tcx>), /// E.g., enum variant constructors. Overloaded(MethodCallee<'tcx>), } @@ -107,8 +107,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.confirm_builtin_call(call_expr, callee_expr, callee_ty, arg_exprs, expected) } - Some(CallStep::DeferredClosure(fn_sig)) => { - self.confirm_deferred_closure_call(call_expr, arg_exprs, expected, fn_sig) + Some(CallStep::DeferredClosure(def_id, fn_sig)) => { + self.confirm_deferred_closure_call(call_expr, arg_exprs, expected, def_id, fn_sig) } Some(CallStep::Overloaded(method_callee)) => { @@ -171,7 +171,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { closure_substs: substs, }, ); - return Some(CallStep::DeferredClosure(closure_sig)); + return Some(CallStep::DeferredClosure(def_id, closure_sig)); } } @@ -339,7 +339,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Ty<'tcx> { let (fn_sig, def_id) = match *callee_ty.kind() { ty::FnDef(def_id, subst) => { - let fn_sig = self.tcx.fn_sig(def_id).subst(self.tcx, subst); + let fn_sig = self.tcx.bound_fn_sig(def_id).subst(self.tcx, subst); // Unit testing: function items annotated with // `#[rustc_evaluate_where_clauses]` trigger special output @@ -533,6 +533,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr: &'tcx hir::Expr<'tcx>, arg_exprs: &'tcx [hir::Expr<'tcx>], expected: Expectation<'tcx>, + closure_def_id: DefId, fn_sig: ty::FnSig<'tcx>, ) -> Ty<'tcx> { // `fn_sig` is the *signature* of the closure being called. We @@ -555,7 +556,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { arg_exprs, fn_sig.c_variadic, TupleArgumentsFlag::TupleArguments, - None, + Some(closure_def_id), ); fn_sig.output() diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_typeck/src/check/cast.rs index a153997599a..d9aaf730efc 100644 --- a/compiler/rustc_typeck/src/check/cast.rs +++ b/compiler/rustc_typeck/src/check/cast.rs @@ -347,16 +347,22 @@ impl<'a, 'tcx> CastCheck<'tcx> { ); err.span_label(self.span, "invalid cast"); if self.expr_ty.is_numeric() { - err.span_help( - self.span, - if self.expr_ty == fcx.tcx.types.i8 { - "try casting from `u8` instead" - } else if self.expr_ty == fcx.tcx.types.u32 { - "try `char::from_u32` instead" - } else { - "try `char::from_u32` instead (via a `u32`)" - }, - ); + if self.expr_ty == fcx.tcx.types.u32 { + match fcx.tcx.sess.source_map().span_to_snippet(self.expr.span) { + Ok(snippet) => err.span_suggestion( + self.span, + "try `char::from_u32` instead", + format!("char::from_u32({snippet})"), + Applicability::MachineApplicable, + ), + + Err(_) => err.span_help(self.span, "try `char::from_u32` instead"), + }; + } else if self.expr_ty == fcx.tcx.types.i8 { + err.span_help(self.span, "try casting from `u8` instead"); + } else { + err.span_help(self.span, "try `char::from_u32` instead (via a `u32`)"); + }; } err.emit(); } diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs index 6a4c0c2091e..3e76738cc5d 100644 --- a/compiler/rustc_typeck/src/check/check.rs +++ b/compiler/rustc_typeck/src/check/check.rs @@ -27,6 +27,7 @@ use rustc_trait_selection::traits; use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; use rustc_ty_utils::representability::{self, Representability}; +use rustc_hir::def::DefKind; use std::iter; use std::ops::ControlFlow; @@ -170,7 +171,7 @@ pub(super) fn check_fn<'a, 'tcx>( let va_list_did = tcx.require_lang_item(LangItem::VaList, Some(span)); let region = fcx.next_region_var(RegionVariableOrigin::MiscVariable(span)); - Some(tcx.type_of(va_list_did).subst(tcx, &[region.into()])) + Some(tcx.bound_type_of(va_list_did).subst(tcx, &[region.into()])) } else { None }; @@ -654,7 +655,7 @@ fn check_opaque_meets_bounds<'tcx>( span: Span, origin: &hir::OpaqueTyOrigin, ) { - let hidden_type = tcx.type_of(def_id).subst(tcx, substs); + let hidden_type = tcx.bound_type_of(def_id.to_def_id()).subst(tcx, substs); let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); let defining_use_anchor = match *origin { @@ -711,28 +712,35 @@ fn check_opaque_meets_bounds<'tcx>( }); } -pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) { +pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) { debug!( "check_item_type(it.def_id={:?}, it.name={})", - it.def_id, - tcx.def_path_str(it.def_id.to_def_id()) + id.def_id, + tcx.def_path_str(id.def_id.to_def_id()) ); let _indenter = indenter(); - match it.kind { - // Consts can play a role in type-checking, so they are included here. - hir::ItemKind::Static(..) => { - tcx.ensure().typeck(it.def_id); - maybe_check_static_with_link_section(tcx, it.def_id, it.span); - check_static_inhabited(tcx, it.def_id, it.span); + match tcx.def_kind(id.def_id) { + DefKind::Static(..) => { + tcx.ensure().typeck(id.def_id); + maybe_check_static_with_link_section(tcx, id.def_id, tcx.def_span(id.def_id)); + check_static_inhabited(tcx, id.def_id, tcx.def_span(id.def_id)); } - hir::ItemKind::Const(..) => { - tcx.ensure().typeck(it.def_id); + DefKind::Const => { + tcx.ensure().typeck(id.def_id); } - hir::ItemKind::Enum(ref enum_definition, _) => { - check_enum(tcx, it.span, &enum_definition.variants, it.def_id); + DefKind::Enum => { + let item = tcx.hir().item(id); + let hir::ItemKind::Enum(ref enum_definition, _) = item.kind else { + return; + }; + check_enum(tcx, item.span, &enum_definition.variants, item.def_id); } - hir::ItemKind::Fn(..) => {} // entirely within check_item_body - hir::ItemKind::Impl(ref impl_) => { + DefKind::Fn => {} // entirely within check_item_body + DefKind::Impl => { + let it = tcx.hir().item(id); + let hir::ItemKind::Impl(ref impl_) = it.kind else { + return; + }; debug!("ItemKind::Impl {} with id {:?}", it.ident, it.def_id); if let Some(impl_trait_ref) = tcx.impl_trait_ref(it.def_id) { check_impl_items_against_trait( @@ -745,7 +753,11 @@ pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) { check_on_unimplemented(tcx, it); } } - hir::ItemKind::Trait(_, _, _, _, ref items) => { + DefKind::Trait => { + let it = tcx.hir().item(id); + let hir::ItemKind::Trait(_, _, _, _, ref items) = it.kind else { + return; + }; check_on_unimplemented(tcx, it); for item in items.iter() { @@ -771,28 +783,36 @@ pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) { } } } - hir::ItemKind::Struct(..) => { - check_struct(tcx, it.def_id, it.span); + DefKind::Struct => { + check_struct(tcx, id.def_id, tcx.def_span(id.def_id)); } - hir::ItemKind::Union(..) => { - check_union(tcx, it.def_id, it.span); + DefKind::Union => { + check_union(tcx, id.def_id, tcx.def_span(id.def_id)); } - hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => { + DefKind::OpaqueTy => { + let item = tcx.hir().item(id); + let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item.kind else { + return; + }; // HACK(jynelson): trying to infer the type of `impl trait` breaks documenting // `async-std` (and `pub async fn` in general). // Since rustdoc doesn't care about the concrete type behind `impl Trait`, just don't look at it! // See https://github.com/rust-lang/rust/issues/75100 if !tcx.sess.opts.actually_rustdoc { - let substs = InternalSubsts::identity_for_item(tcx, it.def_id.to_def_id()); - check_opaque(tcx, it.def_id, substs, it.span, &origin); + let substs = InternalSubsts::identity_for_item(tcx, item.def_id.to_def_id()); + check_opaque(tcx, item.def_id, substs, item.span, &origin); } } - hir::ItemKind::TyAlias(..) => { - let pty_ty = tcx.type_of(it.def_id); - let generics = tcx.generics_of(it.def_id); + DefKind::TyAlias => { + let pty_ty = tcx.type_of(id.def_id); + let generics = tcx.generics_of(id.def_id); check_type_params_are_used(tcx, &generics, pty_ty); } - hir::ItemKind::ForeignMod { abi, items } => { + DefKind::ForeignMod => { + let it = tcx.hir().item(id); + let hir::ItemKind::ForeignMod { abi, items } = it.kind else { + return; + }; check_abi(tcx, it.hir_id(), it.span, abi); if abi == Abi::RustIntrinsic { @@ -851,7 +871,7 @@ pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) { } } } - _ => { /* nothing to do */ } + _ => {} } } @@ -1036,9 +1056,7 @@ fn check_impl_items_against_trait<'tcx>( if let Some(missing_items) = must_implement_one_of { let impl_span = tcx.sess.source_map().guess_head_span(full_impl_span); let attr_span = tcx - .get_attrs(impl_trait_ref.def_id) - .iter() - .find(|attr| attr.has_name(sym::rustc_must_implement_one_of)) + .get_attr(impl_trait_ref.def_id, sym::rustc_must_implement_one_of) .map(|attr| attr.span); missing_items_must_implement_one_of_err(tcx, impl_span, missing_items, attr_span); @@ -1138,20 +1156,20 @@ pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) { pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) { let repr = def.repr(); if repr.packed() { - for attr in tcx.get_attrs(def.did()).iter() { - for r in attr::find_repr_attrs(&tcx.sess, attr) { + for attr in tcx.get_attrs(def.did(), sym::repr) { + for r in attr::parse_repr_attr(&tcx.sess, attr) { if let attr::ReprPacked(pack) = r - && let Some(repr_pack) = repr.pack - && pack as u64 != repr_pack.bytes() - { - struct_span_err!( - tcx.sess, - sp, - E0634, - "type has conflicting packed representation hints" - ) - .emit(); - } + && let Some(repr_pack) = repr.pack + && pack as u64 != repr_pack.bytes() + { + struct_span_err!( + tcx.sess, + sp, + E0634, + "type has conflicting packed representation hints" + ) + .emit(); + } } } if repr.align.is_some() { @@ -1301,8 +1319,7 @@ fn check_enum<'tcx>( def.destructor(tcx); // force the destructor to be evaluated if vs.is_empty() { - let attributes = tcx.get_attrs(def_id.to_def_id()); - if let Some(attr) = tcx.sess.find_by_name(&attributes, sym::repr) { + if let Some(attr) = tcx.get_attr(def_id.to_def_id(), sym::repr) { struct_span_err!( tcx.sess, attr.span, @@ -1451,7 +1468,10 @@ pub(super) fn check_type_params_are_used<'tcx>( } pub(super) fn check_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { - tcx.hir().visit_item_likes_in_module(module_def_id, &mut CheckItemTypesVisitor { tcx }); + let module = tcx.hir_module_items(module_def_id); + for id in module.items() { + check_item_type(tcx, id); + } } pub(super) use wfcheck::check_item_well_formed; diff --git a/compiler/rustc_typeck/src/check/closure.rs b/compiler/rustc_typeck/src/check/closure.rs index 320be9b44d4..c8fe0468736 100644 --- a/compiler/rustc_typeck/src/check/closure.rs +++ b/compiler/rustc_typeck/src/check/closure.rs @@ -175,19 +175,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) { match *expected_ty.kind() { ty::Opaque(def_id, substs) => { - let bounds = self.tcx.explicit_item_bounds(def_id); - let sig = bounds.iter().find_map(|(pred, span)| match pred.kind().skip_binder() { - ty::PredicateKind::Projection(proj_predicate) => self - .deduce_sig_from_projection( - Some(*span), - pred.kind().rebind(proj_predicate.subst(self.tcx, substs)), - ), - _ => None, - }); + let bounds = self.tcx.bound_explicit_item_bounds(def_id); + let sig = bounds + .transpose_iter() + .map(|e| e.map_bound(|e| *e).transpose_tuple2()) + .find_map(|(pred, span)| match pred.0.kind().skip_binder() { + ty::PredicateKind::Projection(proj_predicate) => self + .deduce_sig_from_projection( + Some(span.0), + pred.0.kind().rebind( + pred.map_bound(|_| proj_predicate).subst(self.tcx, substs), + ), + ), + _ => None, + }); let kind = bounds - .iter() - .filter_map(|(pred, _)| match pred.kind().skip_binder() { + .transpose_iter() + .map(|e| e.map_bound(|e| *e).transpose_tuple2()) + .filter_map(|(pred, _)| match pred.0.kind().skip_binder() { ty::PredicateKind::Trait(tp) => { self.tcx.fn_trait_kind_from_lang_item(tp.def_id()) } @@ -371,7 +377,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// # Examples /// - /// ``` + /// ```ignore (illustrative) /// fn with_closure<F>(_: F) /// where F: Fn(&u32) -> &u32 { .. } /// @@ -668,7 +674,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), }; - let item_bounds = self.tcx.explicit_item_bounds(def_id); + let item_bounds = self.tcx.bound_explicit_item_bounds(def_id); // Search for a pending obligation like // @@ -676,17 +682,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // // where R is the return type we are expecting. This type `T` // will be our output. - let output_ty = item_bounds.iter().find_map(|&(predicate, span)| { - let bound_predicate = predicate.subst(self.tcx, substs).kind(); - if let ty::PredicateKind::Projection(proj_predicate) = bound_predicate.skip_binder() { - self.deduce_future_output_from_projection( - span, - bound_predicate.rebind(proj_predicate), - ) - } else { - None - } - }); + let output_ty = item_bounds + .transpose_iter() + .map(|e| e.map_bound(|e| *e).transpose_tuple2()) + .find_map(|(predicate, span)| { + let bound_predicate = predicate.subst(self.tcx, substs).kind(); + if let ty::PredicateKind::Projection(proj_predicate) = bound_predicate.skip_binder() + { + self.deduce_future_output_from_projection( + span.0, + bound_predicate.rebind(proj_predicate), + ) + } else { + None + } + }); debug!("deduce_future_output_from_obligations: output_ty={:?}", output_ty); output_ty diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs index b44baf83cbe..6d540bf7e4f 100644 --- a/compiler/rustc_typeck/src/check/coercion.rs +++ b/compiler/rustc_typeck/src/check/coercion.rs @@ -21,7 +21,7 @@ //! When inferring the generic arguments of functions, the argument //! order is relevant, which can lead to the following edge case: //! -//! ```rust +//! ```ignore (illustrative) //! fn foo<T>(a: T, b: T) { //! // ... //! } @@ -58,7 +58,8 @@ use rustc_session::parse::feature_err; use rustc_span::symbol::sym; use rustc_span::{self, BytePos, DesugaringKind, Span}; use rustc_target::spec::abi::Abi; -use rustc_trait_selection::traits::error_reporting::InferCtxtExt; +use rustc_trait_selection::infer::InferCtxtExt as _; +use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode}; use smallvec::{smallvec, SmallVec}; @@ -615,7 +616,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { )]; let mut has_unsized_tuple_coercion = false; - let mut has_trait_upcasting_coercion = false; + let mut has_trait_upcasting_coercion = None; // Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid // emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where @@ -635,7 +636,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { && data_a.principal_def_id() != data_b.principal_def_id() { debug!("coerce_unsized: found trait upcasting coercion"); - has_trait_upcasting_coercion = true; + has_trait_upcasting_coercion = Some((self_ty, unsize_ty)); } if let ty::Tuple(..) = unsize_ty.kind() { debug!("coerce_unsized: found unsized tuple coercion"); @@ -706,14 +707,19 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { .emit(); } - if has_trait_upcasting_coercion && !self.tcx().features().trait_upcasting { - feature_err( + if let Some((sub, sup)) = has_trait_upcasting_coercion + && !self.tcx().features().trait_upcasting + { + // Renders better when we erase regions, since they're not really the point here. + let (sub, sup) = self.tcx.erase_regions((sub, sup)); + let mut err = feature_err( &self.tcx.sess.parse_sess, sym::trait_upcasting, self.cause.span, - "trait upcasting coercion is experimental", - ) - .emit(); + &format!("cannot cast `{sub}` to `{sup}`, trait upcasting coercion is experimental"), + ); + err.note(&format!("required when coercing `{source}` into `{target}`")); + err.emit(); } Ok(coercion) @@ -731,14 +737,27 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { F: FnOnce(Ty<'tcx>) -> Vec<Adjustment<'tcx>>, G: FnOnce(Ty<'tcx>) -> Vec<Adjustment<'tcx>>, { - if let ty::FnPtr(fn_ty_b) = b.kind() - && let (hir::Unsafety::Normal, hir::Unsafety::Unsafe) = - (fn_ty_a.unsafety(), fn_ty_b.unsafety()) - { - let unsafe_a = self.tcx.safe_to_unsafe_fn_ty(fn_ty_a); - return self.unify_and(unsafe_a, b, to_unsafe); - } - self.unify_and(a, b, normal) + self.commit_unconditionally(|snapshot| { + let result = if let ty::FnPtr(fn_ty_b) = b.kind() + && let (hir::Unsafety::Normal, hir::Unsafety::Unsafe) = + (fn_ty_a.unsafety(), fn_ty_b.unsafety()) + { + let unsafe_a = self.tcx.safe_to_unsafe_fn_ty(fn_ty_a); + self.unify_and(unsafe_a, b, to_unsafe) + } else { + self.unify_and(a, b, normal) + }; + + // FIXME(#73154): This is a hack. Currently LUB can generate + // unsolvable constraints. Additionally, it returns `a` + // unconditionally, even when the "LUB" is `b`. In the future, we + // want the coerced type to be the actual supertype of these two, + // but for now, we want to just error to ensure we don't lock + // ourselves into a specific behavior with NLL. + self.leak_check(false, snapshot)?; + + result + }) } fn coerce_from_fn_pointer( @@ -775,17 +794,19 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { match b.kind() { ty::FnPtr(b_sig) => { let a_sig = a.fn_sig(self.tcx); - // Intrinsics are not coercible to function pointers - if a_sig.abi() == Abi::RustIntrinsic || a_sig.abi() == Abi::PlatformIntrinsic { - return Err(TypeError::IntrinsicCast); - } + if let ty::FnDef(def_id, _) = *a.kind() { + // Intrinsics are not coercible to function pointers + if self.tcx.is_intrinsic(def_id) { + return Err(TypeError::IntrinsicCast); + } + + // Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396). - // Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396). - if let ty::FnDef(def_id, _) = *a.kind() - && b_sig.unsafety() == hir::Unsafety::Normal - && !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty() - { - return Err(TypeError::TargetFeatureCast(def_id)); + if b_sig.unsafety() == hir::Unsafety::Normal + && !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty() + { + return Err(TypeError::TargetFeatureCast(def_id)); + } } let InferOk { value: a_sig, obligations: o1 } = @@ -960,6 +981,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .find_map(|(ty, steps)| self.probe(|_| coerce.unify(ty, target)).ok().map(|_| steps)) } + /// Given a type, this function will calculate and return the type given + /// for `<Ty as Deref>::Target` only if `Ty` also implements `DerefMut`. + /// + /// This function is for diagnostics only, since it does not register + /// trait or region sub-obligations. (presumably we could, but it's not + /// particularly important for diagnostics...) + pub fn deref_once_mutably_for_diagnostic(&self, expr_ty: Ty<'tcx>) -> Option<Ty<'tcx>> { + self.autoderef(rustc_span::DUMMY_SP, expr_ty).nth(1).and_then(|(deref_ty, _)| { + self.infcx + .type_implements_trait( + self.infcx.tcx.lang_items().deref_mut_trait()?, + expr_ty, + ty::List::empty(), + self.param_env, + ) + .may_apply() + .then(|| deref_ty) + }) + } + /// Given some expressions, their known unified type and another expression, /// tries to unify the types, potentially inserting coercions on any of the /// provided expressions and returns their LUB (aka "common supertype"). @@ -1105,8 +1146,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (adjustments, target) = self.register_infer_ok_obligations(ok); self.apply_adjustments(new, adjustments); debug!( - "coercion::try_find_coercion_lub: was able to coerce from previous type {:?} to new type {:?}", - prev_ty, new_ty, + "coercion::try_find_coercion_lub: was able to coerce from new type {:?} to previous type {:?} ({:?})", + new_ty, prev_ty, target ); return Ok(target); } @@ -1162,15 +1203,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } Ok(ok) => { - debug!( - "coercion::try_find_coercion_lub: was able to coerce previous type {:?} to new type {:?}", - prev_ty, new_ty, - ); let (adjustments, target) = self.register_infer_ok_obligations(ok); for expr in exprs { let expr = expr.as_coercion_site(); self.apply_adjustments(expr, adjustments.clone()); } + debug!( + "coercion::try_find_coercion_lub: was able to coerce previous type {:?} to new type {:?} ({:?})", + prev_ty, new_ty, target + ); Ok(target) } } @@ -1210,7 +1251,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// Example: /// -/// ``` +/// ```ignore (illustrative) /// let mut coerce = CoerceMany::new(expected_ty); /// for expr in exprs { /// let expr_ty = fcx.check_expr_with_expectation(expr, expected); @@ -1323,7 +1364,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { /// is a forced-unit case, and hence `expression_ty` must be /// `Nil`. #[instrument(skip(self, fcx, augment_error, label_expression_as_expected), level = "debug")] - crate fn coerce_inner<'a>( + pub(crate) fn coerce_inner<'a>( &mut self, fcx: &FnCtxt<'a, 'tcx>, cause: &ObligationCause<'tcx>, @@ -1402,6 +1443,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { }) }; + debug!(?result); match result { Ok(v) => { self.final_ty = Some(v); @@ -1492,7 +1534,10 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { augment_error(&mut err); } - if let Some(expr) = expression { + let is_insufficiently_polymorphic = + matches!(coercion_error, TypeError::RegionsInsufficientlyPolymorphic(..)); + + if !is_insufficiently_polymorphic && let Some(expr) = expression { fcx.emit_coerce_suggestions( &mut err, expr, diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs index 6d78a863d54..277bc1cf0f0 100644 --- a/compiler/rustc_typeck/src/check/compare_method.rs +++ b/compiler/rustc_typeck/src/check/compare_method.rs @@ -7,10 +7,10 @@ use rustc_hir::intravisit; use rustc_hir::{GenericParamKind, ImplItemKind, TraitItemKind}; use rustc_infer::infer::{self, InferOk, TyCtxtInferExt}; use rustc_infer::traits::util; -use rustc_middle::ty; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::subst::{InternalSubsts, Subst}; use rustc_middle::ty::util::ExplicitSelf; +use rustc_middle::ty::{self, DefIdTree}; use rustc_middle::ty::{GenericParamDefKind, ToPredicate, TyCtxt}; use rustc_span::Span; use rustc_trait_selection::traits::error_reporting::InferCtxtExt; @@ -28,7 +28,7 @@ use super::{potentially_plural_count, FnCtxt, Inherited}; /// - `impl_m_span`: span to use for reporting errors /// - `trait_m`: the method in the trait /// - `impl_trait_ref`: the TraitRef corresponding to the trait implementation -crate fn compare_impl_method<'tcx>( +pub(crate) fn compare_impl_method<'tcx>( tcx: TyCtxt<'tcx>, impl_m: &ty::AssocItem, impl_m_span: Span, @@ -48,6 +48,10 @@ crate fn compare_impl_method<'tcx>( return; } + if let Err(_) = compare_generic_param_kinds(tcx, impl_m, trait_m) { + return; + } + if let Err(_) = compare_number_of_method_arguments(tcx, impl_m, impl_m_span, trait_m, trait_item_span) { @@ -62,10 +66,6 @@ crate fn compare_impl_method<'tcx>( { return; } - - if let Err(_) = compare_const_param_types(tcx, impl_m, trait_m, trait_item_span) { - return; - } } fn compare_predicate_entailment<'tcx>( @@ -265,9 +265,8 @@ fn compare_predicate_entailment<'tcx>( let impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig)); debug!("compare_impl_method: impl_fty={:?}", impl_fty); - // First liberate late bound regions and subst placeholders - let trait_sig = tcx.liberate_late_bound_regions(impl_m.def_id, tcx.fn_sig(trait_m.def_id)); - let trait_sig = trait_sig.subst(tcx, trait_to_placeholder_substs); + let trait_sig = tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs); + let trait_sig = tcx.liberate_late_bound_regions(impl_m.def_id, trait_sig); let trait_sig = inh.normalize_associated_types_in(impl_m_span, impl_m_hir_id, param_env, trait_sig); // Add the resulting inputs and output as well-formed. @@ -579,6 +578,27 @@ fn compare_self_type<'tcx>( Ok(()) } +/// Checks that the number of generics on a given assoc item in a trait impl is the same +/// as the number of generics on the respective assoc item in the trait definition. +/// +/// For example this code emits the errors in the following code: +/// ``` +/// trait Trait { +/// fn foo(); +/// type Assoc<T>; +/// } +/// +/// impl Trait for () { +/// fn foo<T>() {} +/// //~^ error +/// type Assoc = u32; +/// //~^ error +/// } +/// ``` +/// +/// Notably this does not error on `foo<T>` implemented as `foo<const N: u8>` or +/// `foo<const N: u8>` implemented as `foo<const N: u32>`. This is handled in +/// [`compare_generic_param_kinds`]. This function also does not handle lifetime parameters fn compare_number_of_generics<'tcx>( tcx: TyCtxt<'tcx>, impl_: &ty::AssocItem, @@ -589,6 +609,15 @@ fn compare_number_of_generics<'tcx>( let trait_own_counts = tcx.generics_of(trait_.def_id).own_counts(); let impl_own_counts = tcx.generics_of(impl_.def_id).own_counts(); + // This avoids us erroring on `foo<T>` implemented as `foo<const N: u8>` as this is implemented + // in `compare_generic_param_kinds` which will give a nicer error message than something like: + // "expected 1 type parameter, found 0 type parameters" + if (trait_own_counts.types + trait_own_counts.consts) + == (impl_own_counts.types + impl_own_counts.consts) + { + return Ok(()); + } + let matchings = [ ("type", trait_own_counts.types, impl_own_counts.types), ("const", trait_own_counts.consts, impl_own_counts.consts), @@ -914,60 +943,93 @@ fn compare_synthetic_generics<'tcx>( if let Some(reported) = error_found { Err(reported) } else { Ok(()) } } -fn compare_const_param_types<'tcx>( +/// Checks that all parameters in the generics of a given assoc item in a trait impl have +/// the same kind as the respective generic parameter in the trait def. +/// +/// For example all 4 errors in the following code are emitted here: +/// ``` +/// trait Foo { +/// fn foo<const N: u8>(); +/// type bar<const N: u8>; +/// fn baz<const N: u32>(); +/// type blah<T>; +/// } +/// +/// impl Foo for () { +/// fn foo<const N: u64>() {} +/// //~^ error +/// type bar<const N: u64> {} +/// //~^ error +/// fn baz<T>() {} +/// //~^ error +/// type blah<const N: i64> = u32; +/// //~^ error +/// } +/// ``` +/// +/// This function does not handle lifetime parameters +fn compare_generic_param_kinds<'tcx>( tcx: TyCtxt<'tcx>, - impl_m: &ty::AssocItem, - trait_m: &ty::AssocItem, - trait_item_span: Option<Span>, + impl_item: &ty::AssocItem, + trait_item: &ty::AssocItem, ) -> Result<(), ErrorGuaranteed> { - let const_params_of = |def_id| { - tcx.generics_of(def_id).params.iter().filter_map(|param| match param.kind { - GenericParamDefKind::Const { .. } => Some(param.def_id), - _ => None, + assert_eq!(impl_item.kind, trait_item.kind); + + let ty_const_params_of = |def_id| { + tcx.generics_of(def_id).params.iter().filter(|param| { + matches!( + param.kind, + GenericParamDefKind::Const { .. } | GenericParamDefKind::Type { .. } + ) }) }; - let const_params_impl = const_params_of(impl_m.def_id); - let const_params_trait = const_params_of(trait_m.def_id); - - for (const_param_impl, const_param_trait) in iter::zip(const_params_impl, const_params_trait) { - let impl_ty = tcx.type_of(const_param_impl); - let trait_ty = tcx.type_of(const_param_trait); - if impl_ty != trait_ty { - let (impl_span, impl_ident) = match tcx.hir().get_if_local(const_param_impl) { - Some(hir::Node::GenericParam(hir::GenericParam { span, name, .. })) => ( - span, - match name { - hir::ParamName::Plain(ident) => Some(ident), - _ => None, - }, - ), - other => bug!( - "expected GenericParam, found {:?}", - other.map_or_else(|| "nothing".to_string(), |n| format!("{:?}", n)) - ), - }; - let trait_span = match tcx.hir().get_if_local(const_param_trait) { - Some(hir::Node::GenericParam(hir::GenericParam { span, .. })) => Some(span), - _ => None, - }; + + for (param_impl, param_trait) in + iter::zip(ty_const_params_of(impl_item.def_id), ty_const_params_of(trait_item.def_id)) + { + use GenericParamDefKind::*; + if match (¶m_impl.kind, ¶m_trait.kind) { + (Const { .. }, Const { .. }) + if tcx.type_of(param_impl.def_id) != tcx.type_of(param_trait.def_id) => + { + true + } + (Const { .. }, Type { .. }) | (Type { .. }, Const { .. }) => true, + // this is exhaustive so that anyone adding new generic param kinds knows + // to make sure this error is reported for them. + (Const { .. }, Const { .. }) | (Type { .. }, Type { .. }) => false, + (Lifetime { .. }, _) | (_, Lifetime { .. }) => unreachable!(), + } { + let param_impl_span = tcx.def_span(param_impl.def_id); + let param_trait_span = tcx.def_span(param_trait.def_id); + let mut err = struct_span_err!( tcx.sess, - *impl_span, + param_impl_span, E0053, - "method `{}` has an incompatible const parameter type for trait", - trait_m.name - ); - err.span_note( - trait_span.map_or_else(|| trait_item_span.unwrap_or(*impl_span), |span| *span), - &format!( - "the const parameter{} has type `{}`, but the declaration \ - in trait `{}` has type `{}`", - &impl_ident.map_or_else(|| "".to_string(), |ident| format!(" `{ident}`")), - impl_ty, - tcx.def_path_str(trait_m.def_id), - trait_ty - ), + "{} `{}` has an incompatible generic parameter for trait `{}`", + assoc_item_kind_str(&impl_item), + trait_item.name, + &tcx.def_path_str(tcx.parent(trait_item.def_id)) ); + + let make_param_message = |prefix: &str, param: &ty::GenericParamDef| match param.kind { + Const { .. } => { + format!("{} const parameter of type `{}`", prefix, tcx.type_of(param.def_id)) + } + Type { .. } => format!("{} type parameter", prefix), + Lifetime { .. } => unreachable!(), + }; + + let trait_header_span = tcx.def_ident_span(tcx.parent(trait_item.def_id)).unwrap(); + err.span_label(trait_header_span, ""); + err.span_label(param_trait_span, make_param_message("expected", param_trait)); + + let impl_header_span = + tcx.sess.source_map().guess_head_span(tcx.def_span(tcx.parent(impl_item.def_id))); + err.span_label(impl_header_span, ""); + err.span_label(param_impl_span, make_param_message("found", param_impl)); + let reported = err.emit(); return Err(reported); } @@ -976,7 +1038,7 @@ fn compare_const_param_types<'tcx>( Ok(()) } -crate fn compare_const_impl<'tcx>( +pub(crate) fn compare_const_impl<'tcx>( tcx: TyCtxt<'tcx>, impl_c: &ty::AssocItem, impl_c_span: Span, @@ -1003,7 +1065,7 @@ crate fn compare_const_impl<'tcx>( // Compute placeholder form of impl and trait const tys. let impl_ty = tcx.type_of(impl_c.def_id); - let trait_ty = tcx.type_of(trait_c.def_id).subst(tcx, trait_to_impl_substs); + let trait_ty = tcx.bound_type_of(trait_c.def_id).subst(tcx, trait_to_impl_substs); let mut cause = ObligationCause::new( impl_c_span, impl_c_hir_id, @@ -1082,7 +1144,7 @@ crate fn compare_const_impl<'tcx>( }); } -crate fn compare_ty_impl<'tcx>( +pub(crate) fn compare_ty_impl<'tcx>( tcx: TyCtxt<'tcx>, impl_ty: &ty::AssocItem, impl_ty_span: Span, @@ -1095,6 +1157,8 @@ crate fn compare_ty_impl<'tcx>( let _: Result<(), ErrorGuaranteed> = (|| { compare_number_of_generics(tcx, impl_ty, impl_ty_span, trait_ty, trait_item_span)?; + compare_generic_param_kinds(tcx, impl_ty, trait_ty)?; + let sp = tcx.def_span(impl_ty.def_id); compare_type_predicate_entailment(tcx, impl_ty, sp, trait_ty, impl_trait_ref)?; @@ -1387,14 +1451,15 @@ pub fn check_type_bounds<'tcx>( }; let obligations = tcx - .explicit_item_bounds(trait_ty.def_id) - .iter() - .map(|&(bound, span)| { + .bound_explicit_item_bounds(trait_ty.def_id) + .transpose_iter() + .map(|e| e.map_bound(|e| *e).transpose_tuple2()) + .map(|(bound, span)| { debug!(?bound); let concrete_ty_bound = bound.subst(tcx, rebased_substs); debug!("check_type_bounds: concrete_ty_bound = {:?}", concrete_ty_bound); - traits::Obligation::new(mk_cause(span), param_env, concrete_ty_bound) + traits::Obligation::new(mk_cause(span.0), param_env, concrete_ty_bound) }) .collect(); debug!("check_type_bounds: item_bounds={:?}", obligations); diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index f377cf3678e..d0d2841209a 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -129,6 +129,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// N.B., this code relies on `self.diverges` to be accurate. In particular, assignments to `!` /// will be permitted if the diverges flag is currently "always". + #[tracing::instrument(level = "debug", skip(self, expr, expected_ty_expr, allow_two_phase))] pub fn demand_coerce_diag( &self, expr: &hir::Expr<'tcx>, @@ -150,7 +151,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let expr_ty = self.resolve_vars_with_obligations(checked_ty); let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e.clone()); - self.emit_coerce_suggestions(&mut err, expr, expr_ty, expected, expected_ty_expr, Some(e)); + let is_insufficiently_polymorphic = + matches!(e, TypeError::RegionsInsufficientlyPolymorphic(..)); + + // FIXME(#73154): For now, we do leak check when coercing function + // pointers in typeck, instead of only during borrowck. This can lead + // to these `RegionsInsufficientlyPolymorphic` errors that aren't helpful. + if !is_insufficiently_polymorphic { + self.emit_coerce_suggestions( + &mut err, + expr, + expr_ty, + expected, + expected_ty_expr, + Some(e), + ); + } (expected, Some(err)) } @@ -407,8 +423,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.has_only_self_parameter(m) && self .tcx - .get_attrs(m.def_id) - .iter() // This special internal attribute is used to permit // "identity-like" conversion methods to be suggested here. // @@ -419,7 +433,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // // FIXME? Other potential candidate methods: `as_ref` and // `as_mut`? - .any(|a| a.has_name(sym::rustc_conversion_suggestion)) + .has_attr(m.def_id, sym::rustc_conversion_suggestion) }); methods @@ -439,7 +453,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Identify some cases where `as_ref()` would be appropriate and suggest it. /// /// Given the following code: - /// ``` + /// ```compile_fail,E0308 /// struct Foo; /// fn takes_ref(_: &Foo) {} /// let ref opt = Some(Foo); @@ -449,7 +463,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Suggest using `opt.as_ref().map(|param| takes_ref(param));` instead. /// /// It only checks for `Option` and `Result` and won't work with - /// ``` + /// ```ignore (illustrative) /// opt.map(|param| { takes_ref(param) }); /// ``` fn can_use_as_ref(&self, expr: &hir::Expr<'_>) -> Option<(Span, &'static str, String)> { @@ -505,7 +519,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - crate fn maybe_get_struct_pattern_shorthand_field( + pub(crate) fn maybe_get_struct_pattern_shorthand_field( &self, expr: &hir::Expr<'_>, ) -> Option<Symbol> { @@ -541,7 +555,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// If the given `HirId` corresponds to a block with a trailing expression, return that expression - crate fn maybe_get_block_expr(&self, expr: &hir::Expr<'tcx>) -> Option<&'tcx hir::Expr<'tcx>> { + pub(crate) fn maybe_get_block_expr( + &self, + expr: &hir::Expr<'tcx>, + ) -> Option<&'tcx hir::Expr<'tcx>> { match expr { hir::Expr { kind: hir::ExprKind::Block(block, ..), .. } => block.expr, _ => None, @@ -549,7 +566,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Returns whether the given expression is an `else if`. - crate fn is_else_if_block(&self, expr: &hir::Expr<'_>) -> bool { + pub(crate) fn is_else_if_block(&self, expr: &hir::Expr<'_>) -> bool { if let hir::ExprKind::If(..) = expr.kind { let parent_id = self.tcx.hir().get_parent_node(expr.hir_id); if let Some(Node::Expr(hir::Expr { @@ -566,7 +583,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// This function is used to determine potential "simple" improvements or users' errors and /// provide them useful help. For example: /// - /// ``` + /// ```compile_fail,E0308 /// fn some_fn(s: &str) {} /// /// let x = "hey!".to_owned(); @@ -698,28 +715,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if let Some(hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Assign(left_expr, ..), + kind: hir::ExprKind::Assign(..), .. })) = self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id)) { if mutability == hir::Mutability::Mut { - // Found the following case: - // fn foo(opt: &mut Option<String>){ opt = None } - // --- ^^^^ - // | | - // consider dereferencing here: `*opt` | - // expected mutable reference, found enum `Option` - if sm.span_to_snippet(left_expr.span).is_ok() { - return Some(( - left_expr.span.shrink_to_lo(), - "consider dereferencing here to assign to the mutable \ - borrowed piece of memory" - .to_string(), - "*".to_string(), - Applicability::MachineApplicable, - true, - )); - } + // Suppressing this diagnostic, we'll properly print it in `check_expr_assign` + return None; } } diff --git a/compiler/rustc_typeck/src/check/dropck.rs b/compiler/rustc_typeck/src/check/dropck.rs index 3bc92166543..307064327c5 100644 --- a/compiler/rustc_typeck/src/check/dropck.rs +++ b/compiler/rustc_typeck/src/check/dropck.rs @@ -2,17 +2,14 @@ use crate::check::regionck::RegionCtxt; use crate::hir; use crate::hir::def_id::{DefId, LocalDefId}; use rustc_errors::{struct_span_err, ErrorGuaranteed}; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; -use rustc_infer::infer::{InferOk, RegionckMode, TyCtxtInferExt}; -use rustc_infer::traits::TraitEngineExt as _; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; -use rustc_middle::ty::subst::{Subst, SubstsRef}; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::util::IgnoreRegions; use rustc_middle::ty::{self, Predicate, Ty, TyCtxt}; use rustc_span::Span; -use rustc_trait_selection::traits::error_reporting::InferCtxtExt; use rustc_trait_selection::traits::query::dropck_outlives::AtExt; -use rustc_trait_selection::traits::{ObligationCause, TraitEngine, TraitEngineExt}; +use rustc_trait_selection::traits::ObligationCause; /// This function confirms that the `Drop` implementation identified by /// `drop_impl_did` is not any more specialized than the type it is @@ -39,8 +36,8 @@ pub fn check_drop_impl(tcx: TyCtxt<'_>, drop_impl_did: DefId) -> Result<(), Erro ensure_drop_params_and_item_params_correspond( tcx, drop_impl_did.expect_local(), - dtor_self_type, adt_def.did(), + self_to_impl_substs, )?; ensure_drop_predicates_are_implied_by_item_defn( @@ -67,75 +64,34 @@ pub fn check_drop_impl(tcx: TyCtxt<'_>, drop_impl_did: DefId) -> Result<(), Erro fn ensure_drop_params_and_item_params_correspond<'tcx>( tcx: TyCtxt<'tcx>, drop_impl_did: LocalDefId, - drop_impl_ty: Ty<'tcx>, self_type_did: DefId, + drop_impl_substs: SubstsRef<'tcx>, ) -> Result<(), ErrorGuaranteed> { - let drop_impl_hir_id = tcx.hir().local_def_id_to_hir_id(drop_impl_did); - - // check that the impl type can be made to match the trait type. - - tcx.infer_ctxt().enter(|ref infcx| { - let impl_param_env = tcx.param_env(self_type_did); - let tcx = infcx.tcx; - let mut fulfillment_cx = <dyn TraitEngine<'_>>::new(tcx); - - let named_type = tcx.type_of(self_type_did); - - let drop_impl_span = tcx.def_span(drop_impl_did); - let fresh_impl_substs = - infcx.fresh_substs_for_item(drop_impl_span, drop_impl_did.to_def_id()); - let fresh_impl_self_ty = drop_impl_ty.subst(tcx, fresh_impl_substs); - - let cause = &ObligationCause::misc(drop_impl_span, drop_impl_hir_id); - match infcx.at(cause, impl_param_env).eq(named_type, fresh_impl_self_ty) { - Ok(InferOk { obligations, .. }) => { - fulfillment_cx.register_predicate_obligations(infcx, obligations); - } - Err(_) => { - let item_span = tcx.def_span(self_type_did); - let self_descr = tcx.def_kind(self_type_did).descr(self_type_did); - let reported = struct_span_err!( - tcx.sess, - drop_impl_span, - E0366, - "`Drop` impls cannot be specialized" - ) - .span_note( - item_span, - &format!( - "use the same sequence of generic type, lifetime and const parameters \ - as the {self_descr} definition", - ), - ) - .emit(); - return Err(reported); - } + let Err(arg) = tcx.uses_unique_generic_params(drop_impl_substs, IgnoreRegions::No) else { + return Ok(()) + }; + + let drop_impl_span = tcx.def_span(drop_impl_did); + let item_span = tcx.def_span(self_type_did); + let self_descr = tcx.def_kind(self_type_did).descr(self_type_did); + let mut err = + struct_span_err!(tcx.sess, drop_impl_span, E0366, "`Drop` impls cannot be specialized"); + match arg { + ty::util::NotUniqueParam::DuplicateParam(arg) => { + err.note(&format!("`{arg}` is mentioned multiple times")) } - - let errors = fulfillment_cx.select_all_or_error(&infcx); - if !errors.is_empty() { - // this could be reached when we get lazy normalization - let reported = infcx.report_fulfillment_errors(&errors, None, false); - return Err(reported); + ty::util::NotUniqueParam::NotParam(arg) => { + err.note(&format!("`{arg}` is not a generic parameter")) } - - // NB. It seems a bit... suspicious to use an empty param-env - // here. The correct thing, I imagine, would be - // `OutlivesEnvironment::new(impl_param_env)`, which would - // allow region solving to take any `a: 'b` relations on the - // impl into account. But I could not create a test case where - // it did the wrong thing, so I chose to preserve existing - // behavior, since it ought to be simply more - // conservative. -nmatsakis - let outlives_env = OutlivesEnvironment::new(ty::ParamEnv::empty()); - - infcx.resolve_regions_and_report_errors( - drop_impl_did.to_def_id(), - &outlives_env, - RegionckMode::default(), - ); - Ok(()) - }) + }; + err.span_note( + item_span, + &format!( + "use the same sequence of generic lifetime, type and const parameters \ + as the {self_descr} definition", + ), + ); + Err(err.emit()) } /// Confirms that every predicate imposed by dtor_predicates is @@ -275,7 +231,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( /// This function is not only checking that the dropck obligations are met for /// the given type, but it's also currently preventing non-regular recursion in /// types from causing stack overflows (dropck_no_diverge_on_nonregular_*.rs). -crate fn check_drop_obligations<'a, 'tcx>( +pub(crate) fn check_drop_obligations<'a, 'tcx>( rcx: &mut RegionCtxt<'a, 'tcx>, ty: Ty<'tcx>, span: Span, @@ -292,7 +248,7 @@ crate fn check_drop_obligations<'a, 'tcx>( // This is an implementation of the TypeRelation trait with the // aim of simply comparing for equality (without side-effects). // It is not intended to be used anywhere else other than here. -crate struct SimpleEqRelation<'tcx> { +pub(crate) struct SimpleEqRelation<'tcx> { tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, } diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index a1e8d2040dd..09b0dc0a0ea 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -44,13 +44,14 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; use rustc_middle::ty::error::ExpectedFound; use rustc_middle::ty::error::TypeError::{FieldMisMatch, Sorts}; use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{self, AdtKind, Ty, TypeFoldable}; +use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TypeFoldable}; use rustc_session::parse::feature_err; use rustc_span::hygiene::DesugaringKind; use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::source_map::Span; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, Pos}; +use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{self, ObligationCauseCode}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -836,6 +837,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lhs: &'tcx hir::Expr<'tcx>, err_code: &'static str, op_span: Span, + adjust_err: impl FnOnce(&mut DiagnosticBuilder<'tcx, ErrorGuaranteed>), ) { if lhs.is_syntactic_place_expr() { return; @@ -858,6 +860,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); }); + adjust_err(&mut err); + err.emit(); } @@ -1050,10 +1054,47 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return self.tcx.ty_error(); } - self.check_lhs_assignable(lhs, "E0070", span); - let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace); - let rhs_ty = self.check_expr_coercable_to_type(&rhs, lhs_ty, Some(lhs)); + + let suggest_deref_binop = |err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>, + rhs_ty: Ty<'tcx>| { + if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) { + // Can only assign if the type is sized, so if `DerefMut` yields a type that is + // unsized, do not suggest dereferencing it. + let lhs_deref_ty_is_sized = self + .infcx + .type_implements_trait( + self.tcx.lang_items().sized_trait().unwrap(), + lhs_deref_ty, + ty::List::empty(), + self.param_env, + ) + .may_apply(); + if lhs_deref_ty_is_sized && self.can_coerce(rhs_ty, lhs_deref_ty) { + err.span_suggestion_verbose( + lhs.span.shrink_to_lo(), + "consider dereferencing here to assign to the mutably borrowed value", + "*".to_string(), + Applicability::MachineApplicable, + ); + } + } + }; + + self.check_lhs_assignable(lhs, "E0070", span, |err| { + let rhs_ty = self.check_expr(&rhs); + suggest_deref_binop(err, rhs_ty); + }); + + // This is (basically) inlined `check_expr_coercable_to_type`, but we want + // to suggest an additional fixup here in `suggest_deref_binop`. + let rhs_ty = self.check_expr_with_hint(&rhs, lhs_ty); + if let (_, Some(mut diag)) = + self.demand_coerce_diag(rhs, rhs_ty, lhs_ty, Some(lhs), AllowTwoPhase::No) + { + suggest_deref_binop(&mut diag, rhs_ty); + diag.emit(); + } self.require_type_is_sized(lhs_ty, lhs.span, traits::AssignmentLhsSized); @@ -1064,7 +1105,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn check_expr_let(&self, let_expr: &'tcx hir::Let<'tcx>) -> Ty<'tcx> { + pub(super) fn check_expr_let(&self, let_expr: &'tcx hir::Let<'tcx>) -> Ty<'tcx> { // for let statements, this is done in check_stmt let init = let_expr.init; self.warn_if_unreachable(init.hir_id, init.span, "block in `let` expression"); @@ -2034,17 +2075,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { base: &'tcx hir::Expr<'tcx>, def_id: DefId, ) { - let local_id = def_id.expect_local(); - let hir_id = self.tcx.hir().local_def_id_to_hir_id(local_id); - let node = self.tcx.hir().get(hir_id); - - if let Some(fields) = node.tuple_fields() { - let kind = match self.tcx.opt_def_kind(local_id) { - Some(DefKind::Ctor(of, _)) => of, - _ => return, - }; + if let Some(local_id) = def_id.as_local() { + let hir_id = self.tcx.hir().local_def_id_to_hir_id(local_id); + let node = self.tcx.hir().get(hir_id); + + if let Some(fields) = node.tuple_fields() { + let kind = match self.tcx.opt_def_kind(local_id) { + Some(DefKind::Ctor(of, _)) => of, + _ => return, + }; - suggest_call_constructor(base.span, kind, fields.len(), err); + suggest_call_constructor(base.span, kind, fields.len(), err); + } + } else { + // The logic here isn't smart but `associated_item_def_ids` + // doesn't work nicely on local. + if let DefKind::Ctor(of, _) = self.tcx.def_kind(def_id) { + let parent_def_id = self.tcx.parent(def_id); + let fields = self.tcx.associated_item_def_ids(parent_def_id); + suggest_call_constructor(base.span, of, fields.len(), err); + } } } @@ -2380,7 +2430,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err } - crate fn get_field_candidates( + pub(crate) fn get_field_candidates( &self, span: Span, base_t: Ty<'tcx>, @@ -2405,7 +2455,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// This method is called after we have encountered a missing field error to recursively /// search for the field - crate fn check_for_nested_field_satisfying( + pub(crate) fn check_for_nested_field_satisfying( &self, span: Span, matches: &impl Fn(&ty::FieldDef, Ty<'tcx>) -> bool, @@ -2591,10 +2641,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_expr_asm_operand(out_expr, false); } } - hir::InlineAsmOperand::Const { anon_const } - | hir::InlineAsmOperand::SymFn { anon_const } => { - self.to_const(anon_const); - } + // `AnonConst`s have their own body and is type-checked separately. + // As they don't flow into the type system we don't need them to + // be well-formed. + hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::SymFn { .. } => {} hir::InlineAsmOperand::SymStatic { .. } => {} } } diff --git a/compiler/rustc_typeck/src/check/fallback.rs b/compiler/rustc_typeck/src/check/fallback.rs index 85132317824..15788f410f1 100644 --- a/compiler/rustc_typeck/src/check/fallback.rs +++ b/compiler/rustc_typeck/src/check/fallback.rs @@ -144,6 +144,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { /// code. The most common case is something like this: /// /// ```rust + /// # fn foo() -> i32 { 4 } /// match foo() { /// 22 => Default::default(), // call this type `?D` /// _ => return, // return has type `!` @@ -168,7 +169,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { /// fallback to use based on whether there is a coercion pattern /// like this: /// - /// ``` + /// ```ignore (not-rust) /// ?Diverging -> ?V /// ?NonDiverging -> ?V /// ?V != ?NonDiverging diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs index 93b0edb84c0..649bc211321 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs @@ -4,6 +4,7 @@ use crate::astconv::{ }; use crate::check::callee::{self, DeferredCallResolution}; use crate::check::method::{self, MethodCallee, SelfSource}; +use crate::check::{region, rvalue_scopes}; use crate::check::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy}; use rustc_data_structures::captures::Captures; @@ -23,8 +24,8 @@ use rustc_middle::ty::subst::{ self, GenericArgKind, InternalSubsts, Subst, SubstsRef, UserSelfTy, UserSubsts, }; use rustc_middle::ty::{ - self, AdtKind, CanonicalUserType, DefIdTree, GenericParamDefKind, ToPolyTraitRef, ToPredicate, - Ty, UserType, + self, AdtKind, CanonicalUserType, DefIdTree, EarlyBinder, GenericParamDefKind, ToPolyTraitRef, + ToPredicate, Ty, UserType, }; use rustc_session::lint; use rustc_span::hygiene::DesugaringKind; @@ -347,7 +348,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { T: TypeFoldable<'tcx>, { debug!("instantiate_type_scheme(value={:?}, substs={:?})", value, substs); - let value = value.subst(self.tcx, substs); + let value = EarlyBinder(value).subst(self.tcx, substs); let result = self.normalize_associated_types_in(span, value); debug!("instantiate_type_scheme = {:?}", result); result @@ -620,6 +621,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.normalize_associated_types_in(span, field.ty(self.tcx, substs)) } + pub(in super::super) fn resolve_rvalue_scopes(&self, def_id: DefId) { + let scope_tree = region::region_scope_tree(self.tcx, def_id); + let rvalue_scopes = { rvalue_scopes::resolve_rvalue_scopes(self, &scope_tree, def_id) }; + let mut typeck_results = self.inh.typeck_results.borrow_mut(); + typeck_results.region_scope_tree = scope_tree; + typeck_results.rvalue_scopes = rvalue_scopes; + } + pub(in super::super) fn resolve_generator_interiors(&self, def_id: DefId) { let mut generators = self.deferred_generator_interiors.borrow_mut(); for (body_id, interior, kind) in generators.drain(..) { @@ -838,9 +847,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let def_kind = self.tcx.def_kind(def_id); let item_ty = if let DefKind::Variant = def_kind { - self.tcx.type_of(self.tcx.parent(def_id)) + self.tcx.bound_type_of(self.tcx.parent(def_id)) } else { - self.tcx.type_of(def_id) + self.tcx.bound_type_of(def_id) }; let substs = self.infcx.fresh_substs_for_item(span, def_id); let ty = item_ty.subst(self.tcx, substs); @@ -1044,8 +1053,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { let (sig, did, substs) = match (&expected.kind(), &found.kind()) { (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => { - let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1); - let sig2 = self.tcx.fn_sig(*did2).subst(self.tcx, substs2); + let sig1 = self.tcx.bound_fn_sig(*did1).subst(self.tcx, substs1); + let sig2 = self.tcx.bound_fn_sig(*did2).subst(self.tcx, substs2); if sig1 != sig2 { return; } @@ -1056,7 +1065,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (sig1, *did1, substs1) } (ty::FnDef(did, substs), ty::FnPtr(sig2)) => { - let sig1 = self.tcx.fn_sig(*did).subst(self.tcx, substs); + let sig1 = self.tcx.bound_fn_sig(*did).subst(self.tcx, substs); if sig1 != *sig2 { return; } @@ -1401,12 +1410,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If we have a default, then we it doesn't matter that we're not // inferring the type arguments: we provide the default where any // is missing. - let default = tcx.type_of(param.def_id); + let default = tcx.bound_type_of(param.def_id); self.fcx - .normalize_ty( - self.span, - default.subst_spanned(tcx, substs.unwrap(), Some(self.span)), - ) + .normalize_ty(self.span, default.subst(tcx, substs.unwrap())) .into() } else { // If no type arguments were provided, we have to infer them. @@ -1418,8 +1424,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } GenericParamDefKind::Const { has_default } => { if !infer_args && has_default { - tcx.const_param_default(param.def_id) - .subst_spanned(tcx, substs.unwrap(), Some(self.span)) + EarlyBinder(tcx.const_param_default(param.def_id)) + .subst(tcx, substs.unwrap()) .into() } else { self.fcx.var_for_def(self.span, param) @@ -1489,7 +1495,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Add all the obligations that are required, substituting and normalized appropriately. - crate fn add_required_obligations(&self, span: Span, def_id: DefId, substs: &SubstsRef<'tcx>) { + pub(crate) fn add_required_obligations( + &self, + span: Span, + def_id: DefId, + substs: &SubstsRef<'tcx>, + ) { self.add_required_obligations_with_code( span, def_id, diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs b/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs index 48a66e8026b..b7ba9d97878 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs @@ -3,6 +3,7 @@ use std::cmp; use rustc_middle::ty::error::TypeError; // An issue that might be found in the compatibility matrix +#[derive(Debug)] enum Issue { /// The given argument is the invalid type for the input Invalid(usize), @@ -23,9 +24,10 @@ pub(crate) enum Compatibility<'tcx> { } /// Similar to `Issue`, but contains some extra information +#[derive(Debug)] pub(crate) enum Error<'tcx> { - /// The given argument is the invalid type for the input - Invalid(usize, Compatibility<'tcx>), + /// The provided argument is the invalid type for the expected input + Invalid(usize, usize, Compatibility<'tcx>), // provided, expected /// There is a missing input Missing(usize), /// There's a superfluous argument @@ -37,8 +39,15 @@ pub(crate) enum Error<'tcx> { } pub(crate) struct ArgMatrix<'tcx> { + /// Maps the indices in the `compatibility_matrix` rows to the indices of + /// the *user provided* inputs input_indexes: Vec<usize>, + /// Maps the indices in the `compatibility_matrix` columns to the indices + /// of the *expected* args arg_indexes: Vec<usize>, + /// The first dimension (rows) are the remaining user provided inputs to + /// match and the second dimension (cols) are the remaining expected args + /// to match compatibility_matrix: Vec<Vec<Compatibility<'tcx>>>, } @@ -52,8 +61,8 @@ impl<'tcx> ArgMatrix<'tcx> { .map(|i| (0..minimum_input_count).map(|j| is_compatible(i, j)).collect()) .collect(); ArgMatrix { - input_indexes: (0..minimum_input_count).collect(), - arg_indexes: (0..provided_arg_count).collect(), + input_indexes: (0..provided_arg_count).collect(), + arg_indexes: (0..minimum_input_count).collect(), compatibility_matrix, } } @@ -61,15 +70,15 @@ impl<'tcx> ArgMatrix<'tcx> { /// Remove a given input from consideration fn eliminate_input(&mut self, idx: usize) { self.input_indexes.remove(idx); - for row in &mut self.compatibility_matrix { - row.remove(idx); - } + self.compatibility_matrix.remove(idx); } /// Remove a given argument from consideration fn eliminate_arg(&mut self, idx: usize) { self.arg_indexes.remove(idx); - self.compatibility_matrix.remove(idx); + for row in &mut self.compatibility_matrix { + row.remove(idx); + } } /// "satisfy" an input with a given arg, removing both from consideration @@ -78,13 +87,15 @@ impl<'tcx> ArgMatrix<'tcx> { self.eliminate_arg(arg_idx); } + // Returns a `Vec` of (user input, expected arg) of matched arguments. These + // are inputs on the remaining diagonal that match. fn eliminate_satisfied(&mut self) -> Vec<(usize, usize)> { let mut i = cmp::min(self.input_indexes.len(), self.arg_indexes.len()); let mut eliminated = vec![]; while i > 0 { let idx = i - 1; if matches!(self.compatibility_matrix[idx][idx], Compatibility::Compatible) { - eliminated.push((self.arg_indexes[idx], self.input_indexes[idx])); + eliminated.push((self.input_indexes[idx], self.arg_indexes[idx])); self.satisfy_input(idx, idx); } i -= 1; @@ -92,7 +103,7 @@ impl<'tcx> ArgMatrix<'tcx> { return eliminated; } - // Check for the above mismatch cases + // Find some issue in the compatibility matrix fn find_issue(&self) -> Option<Issue> { let mat = &self.compatibility_matrix; let ai = &self.arg_indexes; @@ -121,9 +132,9 @@ impl<'tcx> ArgMatrix<'tcx> { if is_arg { for j in 0..ii.len() { // If we find at least one input this argument could satisfy - // this argument isn't completely useless - if matches!(mat[i][j], Compatibility::Compatible) { - useless = false; + // this argument isn't unsatisfiable + if matches!(mat[j][i], Compatibility::Compatible) { + unsatisfiable = false; break; } } @@ -131,16 +142,16 @@ impl<'tcx> ArgMatrix<'tcx> { if is_input { for j in 0..ai.len() { // If we find at least one argument that could satisfy this input - // this argument isn't unsatisfiable - if matches!(mat[j][i], Compatibility::Compatible) { - unsatisfiable = false; + // this argument isn't useless + if matches!(mat[i][j], Compatibility::Compatible) { + useless = false; break; } } } - match (is_arg, is_input, useless, unsatisfiable) { - // If an input is unsatisfied, and the argument in its position is useless + match (is_input, is_arg, useless, unsatisfiable) { + // If an argument is unsatisfied, and the input in its position is useless // then the most likely explanation is that we just got the types wrong (true, true, true, true) => return Some(Issue::Invalid(i)), // Otherwise, if an input is useless, then indicate that this is an extra argument @@ -167,7 +178,7 @@ impl<'tcx> ArgMatrix<'tcx> { _ => { continue; } - }; + } } // We didn't find any of the individual issues above, but @@ -254,11 +265,11 @@ impl<'tcx> ArgMatrix<'tcx> { // We'll want to know which arguments and inputs these rows and columns correspond to // even after we delete them. pub(crate) fn find_errors(mut self) -> (Vec<Error<'tcx>>, Vec<Option<usize>>) { - let provided_arg_count = self.arg_indexes.len(); + let provided_arg_count = self.input_indexes.len(); let mut errors: Vec<Error<'tcx>> = vec![]; // For each expected argument, the matched *actual* input - let mut matched_inputs: Vec<Option<usize>> = vec![None; self.input_indexes.len()]; + let mut matched_inputs: Vec<Option<usize>> = vec![None; self.arg_indexes.len()]; // Before we start looking for issues, eliminate any arguments that are already satisfied, // so that an argument which is already spoken for by the input it's in doesn't @@ -269,28 +280,28 @@ impl<'tcx> ArgMatrix<'tcx> { // Without this elimination, the first argument causes the second argument // to show up as both a missing input and extra argument, rather than // just an invalid type. - for (arg, inp) in self.eliminate_satisfied() { - matched_inputs[inp] = Some(arg); + for (inp, arg) in self.eliminate_satisfied() { + matched_inputs[arg] = Some(inp); } while self.input_indexes.len() > 0 || self.arg_indexes.len() > 0 { - // Check for the first relevant issue match self.find_issue() { Some(Issue::Invalid(idx)) => { let compatibility = self.compatibility_matrix[idx][idx].clone(); let input_idx = self.input_indexes[idx]; + let arg_idx = self.arg_indexes[idx]; self.satisfy_input(idx, idx); - errors.push(Error::Invalid(input_idx, compatibility)); + errors.push(Error::Invalid(input_idx, arg_idx, compatibility)); } Some(Issue::Extra(idx)) => { - let arg_idx = self.arg_indexes[idx]; - self.eliminate_arg(idx); - errors.push(Error::Extra(arg_idx)); - } - Some(Issue::Missing(idx)) => { let input_idx = self.input_indexes[idx]; self.eliminate_input(idx); - errors.push(Error::Missing(input_idx)); + errors.push(Error::Extra(input_idx)); + } + Some(Issue::Missing(idx)) => { + let arg_idx = self.arg_indexes[idx]; + self.eliminate_arg(idx); + errors.push(Error::Missing(arg_idx)); } Some(Issue::Swap(idx, other)) => { let input_idx = self.input_indexes[idx]; @@ -302,24 +313,21 @@ impl<'tcx> ArgMatrix<'tcx> { // Subtract 1 because we already removed the "min" row self.satisfy_input(max - 1, min); errors.push(Error::Swap(input_idx, other_input_idx, arg_idx, other_arg_idx)); - matched_inputs[input_idx] = Some(other_arg_idx); - matched_inputs[other_input_idx] = Some(arg_idx); + matched_inputs[other_arg_idx] = Some(input_idx); + matched_inputs[arg_idx] = Some(other_input_idx); } Some(Issue::Permutation(args)) => { - // FIXME: If satisfy_input ever did anything non-trivial (emit obligations to help type checking, for example) - // we'd want to call this function with the correct arg/input pairs, but for now, we just throw them in a bucket. - // This works because they force a cycle, so each row is guaranteed to also be a column let mut idxs: Vec<usize> = args.iter().filter_map(|&a| a).collect(); let mut real_idxs = vec![None; provided_arg_count]; for (src, dst) in args.iter().enumerate().filter_map(|(src, dst)| dst.map(|dst| (src, dst))) { - let src_arg = self.arg_indexes[src]; - let dst_arg = self.arg_indexes[dst]; - let dest_input = self.input_indexes[dst]; - real_idxs[src_arg] = Some((dst_arg, dest_input)); - matched_inputs[dest_input] = Some(src_arg); + let src_input_idx = self.input_indexes[src]; + let dst_input_idx = self.input_indexes[dst]; + let dest_arg_idx = self.arg_indexes[dst]; + real_idxs[src_input_idx] = Some((dest_arg_idx, dst_input_idx)); + matched_inputs[dest_arg_idx] = Some(src_input_idx); } idxs.sort(); idxs.reverse(); @@ -331,8 +339,8 @@ impl<'tcx> ArgMatrix<'tcx> { None => { // We didn't find any issues, so we need to push the algorithm forward // First, eliminate any arguments that currently satisfy their inputs - for (arg, inp) in self.eliminate_satisfied() { - matched_inputs[inp] = Some(arg); + for (inp, arg) in self.eliminate_satisfied() { + matched_inputs[arg] = Some(inp); } } }; diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index 75976ebdf28..54003654db0 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -12,7 +12,6 @@ use crate::check::{ use crate::structured_errors::StructuredDiagnostic; use rustc_ast as ast; -use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, Diagnostic, DiagnosticId, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; @@ -24,7 +23,7 @@ use rustc_infer::infer::TypeTrace; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::Session; use rustc_span::symbol::Ident; use rustc_span::{self, Span}; @@ -274,9 +273,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // A "softer" version of the helper above, which checks types without persisting them, // and treats error types differently // This will allow us to "probe" for other argument orders that would likely have been correct - let check_compatible = |arg_idx, input_idx| { - let formal_input_ty: Ty<'tcx> = formal_input_tys[input_idx]; - let expected_input_ty: Ty<'tcx> = expected_input_tys[input_idx]; + let check_compatible = |input_idx, arg_idx| { + let formal_input_ty: Ty<'tcx> = formal_input_tys[arg_idx]; + let expected_input_ty: Ty<'tcx> = expected_input_tys[arg_idx]; // If either is an error type, we defy the usual convention and consider them to *not* be // coercible. This prevents our error message heuristic from trying to pass errors into @@ -285,7 +284,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return Compatibility::Incompatible(None); } - let provided_arg: &hir::Expr<'tcx> = &provided_args[arg_idx]; + let provided_arg: &hir::Expr<'tcx> = &provided_args[input_idx]; let expectation = Expectation::rvalue_hint(self, expected_input_ty); // FIXME: check that this is safe; I don't believe this commits any of the obligations, but I can't be sure. // @@ -394,6 +393,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { break 'errors; } + self.set_tainted_by_errors(); + // The algorithm here is inspired by levenshtein distance and longest common subsequence. // We'll try to detect 4 different types of mistakes: // - An extra parameter has been provided that doesn't satisfy *any* of the other inputs @@ -427,11 +428,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let found_errors = !errors.is_empty(); errors.drain_filter(|error| { - let Error::Invalid(input_idx, Compatibility::Incompatible(error)) = error else { return false }; - let expected_ty = expected_input_tys[*input_idx]; - let Some(Some((provided_ty, _))) = final_arg_types.get(*input_idx) else { return false }; + let Error::Invalid(input_idx, arg_idx, Compatibility::Incompatible(error)) = error else { return false }; + let expected_ty = expected_input_tys[*arg_idx]; + let provided_ty = final_arg_types[*input_idx].map(|ty| ty.0).unwrap(); let cause = &self.misc(provided_args[*input_idx].span); - let trace = TypeTrace::types(cause, true, expected_ty, *provided_ty); + let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); if let Some(e) = error { if !matches!(trace.cause.as_failure_code(e), FailureCode::Error0308(_)) { self.report_and_explain_type_error(trace, e).emit(); @@ -502,6 +503,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { TupleMatchFound::Single => { let expected_ty = expected_input_tys[0]; let provided_ty = final_arg_types[0].map(|ty| ty.0).unwrap(); + let expected_ty = self.resolve_vars_if_possible(expected_ty); + let provided_ty = self.resolve_vars_if_possible(provided_ty); let cause = &self.misc(provided_args[0].span); let compatibility = demand_compatible(0, &mut final_arg_types); let type_error = match compatibility { @@ -523,24 +526,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("arguments to this {} are incorrect", call_name), ); // Call out where the function is defined - if let Some(def_id) = fn_def_id && let Some(def_span) = tcx.def_ident_span(def_id) { - let mut spans: MultiSpan = def_span.into(); - - let params = tcx - .hir() - .get_if_local(def_id) - .and_then(|node| node.body_id()) - .into_iter() - .map(|id| tcx.hir().body(id).params) - .flatten(); - - for param in params { - spans.push_span_label(param.span, String::new()); - } - - let def_kind = tcx.def_kind(def_id); - err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id))); - } + label_fn_like(tcx, &mut err, fn_def_id); err.emit(); break 'errors; } @@ -558,24 +544,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { DiagnosticId::Error(err_code.to_owned()), ); // Call out where the function is defined - if let Some(def_id) = fn_def_id && let Some(def_span) = tcx.def_ident_span(def_id) { - let mut spans: MultiSpan = def_span.into(); - - let params = tcx - .hir() - .get_if_local(def_id) - .and_then(|node| node.body_id()) - .into_iter() - .map(|id| tcx.hir().body(id).params) - .flatten(); - - for param in params { - spans.push_span_label(param.span, String::new()); - } - - let def_kind = tcx.def_kind(def_id); - err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id))); - } + label_fn_like(tcx, &mut err, fn_def_id); err.multipart_suggestion( "use parentheses to construct a tuple", vec![(start, '('.to_string()), (end, ')'.to_string())], @@ -592,18 +561,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Next special case: if there is only one "Incompatible" error, just emit that if errors.len() == 1 { - if let Some(Error::Invalid(input_idx, Compatibility::Incompatible(Some(error)))) = - errors.iter().next() + if let Some(Error::Invalid( + input_idx, + arg_idx, + Compatibility::Incompatible(Some(error)), + )) = errors.iter().next() { - let expected_ty = expected_input_tys[*input_idx]; - let provided_ty = final_arg_types[*input_idx].map(|ty| ty.0).unwrap(); + let expected_ty = expected_input_tys[*arg_idx]; + let provided_ty = final_arg_types[*arg_idx].map(|ty| ty.0).unwrap(); + let expected_ty = self.resolve_vars_if_possible(expected_ty); + let provided_ty = self.resolve_vars_if_possible(provided_ty); let cause = &self.misc(provided_args[*input_idx].span); let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); let mut err = self.report_and_explain_type_error(trace, error); self.emit_coerce_suggestions( &mut err, &provided_args[*input_idx], - final_arg_types[*input_idx].map(|ty| ty.0).unwrap(), + provided_ty, final_arg_types[*input_idx].map(|ty| ty.1).unwrap(), None, None, @@ -613,24 +587,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("arguments to this {} are incorrect", call_name), ); // Call out where the function is defined - if let Some(def_id) = fn_def_id && let Some(def_span) = tcx.def_ident_span(def_id) { - let mut spans: MultiSpan = def_span.into(); - - let params = tcx - .hir() - .get_if_local(def_id) - .and_then(|node| node.body_id()) - .into_iter() - .map(|id| tcx.hir().body(id).params) - .flatten(); - - for param in params { - spans.push_span_label(param.span, String::new()); - } - - let def_kind = tcx.def_kind(def_id); - err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id))); - } + label_fn_like(tcx, &mut err, fn_def_id); err.emit(); break 'errors; } @@ -676,17 +633,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut errors = errors.into_iter().peekable(); while let Some(error) = errors.next() { match error { - Error::Invalid(input_idx, compatibility) => { - let expected_ty = expected_input_tys[input_idx]; + Error::Invalid(input_idx, arg_idx, compatibility) => { + let expected_ty = expected_input_tys[arg_idx]; + let provided_ty = final_arg_types[input_idx].map(|ty| ty.0).unwrap(); + let expected_ty = self.resolve_vars_if_possible(expected_ty); + let provided_ty = self.resolve_vars_if_possible(provided_ty); if let Compatibility::Incompatible(error) = &compatibility { - let provided_ty = final_arg_types - .get(input_idx) - .and_then(|x| x.as_ref()) - .map(|ty| ty.0) - .unwrap_or(tcx.ty_error()); - let cause = &self.misc( - provided_args.get(input_idx).map(|i| i.span).unwrap_or(call_span), - ); + let cause = &self.misc(provided_args[input_idx].span); let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); if let Some(e) = error { self.note_type_err( @@ -701,16 +654,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - if let Some(expr) = provided_args.get(input_idx) { - self.emit_coerce_suggestions( - &mut err, - &expr, - final_arg_types[input_idx].map(|ty| ty.0).unwrap(), - final_arg_types[input_idx].map(|ty| ty.1).unwrap(), - None, - None, - ); - } + self.emit_coerce_suggestions( + &mut err, + &provided_args[input_idx], + final_arg_types[input_idx].map(|ty| ty.0).unwrap(), + final_arg_types[input_idx].map(|ty| ty.1).unwrap(), + None, + None, + ); } Error::Extra(arg_idx) => { let arg_type = if let Some((_, ty)) = final_arg_types[arg_idx] { @@ -886,12 +837,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } Error::Swap(input_idx, other_input_idx, arg_idx, other_arg_idx) => { - let first_span = provided_args[arg_idx].span; - let second_span = provided_args[other_arg_idx].span; + let first_span = provided_args[input_idx].span; + let second_span = provided_args[other_input_idx].span; let first_expected_ty = - self.resolve_vars_if_possible(expected_input_tys[input_idx]); - let first_provided_ty = if let Some((ty, _)) = final_arg_types[arg_idx] { + self.resolve_vars_if_possible(expected_input_tys[arg_idx]); + let first_provided_ty = if let Some((ty, _)) = final_arg_types[input_idx] { format!(",found `{}`", ty) } else { "".into() @@ -901,9 +852,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("expected `{}`{}", first_expected_ty, first_provided_ty), )); let other_expected_ty = - self.resolve_vars_if_possible(expected_input_tys[other_input_idx]); + self.resolve_vars_if_possible(expected_input_tys[other_arg_idx]); let other_provided_ty = - if let Some((ty, _)) = final_arg_types[other_arg_idx] { + if let Some((ty, _)) = final_arg_types[other_input_idx] { format!(",found `{}`", ty) } else { "".into() @@ -948,24 +899,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // Call out where the function is defined - if let Some(def_id) = fn_def_id && let Some(def_span) = tcx.def_ident_span(def_id) { - let mut spans: MultiSpan = def_span.into(); - - let params = tcx - .hir() - .get_if_local(def_id) - .and_then(|node| node.body_id()) - .into_iter() - .flat_map(|id| tcx.hir().body(id).params) - ; - - for param in params { - spans.push_span_label(param.span, String::new()); - } - - let def_kind = tcx.def_kind(def_id); - err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id))); - } + label_fn_like(tcx, &mut err, fn_def_id); // And add a suggestion block for all of the parameters let suggestion_text = match suggestion_text { @@ -986,14 +920,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "{}(", source_map.span_to_snippet(full_call_span).unwrap_or_else(|_| String::new()) ); - for (idx, arg) in matched_inputs.iter().enumerate() { - let suggestion_text = if let Some(arg) = arg { - let arg_span = provided_args[*arg].span.source_callsite(); + for (arg_index, input_idx) in matched_inputs.iter().enumerate() { + let suggestion_text = if let Some(input_idx) = input_idx { + let arg_span = provided_args[*input_idx].span.source_callsite(); let arg_text = source_map.span_to_snippet(arg_span).unwrap(); arg_text } else { // Propose a placeholder of the correct type - let expected_ty = expected_input_tys[idx]; + let expected_ty = expected_input_tys[arg_index]; let input_ty = self.resolve_vars_if_possible(expected_ty); if input_ty.is_unit() { "()".to_string() @@ -1002,7 +936,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; suggestion += &suggestion_text; - if idx < minimum_input_count - 1 { + if arg_index < minimum_input_count - 1 { suggestion += ", "; } } @@ -1475,7 +1409,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// A common error is to add an extra semicolon: /// - /// ``` + /// ```compile_fail,E0308 /// fn foo() -> usize { /// 22; /// } @@ -1661,24 +1595,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Peel derived obligation, because it's the type that originally // started this inference chain that matters, not the one we wound // up with at the end. - fn unpeel_to_top( - mut code: Lrc<ObligationCauseCode<'_>>, - ) -> Lrc<ObligationCauseCode<'_>> { - let mut result_code = code.clone(); + fn unpeel_to_top<'a, 'tcx>( + mut code: &'a ObligationCauseCode<'tcx>, + ) -> &'a ObligationCauseCode<'tcx> { + let mut result_code = code; loop { - let parent = match &*code { - ObligationCauseCode::ImplDerivedObligation(c) => { - c.derived.parent_code.clone() - } + let parent = match code { + ObligationCauseCode::ImplDerivedObligation(c) => &c.derived.parent_code, ObligationCauseCode::BuiltinDerivedObligation(c) - | ObligationCauseCode::DerivedObligation(c) => c.parent_code.clone(), - _ => break, + | ObligationCauseCode::DerivedObligation(c) => &c.parent_code, + _ => break result_code, }; - result_code = std::mem::replace(&mut code, parent); + (result_code, code) = (code, parent); } - result_code } - let self_: ty::subst::GenericArg<'_> = match &*unpeel_to_top(error.obligation.cause.clone_code()) { + let self_: ty::subst::GenericArg<'_> = match unpeel_to_top(error.obligation.cause.code()) { ObligationCauseCode::BuiltinDerivedObligation(code) | ObligationCauseCode::DerivedObligation(code) => { code.parent_trait_pred.self_ty().skip_binder().into() @@ -1728,13 +1659,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We make sure that only *one* argument matches the obligation failure // and we assign the obligation's span to its expression's. error.obligation.cause.span = args[ref_in].span; - let parent_code = error.obligation.cause.clone_code(); - *error.obligation.cause.make_mut_code() = + error.obligation.cause.map_code(|parent_code| { ObligationCauseCode::FunctionArgumentObligation { arg_hir_id: args[ref_in].hir_id, call_hir_id: expr.hir_id, parent_code, - }; + } + }); } else if error.obligation.cause.span == call_sp { // Make function calls point at the callee, not the whole thing. if let hir::ExprKind::Call(callee, _) = expr.kind { @@ -1790,3 +1721,47 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } + +fn label_fn_like<'tcx>( + tcx: TyCtxt<'tcx>, + err: &mut rustc_errors::DiagnosticBuilder<'tcx, rustc_errors::ErrorGuaranteed>, + def_id: Option<DefId>, +) { + let Some(def_id) = def_id else { + return; + }; + + if let Some(def_span) = tcx.def_ident_span(def_id) { + let mut spans: MultiSpan = def_span.into(); + + let params = tcx + .hir() + .get_if_local(def_id) + .and_then(|node| node.body_id()) + .into_iter() + .map(|id| tcx.hir().body(id).params) + .flatten(); + + for param in params { + spans.push_span_label(param.span, String::new()); + } + + let def_kind = tcx.def_kind(def_id); + err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id))); + } else { + match tcx.hir().get_if_local(def_id) { + Some(hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Closure(_, _, _, span, ..), + .. + })) => { + let spans: MultiSpan = (*span).into(); + + // Note: We don't point to param spans here because they overlap + // with the closure span itself + + err.span_note(spans, "closure defined here"); + } + _ => {} + } + } +} diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs index 681d1e37f86..bd58a675448 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs @@ -72,7 +72,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// When encountering an fn-like ctor that needs to unify with a value, check whether calling /// the ctor would successfully solve the type mismatch and if so, suggest it: - /// ``` + /// ```compile_fail,E0308 /// fn foo(x: usize) -> usize { x } /// let x: usize = foo; // suggest calling the `foo` function: `foo(42)` /// ``` @@ -151,7 +151,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } Some(Node::Ctor(hir::VariantData::Tuple(fields, _))) => { sugg_call = fields.iter().map(|_| "_").collect::<Vec<_>>().join(", "); - match def_id.as_local().map(|def_id| hir.def_kind(def_id)) { + match def_id.as_local().map(|def_id| self.tcx.def_kind(def_id)) { Some(DefKind::Ctor(hir::def::CtorOf::Variant, _)) => { msg = "instantiate this tuple variant"; } @@ -463,7 +463,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// A common error is to forget to add a semicolon at the end of a block, e.g., /// - /// ``` + /// ```compile_fail,E0308 + /// # fn bar_that_returns_u32() -> u32 { 4 } /// fn foo() { /// bar_that_returns_u32() /// } @@ -504,7 +505,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// A possible error is to forget to add a return type that is needed: /// - /// ``` + /// ```compile_fail,E0308 + /// # fn bar_that_returns_u32() -> u32 { 4 } /// fn foo() { /// bar_that_returns_u32() /// } @@ -569,7 +571,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// check whether the return type is a generic type with a trait bound /// only suggest this if the generic param is not present in the arguments /// if this is true, hint them towards changing the return type to `impl Trait` - /// ``` + /// ```compile_fail,E0308 /// fn cant_name_it<T: Fn() -> u32>() -> T { /// || 3 /// } diff --git a/compiler/rustc_typeck/src/check/generator_interior.rs b/compiler/rustc_typeck/src/check/generator_interior.rs index 15edc11a497..02167ddef44 100644 --- a/compiler/rustc_typeck/src/check/generator_interior.rs +++ b/compiler/rustc_typeck/src/check/generator_interior.rs @@ -14,28 +14,21 @@ use rustc_hir::hir_id::HirIdSet; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind}; use rustc_middle::middle::region::{self, Scope, ScopeData, YieldData}; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, RvalueScopes, Ty, TyCtxt}; use rustc_span::symbol::sym; use rustc_span::Span; -use smallvec::SmallVec; use tracing::debug; mod drop_ranges; struct InteriorVisitor<'a, 'tcx> { fcx: &'a FnCtxt<'a, 'tcx>, + region_scope_tree: &'a region::ScopeTree, types: FxIndexSet<ty::GeneratorInteriorTypeCause<'tcx>>, - region_scope_tree: &'tcx region::ScopeTree, + rvalue_scopes: &'a RvalueScopes, expr_count: usize, kind: hir::GeneratorKind, prev_unresolved_span: Option<Span>, - /// Match arm guards have temporary borrows from the pattern bindings. - /// In case there is a yield point in a guard with a reference to such bindings, - /// such borrows can span across this yield point. - /// As such, we need to track these borrows and record them despite of the fact - /// that they may succeed the said yield point in the post-order. - guard_bindings: SmallVec<[SmallVec<[HirId; 4]>; 1]>, - guard_bindings_set: HirIdSet, linted_values: HirIdSet, drop_ranges: DropRanges, } @@ -48,7 +41,6 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> { scope: Option<region::Scope>, expr: Option<&'tcx Expr<'tcx>>, source_span: Span, - guard_borrowing_from_pattern: bool, ) { use rustc_span::DUMMY_SP; @@ -89,8 +81,7 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> { // If it is a borrowing happening in the guard, // it needs to be recorded regardless because they // do live across this yield point. - guard_borrowing_from_pattern - || yield_data.expr_and_pat_count >= self.expr_count + yield_data.expr_and_pat_count >= self.expr_count }) .cloned() }) @@ -189,22 +180,22 @@ pub fn resolve_interior<'a, 'tcx>( kind: hir::GeneratorKind, ) { let body = fcx.tcx.hir().body(body_id); + let typeck_results = fcx.inh.typeck_results.borrow(); let mut visitor = InteriorVisitor { fcx, types: FxIndexSet::default(), - region_scope_tree: fcx.tcx.region_scope_tree(def_id), + region_scope_tree: &typeck_results.region_scope_tree, + rvalue_scopes: &typeck_results.rvalue_scopes, expr_count: 0, kind, prev_unresolved_span: None, - guard_bindings: <_>::default(), - guard_bindings_set: <_>::default(), linted_values: <_>::default(), drop_ranges: drop_ranges::compute_drop_ranges(fcx, def_id, body), }; intravisit::walk_body(&mut visitor, body); // Check that we visited the same amount of expressions as the RegionResolutionVisitor - let region_expr_count = visitor.region_scope_tree.body_expr_count(body_id).unwrap(); + let region_expr_count = typeck_results.region_scope_tree.body_expr_count(body_id).unwrap(); assert_eq!(region_expr_count, visitor.expr_count); // The types are already kept in insertion order. @@ -260,8 +251,9 @@ pub fn resolve_interior<'a, 'tcx>( let witness = fcx.tcx.mk_generator_witness(ty::Binder::bind_with_vars(type_list, bound_vars.clone())); + drop(typeck_results); // Store the generator types and spans into the typeck results for this generator. - visitor.fcx.inh.typeck_results.borrow_mut().generator_interior_types = + fcx.inh.typeck_results.borrow_mut().generator_interior_types = ty::Binder::bind_with_vars(type_causes, bound_vars); debug!( @@ -284,31 +276,56 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { let Arm { guard, pat, body, .. } = arm; self.visit_pat(pat); if let Some(ref g) = guard { - self.guard_bindings.push(<_>::default()); - ArmPatCollector { - guard_bindings_set: &mut self.guard_bindings_set, - guard_bindings: self - .guard_bindings - .last_mut() - .expect("should have pushed at least one earlier"), + { + // If there is a guard, we need to count all variables bound in the pattern as + // borrowed for the entire guard body, regardless of whether they are accessed. + // We do this by walking the pattern bindings and recording `&T` for any `x: T` + // that is bound. + + struct ArmPatCollector<'a, 'b, 'tcx> { + interior_visitor: &'a mut InteriorVisitor<'b, 'tcx>, + scope: Scope, + } + + impl<'a, 'b, 'tcx> Visitor<'tcx> for ArmPatCollector<'a, 'b, 'tcx> { + fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) { + intravisit::walk_pat(self, pat); + if let PatKind::Binding(_, id, ident, ..) = pat.kind { + let ty = + self.interior_visitor.fcx.typeck_results.borrow().node_type(id); + let tcx = self.interior_visitor.fcx.tcx; + let ty = tcx.mk_ref( + // Use `ReErased` as `resolve_interior` is going to replace all the + // regions anyway. + tcx.mk_region(ty::ReErased), + ty::TypeAndMut { ty, mutbl: hir::Mutability::Not }, + ); + self.interior_visitor.record( + ty, + id, + Some(self.scope), + None, + ident.span, + ); + } + } + } + + ArmPatCollector { + interior_visitor: self, + scope: Scope { id: g.body().hir_id.local_id, data: ScopeData::Node }, + } + .visit_pat(pat); } - .visit_pat(pat); match g { Guard::If(ref e) => { self.visit_expr(e); } - Guard::IfLet(ref pat, ref e) => { - self.visit_pat(pat); - self.visit_expr(e); + Guard::IfLet(ref l) => { + self.visit_let_expr(l); } } - - let mut scope_var_ids = - self.guard_bindings.pop().expect("should have pushed at least one earlier"); - for var_id in scope_var_ids.drain(..) { - self.guard_bindings_set.remove(&var_id); - } } self.visit_expr(body); } @@ -321,13 +338,11 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { if let PatKind::Binding(..) = pat.kind { let scope = self.region_scope_tree.var_scope(pat.hir_id.local_id).unwrap(); let ty = self.fcx.typeck_results.borrow().pat_ty(pat); - self.record(ty, pat.hir_id, Some(scope), None, pat.span, false); + self.record(ty, pat.hir_id, Some(scope), None, pat.span); } } fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - let mut guard_borrowing_from_pattern = false; - match &expr.kind { ExprKind::Call(callee, args) => match &callee.kind { ExprKind::Path(qpath) => { @@ -354,16 +369,6 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { } _ => intravisit::walk_expr(self, expr), }, - ExprKind::Path(qpath) => { - intravisit::walk_expr(self, expr); - let res = self.fcx.typeck_results.borrow().qpath_res(qpath, expr.hir_id); - match res { - Res::Local(id) if self.guard_bindings_set.contains(&id) => { - guard_borrowing_from_pattern = true; - } - _ => {} - } - } _ => intravisit::walk_expr(self, expr), } @@ -380,26 +385,21 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { // temporary on the stack that is live for the current temporary scope and then return a // reference to it. That value may be live across the entire temporary scope. let scope = if self.drop_ranges.is_borrowed_temporary(expr) { - self.region_scope_tree.temporary_scope(expr.hir_id.local_id) + self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id) } else { debug!("parent_node: {:?}", self.fcx.tcx.hir().find_parent_node(expr.hir_id)); match self.fcx.tcx.hir().find_parent_node(expr.hir_id) { Some(parent) => Some(Scope { id: parent.local_id, data: ScopeData::Node }), - None => self.region_scope_tree.temporary_scope(expr.hir_id.local_id), + None => { + self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id) + } } }; // If there are adjustments, then record the final type -- // this is the actual value that is being produced. if let Some(adjusted_ty) = self.fcx.typeck_results.borrow().expr_ty_adjusted_opt(expr) { - self.record( - adjusted_ty, - expr.hir_id, - scope, - Some(expr), - expr.span, - guard_borrowing_from_pattern, - ); + self.record(adjusted_ty, expr.hir_id, scope, Some(expr), expr.span); } // Also record the unadjusted type (which is the only type if @@ -427,54 +427,13 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { // The type table might not have information for this expression // if it is in a malformed scope. (#66387) if let Some(ty) = self.fcx.typeck_results.borrow().expr_ty_opt(expr) { - if guard_borrowing_from_pattern { - // Match guards create references to all the bindings in the pattern that are used - // in the guard, e.g. `y if is_even(y) => ...` becomes `is_even(*r_y)` where `r_y` - // is a reference to `y`, so we must record a reference to the type of the binding. - let tcx = self.fcx.tcx; - let ref_ty = tcx.mk_ref( - // Use `ReErased` as `resolve_interior` is going to replace all the regions anyway. - tcx.mk_region(ty::ReErased), - ty::TypeAndMut { ty, mutbl: hir::Mutability::Not }, - ); - self.record( - ref_ty, - expr.hir_id, - scope, - Some(expr), - expr.span, - guard_borrowing_from_pattern, - ); - } - self.record( - ty, - expr.hir_id, - scope, - Some(expr), - expr.span, - guard_borrowing_from_pattern, - ); + self.record(ty, expr.hir_id, scope, Some(expr), expr.span); } else { self.fcx.tcx.sess.delay_span_bug(expr.span, "no type for node"); } } } -struct ArmPatCollector<'a> { - guard_bindings_set: &'a mut HirIdSet, - guard_bindings: &'a mut SmallVec<[HirId; 4]>, -} - -impl<'a, 'tcx> Visitor<'tcx> for ArmPatCollector<'a> { - fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) { - intravisit::walk_pat(self, pat); - if let PatKind::Binding(_, id, ..) = pat.kind { - self.guard_bindings.push(id); - self.guard_bindings_set.insert(id); - } - } -} - #[derive(Default)] pub struct SuspendCheckData<'a, 'tcx> { expr: Option<&'tcx Expr<'tcx>>, @@ -609,44 +568,43 @@ fn check_must_not_suspend_def( hir_id: HirId, data: SuspendCheckData<'_, '_>, ) -> bool { - for attr in tcx.get_attrs(def_id).iter() { - if attr.has_name(sym::must_not_suspend) { - tcx.struct_span_lint_hir( - rustc_session::lint::builtin::MUST_NOT_SUSPEND, - hir_id, - data.source_span, - |lint| { - let msg = format!( - "{}`{}`{} held across a suspend point, but should not be", - data.descr_pre, - tcx.def_path_str(def_id), - data.descr_post, - ); - let mut err = lint.build(&msg); + if let Some(attr) = tcx.get_attr(def_id, sym::must_not_suspend) { + tcx.struct_span_lint_hir( + rustc_session::lint::builtin::MUST_NOT_SUSPEND, + hir_id, + data.source_span, + |lint| { + let msg = format!( + "{}`{}`{} held across a suspend point, but should not be", + data.descr_pre, + tcx.def_path_str(def_id), + data.descr_post, + ); + let mut err = lint.build(&msg); - // add span pointing to the offending yield/await - err.span_label(data.yield_span, "the value is held across this suspend point"); + // add span pointing to the offending yield/await + err.span_label(data.yield_span, "the value is held across this suspend point"); - // Add optional reason note - if let Some(note) = attr.value_str() { - // FIXME(guswynn): consider formatting this better - err.span_note(data.source_span, note.as_str()); - } + // Add optional reason note + if let Some(note) = attr.value_str() { + // FIXME(guswynn): consider formatting this better + err.span_note(data.source_span, note.as_str()); + } - // Add some quick suggestions on what to do - // FIXME: can `drop` work as a suggestion here as well? - err.span_help( - data.source_span, - "consider using a block (`{ ... }`) \ - to shrink the value's scope, ending before the suspend point", - ); + // Add some quick suggestions on what to do + // FIXME: can `drop` work as a suggestion here as well? + err.span_help( + data.source_span, + "consider using a block (`{ ... }`) \ + to shrink the value's scope, ending before the suspend point", + ); - err.emit(); - }, - ); + err.emit(); + }, + ); - return true; - } + true + } else { + false } - false } diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs index 800232d9549..ba6fcb98924 100644 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs +++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs @@ -41,11 +41,12 @@ pub fn compute_drop_ranges<'a, 'tcx>( if fcx.sess().opts.debugging_opts.drop_tracking { let consumed_borrowed_places = find_consumed_and_borrowed(fcx, def_id, body); - let num_exprs = fcx.tcx.region_scope_tree(def_id).body_expr_count(body.id()).unwrap_or(0); + let typeck_results = &fcx.typeck_results.borrow(); + let num_exprs = typeck_results.region_scope_tree.body_expr_count(body.id()).unwrap_or(0); let (mut drop_ranges, borrowed_temporaries) = build_control_flow_graph( fcx.tcx.hir(), fcx.tcx, - &fcx.typeck_results.borrow(), + typeck_results, consumed_borrowed_places, body, num_exprs, diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs index 721f251650f..417778cc57d 100644 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs +++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs @@ -71,7 +71,7 @@ pub(super) fn build_control_flow_graph<'tcx>( /// ``` /// /// Rule 3: -/// ```rust +/// ```compile_fail,E0382 /// let mut a = (vec![0], vec![0]); /// drop(a); /// a.1 = vec![1]; @@ -344,9 +344,8 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> { // B -> C and E -> F are added implicitly due to the traversal order. match guard { Some(Guard::If(expr)) => self.visit_expr(expr), - Some(Guard::IfLet(pat, expr)) => { - self.visit_pat(pat); - self.visit_expr(expr); + Some(Guard::IfLet(let_expr)) => { + self.visit_let_expr(let_expr); } None => (), } diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs index 928daba0a7b..b22b791f629 100644 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs +++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs @@ -77,38 +77,8 @@ impl<'tcx> ExprUseDelegate<'tcx> { } self.places.consumed.get_mut(&consumer).map(|places| places.insert(target)); } -} - -impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> { - fn consume( - &mut self, - place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>, - diag_expr_id: HirId, - ) { - let parent = match self.tcx.hir().find_parent_node(place_with_id.hir_id) { - Some(parent) => parent, - None => place_with_id.hir_id, - }; - debug!( - "consume {:?}; diag_expr_id={:?}, using parent {:?}", - place_with_id, diag_expr_id, parent - ); - place_with_id - .try_into() - .map_or((), |tracked_value| self.mark_consumed(parent, tracked_value)); - } - - fn borrow( - &mut self, - place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>, - diag_expr_id: HirId, - bk: rustc_middle::ty::BorrowKind, - ) { - debug!( - "borrow: place_with_id = {place_with_id:?}, diag_expr_id={diag_expr_id:?}, \ - borrow_kind={bk:?}" - ); + fn borrow_place(&mut self, place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>) { self.places .borrowed .insert(TrackedValue::from_place_with_projections_allowed(place_with_id)); @@ -158,6 +128,40 @@ impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> { self.places.borrowed_temporaries.insert(place_with_id.hir_id); } } +} + +impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> { + fn consume( + &mut self, + place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>, + diag_expr_id: HirId, + ) { + let parent = match self.tcx.hir().find_parent_node(place_with_id.hir_id) { + Some(parent) => parent, + None => place_with_id.hir_id, + }; + debug!( + "consume {:?}; diag_expr_id={:?}, using parent {:?}", + place_with_id, diag_expr_id, parent + ); + place_with_id + .try_into() + .map_or((), |tracked_value| self.mark_consumed(parent, tracked_value)); + } + + fn borrow( + &mut self, + place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>, + diag_expr_id: HirId, + bk: rustc_middle::ty::BorrowKind, + ) { + debug!( + "borrow: place_with_id = {place_with_id:?}, diag_expr_id={diag_expr_id:?}, \ + borrow_kind={bk:?}" + ); + + self.borrow_place(place_with_id); + } fn copy( &mut self, @@ -180,6 +184,15 @@ impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> { diag_expr_id: HirId, ) { debug!("mutate {assignee_place:?}; diag_expr_id={diag_expr_id:?}"); + + if assignee_place.place.base == PlaceBase::Rvalue + && assignee_place.place.projections.is_empty() + { + // Assigning to an Rvalue is illegal unless done through a dereference. We would have + // already gotten a type error, so we will just return here. + return; + } + // If the type being assigned needs dropped, then the mutation counts as a borrow // since it is essentially doing `Drop::drop(&mut x); x = new_value;`. if assignee_place.place.base_ty.needs_drop(self.tcx, self.param_env) { @@ -199,9 +212,18 @@ impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> { fn fake_read( &mut self, - _place: expr_use_visitor::Place<'tcx>, - _cause: rustc_middle::mir::FakeReadCause, - _diag_expr_id: HirId, + place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>, + cause: rustc_middle::mir::FakeReadCause, + diag_expr_id: HirId, ) { + debug!( + "fake_read place_with_id={place_with_id:?}; cause={cause:?}; diag_expr_id={diag_expr_id:?}" + ); + + // fake reads happen in places like the scrutinee of a match expression. + // we treat those as a borrow, much like a copy: the idea is that we are + // transiently creating a `&T` ref that we can read from to observe the current + // value (this `&T` is immediately dropped afterwards). + self.borrow_place(place_with_id); } } diff --git a/compiler/rustc_typeck/src/check/inherited.rs b/compiler/rustc_typeck/src/check/inherited.rs index 62ca728868b..5cd63cae8ad 100644 --- a/compiler/rustc_typeck/src/check/inherited.rs +++ b/compiler/rustc_typeck/src/check/inherited.rs @@ -17,11 +17,11 @@ use std::cell::RefCell; use std::ops::Deref; /// Closures defined within the function. For example: -/// -/// fn foo() { -/// bar(move|| { ... }) -/// } -/// +/// ```ignore (illustrative) +/// fn foo() { +/// bar(move|| { ... }) +/// } +/// ``` /// Here, the function `foo()` and the closure passed to /// `bar()` will each have their own `FnCtxt`, but they will /// share the inherited fields. diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs index 0dd8ee88ca2..7fe710cf8f4 100644 --- a/compiler/rustc_typeck/src/check/intrinsic.rs +++ b/compiler/rustc_typeck/src/check/intrinsic.rs @@ -129,7 +129,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { ty::INNERMOST, ty::BoundRegion { var: ty::BoundVar::from_u32(1), kind: ty::BrEnv }, )); - let va_list_ty = tcx.type_of(did).subst(tcx, &[region.into()]); + let va_list_ty = tcx.bound_type_of(did).subst(tcx, &[region.into()]); (tcx.mk_ref(env_region, ty::TypeAndMut { ty: va_list_ty, mutbl }), va_list_ty) }) }; @@ -305,6 +305,9 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { sym::ptr_offset_from => { (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.isize) } + sym::ptr_offset_from_unsigned => { + (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.usize) + } sym::unchecked_div | sym::unchecked_rem | sym::exact_div => { (1, vec![param(0), param(0)], param(0)) } diff --git a/compiler/rustc_typeck/src/check/method/confirm.rs b/compiler/rustc_typeck/src/check/method/confirm.rs index bc0fa916556..7992460f546 100644 --- a/compiler/rustc_typeck/src/check/method/confirm.rs +++ b/compiler/rustc_typeck/src/check/method/confirm.rs @@ -460,21 +460,15 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { debug!("method_predicates after subst = {:?}", method_predicates); - let sig = self.tcx.fn_sig(def_id); + let sig = self.tcx.bound_fn_sig(def_id); - // Instantiate late-bound regions and substitute the trait - // parameters into the method type to get the actual method type. - // - // N.B., instantiate late-bound regions first so that - // `instantiate_type_scheme` can normalize associated types that - // may reference those regions. - let method_sig = self.replace_bound_vars_with_fresh_vars(sig); - debug!("late-bound lifetimes from method instantiated, method_sig={:?}", method_sig); + let sig = sig.subst(self.tcx, all_substs); + debug!("type scheme substituted, sig={:?}", sig); - let method_sig = method_sig.subst(self.tcx, all_substs); - debug!("type scheme substituted, method_sig={:?}", method_sig); + let sig = self.replace_bound_vars_with_fresh_vars(sig); + debug!("late-bound lifetimes from method instantiated, sig={:?}", sig); - (method_sig, method_predicates) + (sig, method_predicates) } fn add_obligations( diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index 8137d702921..d4c5caa6e92 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -116,7 +116,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Adds a suggestion to call the given method to the provided diagnostic. #[instrument(level = "debug", skip(self, err, call_expr))] - crate fn suggest_method_call( + pub(crate) fn suggest_method_call( &self, err: &mut Diagnostic, msg: &str, @@ -460,9 +460,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // N.B., instantiate late-bound regions first so that // `instantiate_type_scheme` can normalize associated types that // may reference those regions. - let fn_sig = tcx.fn_sig(def_id); - let fn_sig = self.replace_bound_vars_with_fresh_vars(span, infer::FnCall, fn_sig).0; + let fn_sig = tcx.bound_fn_sig(def_id); let fn_sig = fn_sig.subst(self.tcx, substs); + let fn_sig = self.replace_bound_vars_with_fresh_vars(span, infer::FnCall, fn_sig).0; let InferOk { value, obligations: o } = if is_op { self.normalize_op_associated_types_in_as_infer_ok(span, fn_sig, opt_input_expr) diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs index e04cc42b6d7..0edf8fac9d6 100644 --- a/compiler/rustc_typeck/src/check/method/probe.rs +++ b/compiler/rustc_typeck/src/check/method/probe.rs @@ -21,12 +21,13 @@ use rustc_middle::middle::stability; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; use rustc_middle::ty::GenericParamDefKind; -use rustc_middle::ty::{self, ParamEnvAnd, ToPredicate, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, EarlyBinder, ParamEnvAnd, ToPredicate, Ty, TyCtxt, TypeFoldable}; use rustc_session::lint; use rustc_span::def_id::LocalDefId; use rustc_span::lev_distance::{ find_best_match_for_name_with_substrings, lev_distance_with_substrings, }; +use rustc_span::symbol::sym; use rustc_span::{symbol::Ident, Span, Symbol, DUMMY_SP}; use rustc_trait_selection::autoderef::{self, Autoderef}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; @@ -200,8 +201,9 @@ pub struct Pick<'tcx> { pub import_ids: SmallVec<[LocalDefId; 1]>, /// Indicates that the source expression should be autoderef'd N times - /// - /// A = expr | *expr | **expr | ... + /// ```ignore (not-rust) + /// A = expr | *expr | **expr | ... + /// ``` pub autoderefs: usize, /// Indicates that we want to add an autoref (and maybe also unsize it), or if the receiver is @@ -642,16 +644,22 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.assemble_inherent_candidates_from_object(generalized_self_ty); self.assemble_inherent_impl_candidates_for_type(p.def_id()); + if self.tcx.has_attr(p.def_id(), sym::rustc_has_incoherent_inherent_impls) { + self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty); + } } ty::Adt(def, _) => { let def_id = def.did(); self.assemble_inherent_impl_candidates_for_type(def_id); - if Some(def_id) == self.tcx.lang_items().c_str() { + if self.tcx.has_attr(def_id, sym::rustc_has_incoherent_inherent_impls) { self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty); } } ty::Foreign(did) => { self.assemble_inherent_impl_candidates_for_type(did); + if self.tcx.has_attr(did, sym::rustc_has_incoherent_inherent_impls) { + self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty); + } } ty::Param(p) => { self.assemble_inherent_candidates_from_param(p); @@ -673,7 +681,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } fn assemble_inherent_candidates_for_incoherent_ty(&mut self, self_ty: Ty<'tcx>) { - let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::AsPlaceholders) else { + let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::AsInfer) else { bug!("unexpected incoherent type: {:?}", self_ty) }; for &impl_def_id in self.tcx.incoherent_impls(simp) { @@ -703,7 +711,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } let (impl_ty, impl_substs) = self.impl_ty_and_substs(impl_def_id); - let impl_ty = impl_ty.subst(self.tcx, impl_substs); + let impl_ty = EarlyBinder(impl_ty).subst(self.tcx, impl_substs); debug!("impl_ty: {:?}", impl_ty); @@ -893,7 +901,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { ) -> bool { match method.kind { ty::AssocKind::Fn => { - let fty = self.tcx.fn_sig(method.def_id); + let fty = self.tcx.bound_fn_sig(method.def_id); self.probe(|_| { let substs = self.fresh_substs_for_item(self.span, method.def_id); let fty = fty.subst(self.tcx, substs); @@ -1631,7 +1639,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { /// /// Example (`src/test/ui/method-two-trait-defer-resolution-1.rs`): /// - /// ``` + /// ```ignore (illustrative) /// trait Foo { ... } /// impl Foo for Vec<i32> { ... } /// impl Foo for Vec<usize> { ... } @@ -1763,7 +1771,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { #[instrument(level = "debug", skip(self))] fn xform_method_sig(&self, method: DefId, substs: SubstsRef<'tcx>) -> ty::FnSig<'tcx> { - let fn_sig = self.tcx.fn_sig(method); + let fn_sig = self.tcx.bound_fn_sig(method); debug!(?fn_sig); assert!(!substs.has_escaping_bound_vars()); @@ -1776,12 +1784,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let generics = self.tcx.generics_of(method); assert_eq!(substs.len(), generics.parent_count as usize); - // Erase any late-bound regions from the method and substitute - // in the values from the substitution. - let xform_fn_sig = self.erase_late_bound_regions(fn_sig); - - if generics.params.is_empty() { - xform_fn_sig.subst(self.tcx, substs) + let xform_fn_sig = if generics.params.is_empty() { + fn_sig.subst(self.tcx, substs) } else { let substs = InternalSubsts::for_item(self.tcx, method, |param, _| { let i = param.index as usize; @@ -1799,8 +1803,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } }); - xform_fn_sig.subst(self.tcx, substs) - } + fn_sig.subst(self.tcx, substs) + }; + + self.erase_late_bound_regions(xform_fn_sig) } /// Gets the type of an impl and generate substitutions with placeholders. diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index 634ba2baf96..f9c0ea82e02 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -368,16 +368,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if self.is_fn_ty(rcvr_ty, span) { if let SelfSource::MethodCall(expr) = source { let suggest = if let ty::FnDef(def_id, _) = rcvr_ty.kind() { - let local_id = def_id.expect_local(); - let hir_id = tcx.hir().local_def_id_to_hir_id(local_id); - let node = tcx.hir().get(hir_id); - let fields = node.tuple_fields(); - - if let Some(fields) = fields - && let Some(DefKind::Ctor(of, _)) = self.tcx.opt_def_kind(local_id) { - Some((fields, of)) + if let Some(local_id) = def_id.as_local() { + let hir_id = tcx.hir().local_def_id_to_hir_id(local_id); + let node = tcx.hir().get(hir_id); + let fields = node.tuple_fields(); + if let Some(fields) = fields + && let Some(DefKind::Ctor(of, _)) = self.tcx.opt_def_kind(local_id) { + Some((fields.len(), of)) + } else { + None + } } else { - None + // The logic here isn't smart but `associated_item_def_ids` + // doesn't work nicely on local. + if let DefKind::Ctor(of, _) = tcx.def_kind(def_id) { + let parent_def_id = tcx.parent(*def_id); + Some((tcx.associated_item_def_ids(parent_def_id).len(), of)) + } else { + None + } } } else { None @@ -385,7 +394,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the function is a tuple constructor, we recommend that they call it if let Some((fields, kind)) = suggest { - suggest_call_constructor(expr.span, kind, fields.len(), &mut err); + suggest_call_constructor(expr.span, kind, fields, &mut err); } else { // General case err.span_label( @@ -1227,7 +1236,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .into_iter() .any(|info| self.associated_value(info.def_id, item_name).is_some()); let found_assoc = |ty: Ty<'tcx>| { - simplify_type(tcx, ty, TreatParams::AsPlaceholders) + simplify_type(tcx, ty, TreatParams::AsInfer) .and_then(|simp| { tcx.incoherent_impls(simp) .iter() @@ -1334,7 +1343,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } - crate fn note_unmet_impls_on_type( + pub(crate) fn note_unmet_impls_on_type( &self, err: &mut Diagnostic, errors: Vec<FulfillmentError<'tcx>>, @@ -1947,7 +1956,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // cases where a positive bound implies a negative impl. (candidates, Vec::new()) } else if let Some(simp_rcvr_ty) = - simplify_type(self.tcx, rcvr_ty, TreatParams::AsBoundTypes) + simplify_type(self.tcx, rcvr_ty, TreatParams::AsPlaceholder) { let mut potential_candidates = Vec::new(); let mut explicitly_negative = Vec::new(); @@ -1962,7 +1971,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .any(|imp_did| { let imp = self.tcx.impl_trait_ref(imp_did).unwrap(); let imp_simp = - simplify_type(self.tcx, imp.self_ty(), TreatParams::AsBoundTypes); + simplify_type(self.tcx, imp.self_ty(), TreatParams::AsPlaceholder); imp_simp.map_or(false, |s| s == simp_rcvr_ty) }) { diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index 76c955d6f69..280fae5fe6d 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -85,7 +85,9 @@ pub mod method; mod op; mod pat; mod place_op; +mod region; mod regionck; +pub mod rvalue_scopes; mod upvar; mod wfcheck; pub mod writeback; @@ -111,7 +113,6 @@ use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; -use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::{HirIdMap, ImplicitSelfKind, Node}; use rustc_index::bit_set::BitSet; use rustc_index::vec::Idx; @@ -474,6 +475,9 @@ fn typeck_with_fallback<'tcx>( // because they don't constrain other type variables. fcx.closure_analyze(body); assert!(fcx.deferred_call_resolutions.borrow().is_empty()); + // Before the generator analysis, temporary scopes shall be marked to provide more + // precise information on types to be captured. + fcx.resolve_rvalue_scopes(def_id.to_def_id()); fcx.resolve_generator_interiors(def_id.to_def_id()); for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) { @@ -891,16 +895,19 @@ fn report_unexpected_variant_res(tcx: TyCtxt<'_>, res: Res, span: Span) { /// Tupling means that all call-side arguments are packed into a tuple and /// passed as a single parameter. For example, if tupling is enabled, this /// function: -/// -/// fn f(x: (isize, isize)) -/// +/// ``` +/// fn f(x: (isize, isize)) {} +/// ``` /// Can be called as: -/// -/// f(1, 2); -/// +/// ```ignore UNSOLVED (can this be done in user code?) +/// # fn f(x: (isize, isize)) {} +/// f(1, 2); +/// ``` /// Instead of: -/// -/// f((1, 2)); +/// ``` +/// # fn f(x: (isize, isize)) {} +/// f((1, 2)); +/// ``` #[derive(Clone, Eq, PartialEq)] enum TupleArgumentsFlag { DontTupleArguments, @@ -933,19 +940,6 @@ impl<'a, 'tcx> MaybeInProgressTables<'a, 'tcx> { } } -struct CheckItemTypesVisitor<'tcx> { - tcx: TyCtxt<'tcx>, -} - -impl<'tcx> ItemLikeVisitor<'tcx> for CheckItemTypesVisitor<'tcx> { - fn visit_item(&mut self, i: &'tcx hir::Item<'tcx>) { - check_item_type(self.tcx, i); - } - fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem<'tcx>) {} - fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem<'tcx>) {} - fn visit_foreign_item(&mut self, _: &'tcx hir::ForeignItem<'tcx>) {} -} - fn typeck_item_bodies(tcx: TyCtxt<'_>, (): ()) { tcx.hir().par_body_owners(|body_owner_def_id| tcx.ensure().typeck(body_owner_def_id)); } diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs index 1ae53a77adc..c99d9d8f923 100644 --- a/compiler/rustc_typeck/src/check/op.rs +++ b/compiler/rustc_typeck/src/check/op.rs @@ -41,7 +41,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return_ty }; - self.check_lhs_assignable(lhs, "E0067", op.span); + self.check_lhs_assignable(lhs, "E0067", op.span, |err| { + if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) { + if self + .lookup_op_method( + lhs_deref_ty, + Some(rhs_ty), + Some(rhs), + Op::Binary(op, IsAssign::Yes), + ) + .is_ok() + { + // Suppress this error, since we already emitted + // a deref suggestion in check_overloaded_binop + err.delay_as_bug(); + } + } + }); ty } @@ -404,16 +420,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (err, missing_trait, use_output) } }; - if let Ref(_, rty, _) = lhs_ty.kind() { - if self.infcx.type_is_copy_modulo_regions(self.param_env, *rty, lhs_expr.span) - && self - .lookup_op_method( - *rty, - Some(rhs_ty), - Some(rhs_expr), - Op::Binary(op, is_assign), - ) - .is_ok() + + let mut suggest_deref_binop = |lhs_deref_ty: Ty<'tcx>| { + if self + .lookup_op_method( + lhs_deref_ty, + Some(rhs_ty), + Some(rhs_expr), + Op::Binary(op, is_assign), + ) + .is_ok() { if let Ok(lstring) = source_map.span_to_snippet(lhs_expr.span) { let msg = &format!( @@ -423,7 +439,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { IsAssign::Yes => "=", IsAssign::No => "", }, - rty.peel_refs(), + lhs_deref_ty.peel_refs(), lstring, ); err.span_suggestion_verbose( @@ -434,6 +450,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } } + }; + + // We should suggest `a + b` => `*a + b` if `a` is copy, and suggest + // `a += b` => `*a += b` if a is a mut ref. + if is_assign == IsAssign::Yes + && let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) { + suggest_deref_binop(lhs_deref_ty); + } else if is_assign == IsAssign::No + && let Ref(_, lhs_deref_ty, _) = lhs_ty.kind() { + if self.infcx.type_is_copy_modulo_regions(self.param_env, *lhs_deref_ty, lhs_expr.span) { + suggest_deref_binop(*lhs_deref_ty); + } } if let Some(missing_trait) = missing_trait { let mut visitor = TypeParamVisitor(vec![]); diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index 356763fab5e..5eba95b495d 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -1894,7 +1894,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// Syntactically, these look like `[pat_0, ..., pat_n]`. /// Semantically, we are type checking a pattern with structure: - /// ``` + /// ```ignore (not-rust) /// [before_0, ..., before_n, (slice, after_0, ... after_n)?] /// ``` /// The type of `slice`, if it is present, depends on the `expected` type. diff --git a/compiler/rustc_passes/src/region.rs b/compiler/rustc_typeck/src/check/region.rs index d694782f9e7..43d189abcf7 100644 --- a/compiler/rustc_passes/src/region.rs +++ b/compiler/rustc_typeck/src/check/region.rs @@ -14,7 +14,6 @@ use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{Arm, Block, Expr, Local, Pat, PatKind, Stmt}; use rustc_index::vec::Idx; use rustc_middle::middle::region::*; -use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_span::source_map; use rustc_span::Span; @@ -527,7 +526,13 @@ fn resolve_local<'tcx>( if let Some(pat) = pat { if is_binding_pat(pat) { - record_rvalue_scope(visitor, &expr, blk_scope); + visitor.scope_tree.record_rvalue_candidate( + expr.hir_id, + RvalueCandidateType::Pattern { + target: expr.hir_id.local_id, + lifetime: blk_scope, + }, + ); } } } @@ -625,9 +630,15 @@ fn resolve_local<'tcx>( blk_id: Option<Scope>, ) { match expr.kind { - hir::ExprKind::AddrOf(_, _, ref subexpr) => { - record_rvalue_scope_if_borrow_expr(visitor, &subexpr, blk_id); - record_rvalue_scope(visitor, &subexpr, blk_id); + hir::ExprKind::AddrOf(_, _, subexpr) => { + record_rvalue_scope_if_borrow_expr(visitor, subexpr, blk_id); + visitor.scope_tree.record_rvalue_candidate( + subexpr.hir_id, + RvalueCandidateType::Borrow { + target: subexpr.hir_id.local_id, + lifetime: blk_id, + }, + ); } hir::ExprKind::Struct(_, fields, _) => { for field in fields { @@ -647,52 +658,15 @@ fn resolve_local<'tcx>( record_rvalue_scope_if_borrow_expr(visitor, &subexpr, blk_id); } } - _ => {} - } - } - - /// Applied to an expression `expr` if `expr` -- or something owned or partially owned by - /// `expr` -- is going to be indirectly referenced by a variable in a let statement. In that - /// case, the "temporary lifetime" or `expr` is extended to be the block enclosing the `let` - /// statement. - /// - /// More formally, if `expr` matches the grammar `ET`, record the rvalue scope of the matching - /// `<rvalue>` as `blk_id`: - /// - /// ```text - /// ET = *ET - /// | ET[...] - /// | ET.f - /// | (ET) - /// | <rvalue> - /// ``` - /// - /// Note: ET is intended to match "rvalues or places based on rvalues". - fn record_rvalue_scope<'tcx>( - visitor: &mut RegionResolutionVisitor<'tcx>, - expr: &hir::Expr<'_>, - blk_scope: Option<Scope>, - ) { - let mut expr = expr; - loop { - // Note: give all the expressions matching `ET` with the - // extended temporary lifetime, not just the innermost rvalue, - // because in codegen if we must compile e.g., `*rvalue()` - // into a temporary, we request the temporary scope of the - // outer expression. - visitor.scope_tree.record_rvalue_scope(expr.hir_id.local_id, blk_scope); - - match expr.kind { - hir::ExprKind::AddrOf(_, _, ref subexpr) - | hir::ExprKind::Unary(hir::UnOp::Deref, ref subexpr) - | hir::ExprKind::Field(ref subexpr, _) - | hir::ExprKind::Index(ref subexpr, _) => { - expr = &subexpr; - } - _ => { - return; - } + hir::ExprKind::Call(..) | hir::ExprKind::MethodCall(..) => { + // FIXME(@dingxiangfei2009): choose call arguments here + // for candidacy for extended parameter rule application + } + hir::ExprKind::Index(..) => { + // FIXME(@dingxiangfei2009): select the indices + // as candidate for rvalue scope rules } + _ => {} } } } @@ -821,14 +795,16 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { } } -fn region_scope_tree(tcx: TyCtxt<'_>, def_id: DefId) -> &ScopeTree { +/// Per-body `region::ScopeTree`. The `DefId` should be the owner `DefId` for the body; +/// in the case of closures, this will be redirected to the enclosing function. +pub fn region_scope_tree(tcx: TyCtxt<'_>, def_id: DefId) -> ScopeTree { let typeck_root_def_id = tcx.typeck_root_def_id(def_id); if typeck_root_def_id != def_id { - return tcx.region_scope_tree(typeck_root_def_id); + return region_scope_tree(tcx, typeck_root_def_id); } let id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); - let scope_tree = if let Some(body_id) = tcx.hir().maybe_body_owned_by(id) { + if let Some(body_id) = tcx.hir().maybe_body_owned_by(id) { let mut visitor = RegionResolutionVisitor { tcx, scope_tree: ScopeTree::default(), @@ -845,11 +821,5 @@ fn region_scope_tree(tcx: TyCtxt<'_>, def_id: DefId) -> &ScopeTree { visitor.scope_tree } else { ScopeTree::default() - }; - - tcx.arena.alloc(scope_tree) -} - -pub fn provide(providers: &mut Providers) { - *providers = Providers { region_scope_tree, ..*providers }; + } } diff --git a/compiler/rustc_typeck/src/check/regionck.rs b/compiler/rustc_typeck/src/check/regionck.rs index e37e83e7487..01a76ce5586 100644 --- a/compiler/rustc_typeck/src/check/regionck.rs +++ b/compiler/rustc_typeck/src/check/regionck.rs @@ -75,7 +75,6 @@ use crate::check::dropck; use crate::check::FnCtxt; use crate::mem_categorization as mc; -use crate::middle::region; use crate::outlives::outlives_bounds::InferCtxtExt as _; use rustc_data_structures::stable_set::FxHashSet; use rustc_hir as hir; @@ -219,8 +218,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub struct RegionCtxt<'a, 'tcx> { pub fcx: &'a FnCtxt<'a, 'tcx>, - pub region_scope_tree: &'tcx region::ScopeTree, - outlives_environment: OutlivesEnvironment<'tcx>, // id of innermost fn body id @@ -247,11 +244,9 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { Subject(subject): Subject, param_env: ty::ParamEnv<'tcx>, ) -> RegionCtxt<'a, 'tcx> { - let region_scope_tree = fcx.tcx.region_scope_tree(subject); let outlives_environment = OutlivesEnvironment::new(param_env); RegionCtxt { fcx, - region_scope_tree, body_id: initial_body_id, body_owner: subject, subject_def_id: subject, @@ -269,7 +264,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { /// /// Consider this silly example: /// - /// ``` + /// ```ignore UNSOLVED (does replacing @i32 with Box<i32> preserve the desired semantics for the example?) /// fn borrow(x: &i32) -> &i32 {x} /// fn foo(x: @i32) -> i32 { // block: B /// let b = borrow(x); // region: <R0> diff --git a/compiler/rustc_typeck/src/check/rvalue_scopes.rs b/compiler/rustc_typeck/src/check/rvalue_scopes.rs new file mode 100644 index 00000000000..22c9e796107 --- /dev/null +++ b/compiler/rustc_typeck/src/check/rvalue_scopes.rs @@ -0,0 +1,83 @@ +use super::FnCtxt; +use hir::def_id::DefId; +use hir::Node; +use rustc_hir as hir; +use rustc_middle::middle::region::{RvalueCandidateType, Scope, ScopeTree}; +use rustc_middle::ty::RvalueScopes; + +/// Applied to an expression `expr` if `expr` -- or something owned or partially owned by +/// `expr` -- is going to be indirectly referenced by a variable in a let statement. In that +/// case, the "temporary lifetime" or `expr` is extended to be the block enclosing the `let` +/// statement. +/// +/// More formally, if `expr` matches the grammar `ET`, record the rvalue scope of the matching +/// `<rvalue>` as `blk_id`: +/// +/// ```text +/// ET = *ET +/// | ET[...] +/// | ET.f +/// | (ET) +/// | <rvalue> +/// ``` +/// +/// Note: ET is intended to match "rvalues or places based on rvalues". +fn record_rvalue_scope_rec( + rvalue_scopes: &mut RvalueScopes, + mut expr: &hir::Expr<'_>, + lifetime: Option<Scope>, +) { + loop { + // Note: give all the expressions matching `ET` with the + // extended temporary lifetime, not just the innermost rvalue, + // because in codegen if we must compile e.g., `*rvalue()` + // into a temporary, we request the temporary scope of the + // outer expression. + + rvalue_scopes.record_rvalue_scope(expr.hir_id.local_id, lifetime); + + match expr.kind { + hir::ExprKind::AddrOf(_, _, subexpr) + | hir::ExprKind::Unary(hir::UnOp::Deref, subexpr) + | hir::ExprKind::Field(subexpr, _) + | hir::ExprKind::Index(subexpr, _) => { + expr = subexpr; + } + _ => { + return; + } + } + } +} +fn record_rvalue_scope( + rvalue_scopes: &mut RvalueScopes, + expr: &hir::Expr<'_>, + candidate: &RvalueCandidateType, +) { + debug!("resolve_rvalue_scope(expr={expr:?}, candidate={candidate:?})"); + match candidate { + RvalueCandidateType::Borrow { lifetime, .. } + | RvalueCandidateType::Pattern { lifetime, .. } => { + record_rvalue_scope_rec(rvalue_scopes, expr, *lifetime) + } // FIXME(@dingxiangfei2009): handle the candidates in the function call arguments + } +} + +pub fn resolve_rvalue_scopes<'a, 'tcx>( + fcx: &'a FnCtxt<'a, 'tcx>, + scope_tree: &'a ScopeTree, + def_id: DefId, +) -> RvalueScopes { + let tcx = &fcx.tcx; + let hir_map = tcx.hir(); + let mut rvalue_scopes = RvalueScopes::new(); + debug!("start resolving rvalue scopes, def_id={def_id:?}"); + debug!("rvalue_scope: rvalue_candidates={:?}", scope_tree.rvalue_candidates); + for (&hir_id, candidate) in &scope_tree.rvalue_candidates { + let Some(Node::Expr(expr)) = hir_map.find(hir_id) else { + bug!("hir node does not exist") + }; + record_rvalue_scope(&mut rvalue_scopes, expr, candidate); + } + rvalue_scopes +} diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 9dbb8132932..f57d5761051 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -5,9 +5,9 @@ //! immutable "borrow kind" (see `ty::BorrowKind` for details) and then //! "escalating" the kind as needed. The borrow kind proceeds according to //! the following lattice: -//! -//! ty::ImmBorrow -> ty::UniqueImmBorrow -> ty::MutBorrow -//! +//! ```ignore (not-rust) +//! ty::ImmBorrow -> ty::UniqueImmBorrow -> ty::MutBorrow +//! ``` //! So, for example, if we see an assignment `x = 5` to an upvar `x`, we //! will promote its borrow kind to mutable borrow. If we see an `&mut x` //! we'll do the same. Naturally, this applies not just to the upvar, but @@ -447,16 +447,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// the existing min_capture map that is stored in TypeckResults. /// /// Eg: - /// ```rust,no_run + /// ``` + /// #[derive(Debug)] /// struct Point { x: i32, y: i32 } /// - /// let s: String; // hir_id_s - /// let mut p: Point; // his_id_p + /// let s = String::from("s"); // hir_id_s + /// let mut p = Point { x: 2, y: -2 }; // his_id_p /// let c = || { - /// println!("{s}"); // L1 + /// println!("{s:?}"); // L1 /// p.x += 10; // L2 /// println!("{}" , p.y); // L3 - /// println!("{p}"); // L4 + /// println!("{p:?}"); // L4 /// drop(s); // L5 /// }; /// ``` @@ -465,7 +466,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// InferBorrowKind results in a structure like this: /// - /// ```text + /// ```ignore (illustrative) /// { /// Place(base: hir_id_s, projections: [], ....) -> { /// capture_kind_expr: hir_id_L5, @@ -487,10 +488,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// path_expr_id: hir_id_L4, /// capture_kind: ByValue /// }, + /// } /// ``` /// /// After the min capture analysis, we get: - /// ```text + /// ```ignore (illustrative) /// { /// hir_id_s -> [ /// Place(base: hir_id_s, projections: [], ....) -> { @@ -506,6 +508,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// capture_kind: ByValue /// }, /// ], + /// } /// ``` fn compute_min_captures( &self, @@ -914,6 +917,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { reasons.auto_traits.extend(auto_trait_reasons); reasons.drop_order = drop_order; + // `auto_trait_reasons` are in hashset order, so sort them to put the + // diagnostics we emit later in a cross-platform-consistent order. + reasons.auto_traits.sort_unstable(); + reasons } @@ -1281,27 +1288,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// - Ty(place): Type of place /// - `(a, b)`: Represents the function parameters `base_path_ty` and `captured_by_move_projs` /// respectively. - /// ``` + /// ```ignore (illustrative) /// (Ty(w), [ &[p, x], &[c] ]) - /// | - /// ---------------------------- - /// | | - /// v v + /// // | + /// // ---------------------------- + /// // | | + /// // v v /// (Ty(w.p), [ &[x] ]) (Ty(w.c), [ &[] ]) // I(1) - /// | | - /// v v + /// // | | + /// // v v /// (Ty(w.p), [ &[x] ]) false - /// | - /// | - /// ------------------------------- - /// | | - /// v v + /// // | + /// // | + /// // ------------------------------- + /// // | | + /// // v v /// (Ty((w.p).x), [ &[] ]) (Ty((w.p).y), []) // IMP 2 - /// | | - /// v v + /// // | | + /// // v v /// false NeedsSignificantDrop(Ty(w.p.y)) - /// | - /// v + /// // | + /// // v /// true /// ``` /// @@ -1319,7 +1326,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// Consider another example: /// - /// ```rust + /// ```ignore (pseudo-rust) /// struct X; /// impl Drop for X {} /// @@ -1724,14 +1731,14 @@ struct InferBorrowKind<'a, 'tcx> { /// s.str2 via a MutableBorrow /// /// ```rust,no_run - /// struct SomeStruct { str1: String, str2: String } + /// struct SomeStruct { str1: String, str2: String }; /// /// // Assume that the HirId for the variable definition is `V1` - /// let mut s = SomeStruct { str1: format!("s1"), str2: format!("s2") } + /// let mut s = SomeStruct { str1: format!("s1"), str2: format!("s2") }; /// /// let fix_s = |new_s2| { /// // Assume that the HirId for the expression `s.str1` is `E1` - /// println!("Updating SomeStruct with str1=", s.str1); + /// println!("Updating SomeStruct with str1={0}", s.str1); /// // Assume that the HirId for the expression `*s.str2` is `E2` /// s.str2 = new_s2; /// }; @@ -1739,7 +1746,7 @@ struct InferBorrowKind<'a, 'tcx> { /// /// For closure `fix_s`, (at a high level) the map contains /// - /// ``` + /// ```ignore (illustrative) /// Place { V1, [ProjectionKind::Field(Index=0, Variant=0)] } : CaptureKind { E1, ImmutableBorrow } /// Place { V1, [ProjectionKind::Field(Index=1, Variant=0)] } : CaptureKind { E2, MutableBorrow } /// ``` @@ -1748,14 +1755,19 @@ struct InferBorrowKind<'a, 'tcx> { } impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { - fn fake_read(&mut self, place: Place<'tcx>, cause: FakeReadCause, diag_expr_id: hir::HirId) { - let PlaceBase::Upvar(_) = place.base else { return }; + fn fake_read( + &mut self, + place: &PlaceWithHirId<'tcx>, + cause: FakeReadCause, + diag_expr_id: hir::HirId, + ) { + let PlaceBase::Upvar(_) = place.place.base else { return }; // We need to restrict Fake Read precision to avoid fake reading unsafe code, // such as deref of a raw pointer. let dummy_capture_kind = ty::UpvarCapture::ByRef(ty::BorrowKind::ImmBorrow); - let (place, _) = restrict_capture_precision(place, dummy_capture_kind); + let (place, _) = restrict_capture_precision(place.place.clone(), dummy_capture_kind); let (place, _) = restrict_repr_packed_field_ref_capture( self.fcx.tcx, @@ -2067,7 +2079,7 @@ fn migration_suggestion_for_2229( /// Consider the following example /// ```rust,no_run /// struct Point { x: i32, y: i32 } -/// let mut p: Point { x: 10, y: 10 }; +/// let mut p = Point { x: 10, y: 10 }; /// /// let c = || { /// p.x += 10; @@ -2213,7 +2225,10 @@ fn determine_place_ancestry_relation<'tcx>( /// /// Reason we only drop the last deref is because of the following edge case: /// -/// ```rust +/// ``` +/// # struct A { field_of_a: Box<i32> } +/// # struct B {} +/// # struct C<'a>(&'a i32); /// struct MyStruct<'a> { /// a: &'static A, /// b: B, @@ -2221,7 +2236,7 @@ fn determine_place_ancestry_relation<'tcx>( /// } /// /// fn foo<'a, 'b>(m: &'a MyStruct<'b>) -> impl FnMut() + 'static { -/// let c = || drop(&*m.a.field_of_a); +/// || drop(&*m.a.field_of_a) /// // Here we really do want to capture `*m.a` because that outlives `'static` /// /// // If we capture `m`, then the closure no longer outlives `'static' diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index ec2b7c13ff3..50966868ec7 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -21,7 +21,8 @@ use rustc_middle::hir::nested_filter; use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts, Subst}; use rustc_middle::ty::trait_def::TraitSpecializationKind; use rustc_middle::ty::{ - self, AdtKind, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitor, + self, AdtKind, EarlyBinder, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, + TypeVisitor, }; use rustc_session::parse::feature_err; use rustc_span::symbol::{sym, Ident, Symbol}; @@ -37,7 +38,7 @@ use std::ops::ControlFlow; /// Helper type of a temporary returned by `.for_item(...)`. /// This is necessary because we can't write the following bound: /// -/// ```rust +/// ```ignore (illustrative) /// F: for<'b, 'tcx> where 'tcx FnOnce(FnCtxt<'b, 'tcx>) /// ``` struct CheckWfFcxBuilder<'tcx> { @@ -1388,7 +1389,7 @@ fn check_where_clauses<'tcx, 'fcx>( } let mut param_count = CountParams::default(); let has_region = pred.visit_with(&mut param_count).is_break(); - let substituted_pred = pred.subst(tcx, substs); + let substituted_pred = EarlyBinder(pred).subst(tcx, substs); // Don't check non-defaulted params, dependent defaults (including lifetimes) // or preds with multiple params. if substituted_pred.has_param_types_or_consts() diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs index 72a50d02ad8..16096ea3d74 100644 --- a/compiler/rustc_typeck/src/check/writeback.rs +++ b/compiler/rustc_typeck/src/check/writeback.rs @@ -71,6 +71,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { wbcx.visit_user_provided_sigs(); wbcx.visit_generator_interior_types(); + wbcx.typeck_results.region_scope_tree = + mem::take(&mut self.typeck_results.borrow_mut().region_scope_tree); + wbcx.typeck_results.rvalue_scopes = + mem::take(&mut self.typeck_results.borrow_mut().rvalue_scopes); + let used_trait_imports = mem::take(&mut self.typeck_results.borrow_mut().used_trait_imports); debug!("used_trait_imports({:?}) = {:?}", item_def_id, used_trait_imports); @@ -646,7 +651,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } -crate trait Locatable { +pub(crate) trait Locatable { fn to_span(&self, tcx: TyCtxt<'_>) -> Span; } diff --git a/compiler/rustc_typeck/src/check_unused.rs b/compiler/rustc_typeck/src/check_unused.rs index d52886a09bd..00f0d1e6f02 100644 --- a/compiler/rustc_typeck/src/check_unused.rs +++ b/compiler/rustc_typeck/src/check_unused.rs @@ -17,7 +17,7 @@ pub fn check_crate(tcx: TyCtxt<'_>) { } for id in tcx.hir().items() { - if matches!(tcx.hir().def_kind(id.def_id), DefKind::Use) { + if matches!(tcx.def_kind(id.def_id), DefKind::Use) { if tcx.visibility(id.def_id).is_public() { continue; } @@ -101,7 +101,7 @@ fn unused_crates_lint(tcx: TyCtxt<'_>) { let mut crates_to_lint = vec![]; for id in tcx.hir().items() { - if matches!(tcx.hir().def_kind(id.def_id), DefKind::ExternCrate) { + if matches!(tcx.def_kind(id.def_id), DefKind::ExternCrate) { let item = tcx.hir().item(id); if let hir::ItemKind::ExternCrate(orig_name) = item.kind { crates_to_lint.push(ExternCrateToLint { @@ -128,7 +128,8 @@ fn unused_crates_lint(tcx: TyCtxt<'_>) { tcx.struct_span_lint_hir(lint, id, span, |lint| { // Removal suggestion span needs to include attributes (Issue #54400) let span_with_attrs = tcx - .get_attrs(extern_crate.def_id) + .hir() + .attrs(id) .iter() .map(|attr| attr.span) .fold(span, |acc, attr_span| acc.to(attr_span)); @@ -166,13 +167,13 @@ fn unused_crates_lint(tcx: TyCtxt<'_>) { continue; } + let id = tcx.hir().local_def_id_to_hir_id(def_id); // If the extern crate has any attributes, they may have funky // semantics we can't faithfully represent using `use` (most // notably `#[macro_use]`). Ignore it. - if !tcx.get_attrs(extern_crate.def_id).is_empty() { + if !tcx.hir().attrs(id).is_empty() { continue; } - let id = tcx.hir().local_def_id_to_hir_id(def_id); tcx.struct_span_lint_hir(lint, id, extern_crate.span, |lint| { // Otherwise, we can convert it into a `use` of some kind. let base_replacement = match extern_crate.orig_name { diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls.rs b/compiler/rustc_typeck/src/coherence/inherent_impls.rs index 5ad0c4ac52d..7a9b874b5e4 100644 --- a/compiler/rustc_typeck/src/coherence/inherent_impls.rs +++ b/compiler/rustc_typeck/src/coherence/inherent_impls.rs @@ -9,8 +9,8 @@ use rustc_errors::struct_span_err; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; -use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_middle::ty::fast_reject::{simplify_type, SimplifiedType, TreatParams}; use rustc_middle::ty::{self, CrateInherentImpls, Ty, TyCtxt}; use rustc_span::symbol::sym; @@ -19,7 +19,9 @@ use rustc_span::Span; /// On-demand query: yields a map containing all types mapped to their inherent impls. pub fn crate_inherent_impls(tcx: TyCtxt<'_>, (): ()) -> CrateInherentImpls { let mut collect = InherentCollect { tcx, impls_map: Default::default() }; - tcx.hir().visit_all_item_likes(&mut collect); + for id in tcx.hir().items() { + collect.check_item(id); + } collect.impls_map } @@ -46,92 +48,67 @@ struct InherentCollect<'tcx> { impls_map: CrateInherentImpls, } -impl<'tcx> ItemLikeVisitor<'_> for InherentCollect<'tcx> { - fn visit_item(&mut self, item: &hir::Item<'_>) { - let hir::ItemKind::Impl(hir::Impl { of_trait: None, self_ty: ty, ref items, .. }) = item.kind else { +const INTO_CORE: &str = "consider moving this inherent impl into `core` if possible"; +const INTO_DEFINING_CRATE: &str = + "consider moving this inherent impl into the crate defining the type if possible"; +const ADD_ATTR_TO_TY: &str = "alternatively add `#[rustc_has_incoherent_inherent_impls]` to the type \ + and `#[rustc_allow_incoherent_impl]` to the relevant impl items"; +const ADD_ATTR: &str = + "alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items"; + +impl<'tcx> InherentCollect<'tcx> { + fn check_def_id(&mut self, item: &hir::Item<'_>, self_ty: Ty<'tcx>, def_id: DefId) { + let impl_def_id = item.def_id; + if let Some(def_id) = def_id.as_local() { + // Add the implementation to the mapping from implementation to base + // type def ID, if there is a base type for this implementation and + // the implementation does not have any associated traits. + let vec = self.impls_map.inherent_impls.entry(def_id).or_default(); + vec.push(impl_def_id.to_def_id()); return; - }; + } - let self_ty = self.tcx.type_of(item.def_id); - match *self_ty.kind() { - ty::Adt(def, _) => { - let def_id = def.did(); - if !def_id.is_local() && Some(def_id) == self.tcx.lang_items().c_str() { - self.check_primitive_impl(item.def_id, self_ty, items, ty.span) - } else { - self.check_def_id(item, def_id); - } - } - ty::Foreign(did) => { - self.check_def_id(item, did); - } - ty::Dynamic(data, ..) if data.principal_def_id().is_some() => { - self.check_def_id(item, data.principal_def_id().unwrap()); - } - ty::Dynamic(..) => { + if self.tcx.features().rustc_attrs { + let hir::ItemKind::Impl(&hir::Impl { items, .. }) = item.kind else { + bug!("expected `impl` item: {:?}", item); + }; + + if !self.tcx.has_attr(def_id, sym::rustc_has_incoherent_inherent_impls) { struct_span_err!( self.tcx.sess, - ty.span, - E0785, - "cannot define inherent `impl` for a dyn auto trait" + item.span, + E0390, + "cannot define inherent `impl` for a type outside of the crate where the type is defined", ) - .span_label(ty.span, "impl requires at least one non-auto trait") - .note("define and implement a new trait or type instead") + .help(INTO_DEFINING_CRATE) + .span_help(item.span, ADD_ATTR_TO_TY) .emit(); + return; } - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Str - | ty::Array(..) - | ty::Slice(_) - | ty::RawPtr(_) - | ty::Ref(..) - | ty::Never - | ty::Tuple(..) => self.check_primitive_impl(item.def_id, self_ty, items, ty.span), - ty::FnPtr(_) | ty::Projection(..) | ty::Opaque(..) | ty::Param(_) => { - let mut err = struct_span_err!( - self.tcx.sess, - ty.span, - E0118, - "no nominal type found for inherent implementation" - ); - err.span_label(ty.span, "impl requires a nominal type") - .note("either implement a trait on it or create a newtype to wrap it instead"); - - err.emit(); - } - ty::FnDef(..) - | ty::Closure(..) - | ty::Generator(..) - | ty::GeneratorWitness(..) - | ty::Bound(..) - | ty::Placeholder(_) - | ty::Infer(_) => { - bug!("unexpected impl self type of impl: {:?} {:?}", item.def_id, self_ty); + for impl_item in items { + if !self + .tcx + .has_attr(impl_item.id.def_id.to_def_id(), sym::rustc_allow_incoherent_impl) + { + struct_span_err!( + self.tcx.sess, + item.span, + E0390, + "cannot define inherent `impl` for a type outside of the crate where the type is defined", + ) + .help(INTO_DEFINING_CRATE) + .span_help(impl_item.span, ADD_ATTR) + .emit(); + return; + } } - ty::Error(_) => {} - } - } - - fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'_>) {} - - fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'_>) {} - fn visit_foreign_item(&mut self, _foreign_item: &hir::ForeignItem<'_>) {} -} - -impl<'tcx> InherentCollect<'tcx> { - fn check_def_id(&mut self, item: &hir::Item<'_>, def_id: DefId) { - if let Some(def_id) = def_id.as_local() { - // Add the implementation to the mapping from implementation to base - // type def ID, if there is a base type for this implementation and - // the implementation does not have any associated traits. - let vec = self.impls_map.inherent_impls.entry(def_id).or_default(); - vec.push(item.def_id.to_def_id()); + if let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::AsInfer) { + self.impls_map.incoherent_impls.entry(simp).or_default().push(impl_def_id); + } else { + bug!("unexpected self type: {:?}", self_ty); + } } else { struct_span_err!( self.tcx.sess, @@ -153,9 +130,6 @@ impl<'tcx> InherentCollect<'tcx> { items: &[hir::ImplItemRef], span: Span, ) { - const INTO_CORE: &str = "consider moving this inherent impl into `core` if possible"; - const ADD_ATTR: &str = - "alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items"; if !self.tcx.hir().rustc_coherence_is_core() { if self.tcx.features().rustc_attrs { for item in items { @@ -195,10 +169,80 @@ impl<'tcx> InherentCollect<'tcx> { } } - if let Some(simp) = simplify_type(self.tcx, ty, TreatParams::AsPlaceholders) { + if let Some(simp) = simplify_type(self.tcx, ty, TreatParams::AsInfer) { self.impls_map.incoherent_impls.entry(simp).or_default().push(impl_def_id); } else { bug!("unexpected primitive type: {:?}", ty); } } + + fn check_item(&mut self, id: hir::ItemId) { + if !matches!(self.tcx.def_kind(id.def_id), DefKind::Impl) { + return; + } + + let item = self.tcx.hir().item(id); + let hir::ItemKind::Impl(hir::Impl { of_trait: None, self_ty: ty, ref items, .. }) = item.kind else { + return; + }; + + let self_ty = self.tcx.type_of(item.def_id); + match *self_ty.kind() { + ty::Adt(def, _) => { + self.check_def_id(item, self_ty, def.did()); + } + ty::Foreign(did) => { + self.check_def_id(item, self_ty, did); + } + ty::Dynamic(data, ..) if data.principal_def_id().is_some() => { + self.check_def_id(item, self_ty, data.principal_def_id().unwrap()); + } + ty::Dynamic(..) => { + struct_span_err!( + self.tcx.sess, + ty.span, + E0785, + "cannot define inherent `impl` for a dyn auto trait" + ) + .span_label(ty.span, "impl requires at least one non-auto trait") + .note("define and implement a new trait or type instead") + .emit(); + } + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Str + | ty::Array(..) + | ty::Slice(_) + | ty::RawPtr(_) + | ty::Ref(..) + | ty::Never + | ty::Tuple(..) => self.check_primitive_impl(item.def_id, self_ty, items, ty.span), + ty::FnPtr(_) | ty::Projection(..) | ty::Opaque(..) | ty::Param(_) => { + let mut err = struct_span_err!( + self.tcx.sess, + ty.span, + E0118, + "no nominal type found for inherent implementation" + ); + + err.span_label(ty.span, "impl requires a nominal type") + .note("either implement a trait on it or create a newtype to wrap it instead"); + + err.emit(); + } + ty::FnDef(..) + | ty::Closure(..) + | ty::Generator(..) + | ty::GeneratorWitness(..) + | ty::Bound(..) + | ty::Placeholder(_) + | ty::Infer(_) => { + bug!("unexpected impl self type of impl: {:?} {:?}", item.def_id, self_ty); + } + ty::Error(_) => {} + } + } } diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs b/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs index cf71e0f300c..db67c1f7c9e 100644 --- a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs +++ b/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs @@ -1,8 +1,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::struct_span_err; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; -use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_index::vec::IndexVec; use rustc_middle::traits::specialization_graph::OverlapMode; use rustc_middle::ty::{self, TyCtxt}; @@ -12,7 +12,10 @@ use smallvec::SmallVec; use std::collections::hash_map::Entry; pub fn crate_inherent_impls_overlap_check(tcx: TyCtxt<'_>, (): ()) { - tcx.hir().visit_all_item_likes(&mut InherentOverlapChecker { tcx }); + let mut inherent_overlap_checker = InherentOverlapChecker { tcx }; + for id in tcx.hir().items() { + inherent_overlap_checker.check_item(id); + } } struct InherentOverlapChecker<'tcx> { @@ -121,200 +124,184 @@ impl<'tcx> InherentOverlapChecker<'tcx> { || true, ); } -} -impl<'tcx> ItemLikeVisitor<'_> for InherentOverlapChecker<'tcx> { - fn visit_item(&mut self, item: &hir::Item<'_>) { - match item.kind { - hir::ItemKind::Enum(..) - | hir::ItemKind::Struct(..) - | hir::ItemKind::Trait(..) - | hir::ItemKind::Union(..) => { - let impls = self.tcx.inherent_impls(item.def_id); + fn check_item(&mut self, id: hir::ItemId) { + let def_kind = self.tcx.def_kind(id.def_id); + if !matches!(def_kind, DefKind::Enum | DefKind::Struct | DefKind::Trait | DefKind::Union) { + return; + } - // If there is only one inherent impl block, - // there is nothing to overlap check it with - if impls.len() <= 1 { - return; - } + let impls = self.tcx.inherent_impls(id.def_id); - let overlap_mode = OverlapMode::get(self.tcx, item.def_id.to_def_id()); + // If there is only one inherent impl block, + // there is nothing to overlap check it with + if impls.len() <= 1 { + return; + } - let impls_items = impls - .iter() - .map(|impl_def_id| (impl_def_id, self.tcx.associated_items(*impl_def_id))) - .collect::<SmallVec<[_; 8]>>(); + let overlap_mode = OverlapMode::get(self.tcx, id.def_id.to_def_id()); - // Perform a O(n^2) algorithm for small n, - // otherwise switch to an allocating algorithm with - // faster asymptotic runtime. - const ALLOCATING_ALGO_THRESHOLD: usize = 500; - if impls.len() < ALLOCATING_ALGO_THRESHOLD { - for (i, &(&impl1_def_id, impl_items1)) in impls_items.iter().enumerate() { - for &(&impl2_def_id, impl_items2) in &impls_items[(i + 1)..] { - if self.impls_have_common_items(impl_items1, impl_items2) { - self.check_for_overlapping_inherent_impls( - overlap_mode, - impl1_def_id, - impl2_def_id, - ); - } - } + let impls_items = impls + .iter() + .map(|impl_def_id| (impl_def_id, self.tcx.associated_items(*impl_def_id))) + .collect::<SmallVec<[_; 8]>>(); + + // Perform a O(n^2) algorithm for small n, + // otherwise switch to an allocating algorithm with + // faster asymptotic runtime. + const ALLOCATING_ALGO_THRESHOLD: usize = 500; + if impls.len() < ALLOCATING_ALGO_THRESHOLD { + for (i, &(&impl1_def_id, impl_items1)) in impls_items.iter().enumerate() { + for &(&impl2_def_id, impl_items2) in &impls_items[(i + 1)..] { + if self.impls_have_common_items(impl_items1, impl_items2) { + self.check_for_overlapping_inherent_impls( + overlap_mode, + impl1_def_id, + impl2_def_id, + ); } - } else { - // Build a set of connected regions of impl blocks. - // Two impl blocks are regarded as connected if they share - // an item with the same unhygienic identifier. - // After we have assembled the connected regions, - // run the O(n^2) algorithm on each connected region. - // This is advantageous to running the algorithm over the - // entire graph when there are many connected regions. + } + } + } else { + // Build a set of connected regions of impl blocks. + // Two impl blocks are regarded as connected if they share + // an item with the same unhygienic identifier. + // After we have assembled the connected regions, + // run the O(n^2) algorithm on each connected region. + // This is advantageous to running the algorithm over the + // entire graph when there are many connected regions. - rustc_index::newtype_index! { - pub struct RegionId { - ENCODABLE = custom + rustc_index::newtype_index! { + pub struct RegionId { + ENCODABLE = custom + } + } + struct ConnectedRegion { + idents: SmallVec<[Symbol; 8]>, + impl_blocks: FxHashSet<usize>, + } + let mut connected_regions: IndexVec<RegionId, _> = Default::default(); + // Reverse map from the Symbol to the connected region id. + let mut connected_region_ids = FxHashMap::default(); + + for (i, &(&_impl_def_id, impl_items)) in impls_items.iter().enumerate() { + if impl_items.len() == 0 { + continue; + } + // First obtain a list of existing connected region ids + let mut idents_to_add = SmallVec::<[Symbol; 8]>::new(); + let mut ids = impl_items + .in_definition_order() + .filter_map(|item| { + let entry = connected_region_ids.entry(item.name); + if let Entry::Occupied(e) = &entry { + Some(*e.get()) + } else { + idents_to_add.push(item.name); + None } + }) + .collect::<SmallVec<[RegionId; 8]>>(); + // Sort the id list so that the algorithm is deterministic + ids.sort_unstable(); + ids.dedup(); + let ids = ids; + match &ids[..] { + // Create a new connected region + [] => { + let id_to_set = connected_regions.next_index(); + // Update the connected region ids + for ident in &idents_to_add { + connected_region_ids.insert(*ident, id_to_set); + } + connected_regions.insert( + id_to_set, + ConnectedRegion { + idents: idents_to_add, + impl_blocks: std::iter::once(i).collect(), + }, + ); } - struct ConnectedRegion { - idents: SmallVec<[Symbol; 8]>, - impl_blocks: FxHashSet<usize>, + // Take the only id inside the list + &[id_to_set] => { + let region = connected_regions[id_to_set].as_mut().unwrap(); + region.impl_blocks.insert(i); + region.idents.extend_from_slice(&idents_to_add); + // Update the connected region ids + for ident in &idents_to_add { + connected_region_ids.insert(*ident, id_to_set); + } } - let mut connected_regions: IndexVec<RegionId, _> = Default::default(); - // Reverse map from the Symbol to the connected region id. - let mut connected_region_ids = FxHashMap::default(); - - for (i, &(&_impl_def_id, impl_items)) in impls_items.iter().enumerate() { - if impl_items.len() == 0 { - continue; + // We have multiple connected regions to merge. + // In the worst case this might add impl blocks + // one by one and can thus be O(n^2) in the size + // of the resulting final connected region, but + // this is no issue as the final step to check + // for overlaps runs in O(n^2) as well. + &[id_to_set, ..] => { + let mut region = connected_regions.remove(id_to_set).unwrap(); + region.impl_blocks.insert(i); + region.idents.extend_from_slice(&idents_to_add); + // Update the connected region ids + for ident in &idents_to_add { + connected_region_ids.insert(*ident, id_to_set); } - // First obtain a list of existing connected region ids - let mut idents_to_add = SmallVec::<[Symbol; 8]>::new(); - let mut ids = impl_items - .in_definition_order() - .filter_map(|item| { - let entry = connected_region_ids.entry(item.name); - if let Entry::Occupied(e) = &entry { - Some(*e.get()) - } else { - idents_to_add.push(item.name); - None - } - }) - .collect::<SmallVec<[RegionId; 8]>>(); - // Sort the id list so that the algorithm is deterministic - ids.sort_unstable(); - ids.dedup(); - let ids = ids; - match &ids[..] { - // Create a new connected region - [] => { - let id_to_set = connected_regions.next_index(); - // Update the connected region ids - for ident in &idents_to_add { - connected_region_ids.insert(*ident, id_to_set); - } - connected_regions.insert( - id_to_set, - ConnectedRegion { - idents: idents_to_add, - impl_blocks: std::iter::once(i).collect(), - }, - ); - } - // Take the only id inside the list - &[id_to_set] => { - let region = connected_regions[id_to_set].as_mut().unwrap(); - region.impl_blocks.insert(i); - region.idents.extend_from_slice(&idents_to_add); - // Update the connected region ids - for ident in &idents_to_add { - connected_region_ids.insert(*ident, id_to_set); - } - } - // We have multiple connected regions to merge. - // In the worst case this might add impl blocks - // one by one and can thus be O(n^2) in the size - // of the resulting final connected region, but - // this is no issue as the final step to check - // for overlaps runs in O(n^2) as well. - &[id_to_set, ..] => { - let mut region = connected_regions.remove(id_to_set).unwrap(); - region.impl_blocks.insert(i); - region.idents.extend_from_slice(&idents_to_add); - // Update the connected region ids - for ident in &idents_to_add { - connected_region_ids.insert(*ident, id_to_set); - } - - // Remove other regions from ids. - for &id in ids.iter() { - if id == id_to_set { - continue; - } - let r = connected_regions.remove(id).unwrap(); - for ident in r.idents.iter() { - connected_region_ids.insert(*ident, id_to_set); - } - region.idents.extend_from_slice(&r.idents); - region.impl_blocks.extend(r.impl_blocks); - } - connected_regions.insert(id_to_set, region); + // Remove other regions from ids. + for &id in ids.iter() { + if id == id_to_set { + continue; + } + let r = connected_regions.remove(id).unwrap(); + for ident in r.idents.iter() { + connected_region_ids.insert(*ident, id_to_set); } + region.idents.extend_from_slice(&r.idents); + region.impl_blocks.extend(r.impl_blocks); } + + connected_regions.insert(id_to_set, region); } + } + } - debug!( - "churning through {} components (sum={}, avg={}, var={}, max={})", - connected_regions.len(), - impls.len(), - impls.len() / connected_regions.len(), - { - let avg = impls.len() / connected_regions.len(); - let s = connected_regions - .iter() - .flatten() - .map(|r| r.impl_blocks.len() as isize - avg as isize) - .map(|v| v.abs() as usize) - .sum::<usize>(); - s / connected_regions.len() - }, - connected_regions - .iter() - .flatten() - .map(|r| r.impl_blocks.len()) - .max() - .unwrap() - ); - // List of connected regions is built. Now, run the overlap check - // for each pair of impl blocks in the same connected region. - for region in connected_regions.into_iter().flatten() { - let mut impl_blocks = - region.impl_blocks.into_iter().collect::<SmallVec<[usize; 8]>>(); - impl_blocks.sort_unstable(); - for (i, &impl1_items_idx) in impl_blocks.iter().enumerate() { - let &(&impl1_def_id, impl_items1) = &impls_items[impl1_items_idx]; - for &impl2_items_idx in impl_blocks[(i + 1)..].iter() { - let &(&impl2_def_id, impl_items2) = &impls_items[impl2_items_idx]; - if self.impls_have_common_items(impl_items1, impl_items2) { - self.check_for_overlapping_inherent_impls( - overlap_mode, - impl1_def_id, - impl2_def_id, - ); - } - } + debug!( + "churning through {} components (sum={}, avg={}, var={}, max={})", + connected_regions.len(), + impls.len(), + impls.len() / connected_regions.len(), + { + let avg = impls.len() / connected_regions.len(); + let s = connected_regions + .iter() + .flatten() + .map(|r| r.impl_blocks.len() as isize - avg as isize) + .map(|v| v.abs() as usize) + .sum::<usize>(); + s / connected_regions.len() + }, + connected_regions.iter().flatten().map(|r| r.impl_blocks.len()).max().unwrap() + ); + // List of connected regions is built. Now, run the overlap check + // for each pair of impl blocks in the same connected region. + for region in connected_regions.into_iter().flatten() { + let mut impl_blocks = + region.impl_blocks.into_iter().collect::<SmallVec<[usize; 8]>>(); + impl_blocks.sort_unstable(); + for (i, &impl1_items_idx) in impl_blocks.iter().enumerate() { + let &(&impl1_def_id, impl_items1) = &impls_items[impl1_items_idx]; + for &impl2_items_idx in impl_blocks[(i + 1)..].iter() { + let &(&impl2_def_id, impl_items2) = &impls_items[impl2_items_idx]; + if self.impls_have_common_items(impl_items1, impl_items2) { + self.check_for_overlapping_inherent_impls( + overlap_mode, + impl1_def_id, + impl2_def_id, + ); } } } } - _ => {} } } - - fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'_>) {} - - fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'_>) {} - - fn visit_foreign_item(&mut self, _foreign_item: &hir::ForeignItem<'_>) {} } diff --git a/compiler/rustc_typeck/src/coherence/orphan.rs b/compiler/rustc_typeck/src/coherence/orphan.rs index f57986a985c..eb6217f1174 100644 --- a/compiler/rustc_typeck/src/coherence/orphan.rs +++ b/compiler/rustc_typeck/src/coherence/orphan.rs @@ -5,10 +5,10 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::struct_span_err; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; -use rustc_index::bit_set::GrowableBitSet; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::subst::{GenericArg, InternalSubsts}; +use rustc_middle::ty::subst::InternalSubsts; +use rustc_middle::ty::util::IgnoreRegions; use rustc_middle::ty::{self, ImplPolarity, Ty, TyCtxt, TypeFoldable, TypeVisitor}; use rustc_session::lint; use rustc_span::def_id::{DefId, LocalDefId}; @@ -325,51 +325,6 @@ fn emit_orphan_check_error<'tcx>( }) } -#[derive(Default)] -struct AreUniqueParamsVisitor { - seen: GrowableBitSet<u32>, -} - -#[derive(Copy, Clone)] -enum NotUniqueParam<'tcx> { - DuplicateParam(GenericArg<'tcx>), - NotParam(GenericArg<'tcx>), -} - -impl<'tcx> TypeVisitor<'tcx> for AreUniqueParamsVisitor { - type BreakTy = NotUniqueParam<'tcx>; - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { - match t.kind() { - ty::Param(p) => { - if self.seen.insert(p.index) { - ControlFlow::CONTINUE - } else { - ControlFlow::Break(NotUniqueParam::DuplicateParam(t.into())) - } - } - _ => ControlFlow::Break(NotUniqueParam::NotParam(t.into())), - } - } - fn visit_region(&mut self, _: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { - // We don't drop candidates during candidate assembly because of region - // constraints, so the behavior for impls only constrained by regions - // will not change. - ControlFlow::CONTINUE - } - fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { - match c.val() { - ty::ConstKind::Param(p) => { - if self.seen.insert(p.index) { - ControlFlow::CONTINUE - } else { - ControlFlow::Break(NotUniqueParam::DuplicateParam(c.into())) - } - } - _ => ControlFlow::Break(NotUniqueParam::NotParam(c.into())), - } - } -} - /// Lint impls of auto traits if they are likely to have /// unsound or surprising effects on auto impls. fn lint_auto_trait_impls(tcx: TyCtxt<'_>, trait_def_id: DefId, impls: &[LocalDefId]) { @@ -400,9 +355,9 @@ fn lint_auto_trait_impls(tcx: TyCtxt<'_>, trait_def_id: DefId, impls: &[LocalDef // Impls which completely cover a given root type are fine as they // disable auto impls entirely. So only lint if the substs // are not a permutation of the identity substs. - match substs.visit_with(&mut AreUniqueParamsVisitor::default()) { - ControlFlow::Continue(()) => {} // ok - ControlFlow::Break(arg) => { + match tcx.uses_unique_generic_params(substs, IgnoreRegions::Yes) { + Ok(()) => {} // ok + Err(arg) => { // Ideally: // // - compute the requirements for the auto impl candidate @@ -429,13 +384,21 @@ fn lint_auto_trait_impls(tcx: TyCtxt<'_>, trait_def_id: DefId, impls: &[LocalDef tcx.hir().local_def_id_to_hir_id(impl_def_id), tcx.def_span(impl_def_id), |err| { + let item_span = tcx.def_span(self_type_did); + let self_descr = tcx.def_kind(self_type_did).descr(self_type_did); let mut err = err.build(&format!( "cross-crate traits with a default impl, like `{}`, \ should not be specialized", tcx.def_path_str(trait_def_id), )); - let item_span = tcx.def_span(self_type_did); - let self_descr = tcx.def_kind(self_type_did).descr(self_type_did); + match arg { + ty::util::NotUniqueParam::DuplicateParam(arg) => { + err.note(&format!("`{}` is mentioned multiple times", arg)); + } + ty::util::NotUniqueParam::NotParam(arg) => { + err.note(&format!("`{}` is not a generic parameter", arg)); + } + } err.span_note( item_span, &format!( @@ -443,14 +406,6 @@ fn lint_auto_trait_impls(tcx: TyCtxt<'_>, trait_def_id: DefId, impls: &[LocalDef self_descr, ), ); - match arg { - NotUniqueParam::DuplicateParam(arg) => { - err.note(&format!("`{}` is mentioned multiple times", arg)); - } - NotUniqueParam::NotParam(arg) => { - err.note(&format!("`{}` is not a generic parameter", arg)); - } - } err.emit(); }, ); diff --git a/compiler/rustc_typeck/src/coherence/unsafety.rs b/compiler/rustc_typeck/src/coherence/unsafety.rs index f7aabf2406f..3cfc96ccbfd 100644 --- a/compiler/rustc_typeck/src/coherence/unsafety.rs +++ b/compiler/rustc_typeck/src/coherence/unsafety.rs @@ -3,101 +3,83 @@ use rustc_errors::struct_span_err; use rustc_hir as hir; -use rustc_hir::itemlikevisit::ItemLikeVisitor; +use rustc_hir::def::DefKind; use rustc_hir::Unsafety; use rustc_middle::ty::TyCtxt; pub fn check(tcx: TyCtxt<'_>) { - let mut unsafety = UnsafetyChecker { tcx }; - tcx.hir().visit_all_item_likes(&mut unsafety); + for id in tcx.hir().items() { + if matches!(tcx.def_kind(id.def_id), DefKind::Impl) { + let item = tcx.hir().item(id); + if let hir::ItemKind::Impl(ref impl_) = item.kind { + check_unsafety_coherence( + tcx, + item, + Some(&impl_.generics), + impl_.unsafety, + impl_.polarity, + ); + } + } + } } -struct UnsafetyChecker<'tcx> { +fn check_unsafety_coherence<'tcx>( tcx: TyCtxt<'tcx>, -} - -impl<'tcx> UnsafetyChecker<'tcx> { - fn check_unsafety_coherence( - &mut self, - item: &hir::Item<'_>, - impl_generics: Option<&hir::Generics<'_>>, - unsafety: hir::Unsafety, - polarity: hir::ImplPolarity, - ) { - if let Some(trait_ref) = self.tcx.impl_trait_ref(item.def_id) { - let trait_def = self.tcx.trait_def(trait_ref.def_id); - let unsafe_attr = impl_generics.and_then(|generics| { - generics.params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle") - }); - match (trait_def.unsafety, unsafe_attr, unsafety, polarity) { - (Unsafety::Normal, None, Unsafety::Unsafe, hir::ImplPolarity::Positive) => { - struct_span_err!( - self.tcx.sess, - item.span, - E0199, - "implementing the trait `{}` is not unsafe", - trait_ref.print_only_trait_path() - ) - .emit(); - } - - (Unsafety::Unsafe, _, Unsafety::Normal, hir::ImplPolarity::Positive) => { - struct_span_err!( - self.tcx.sess, - item.span, - E0200, - "the trait `{}` requires an `unsafe impl` declaration", - trait_ref.print_only_trait_path() - ) - .emit(); - } + item: &hir::Item<'_>, + impl_generics: Option<&hir::Generics<'_>>, + unsafety: hir::Unsafety, + polarity: hir::ImplPolarity, +) { + if let Some(trait_ref) = tcx.impl_trait_ref(item.def_id) { + let trait_def = tcx.trait_def(trait_ref.def_id); + let unsafe_attr = impl_generics.and_then(|generics| { + generics.params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle") + }); + match (trait_def.unsafety, unsafe_attr, unsafety, polarity) { + (Unsafety::Normal, None, Unsafety::Unsafe, hir::ImplPolarity::Positive) => { + struct_span_err!( + tcx.sess, + item.span, + E0199, + "implementing the trait `{}` is not unsafe", + trait_ref.print_only_trait_path() + ) + .emit(); + } - ( - Unsafety::Normal, - Some(attr_name), - Unsafety::Normal, - hir::ImplPolarity::Positive, - ) => { - struct_span_err!( - self.tcx.sess, - item.span, - E0569, - "requires an `unsafe impl` declaration due to `#[{}]` attribute", - attr_name - ) - .emit(); - } + (Unsafety::Unsafe, _, Unsafety::Normal, hir::ImplPolarity::Positive) => { + struct_span_err!( + tcx.sess, + item.span, + E0200, + "the trait `{}` requires an `unsafe impl` declaration", + trait_ref.print_only_trait_path() + ) + .emit(); + } - (_, _, Unsafety::Unsafe, hir::ImplPolarity::Negative(_)) => { - // Reported in AST validation - self.tcx.sess.delay_span_bug(item.span, "unsafe negative impl"); - } - (_, _, Unsafety::Normal, hir::ImplPolarity::Negative(_)) - | (Unsafety::Unsafe, _, Unsafety::Unsafe, hir::ImplPolarity::Positive) - | (Unsafety::Normal, Some(_), Unsafety::Unsafe, hir::ImplPolarity::Positive) - | (Unsafety::Normal, None, Unsafety::Normal, _) => { - // OK - } + (Unsafety::Normal, Some(attr_name), Unsafety::Normal, hir::ImplPolarity::Positive) => { + struct_span_err!( + tcx.sess, + item.span, + E0569, + "requires an `unsafe impl` declaration due to `#[{}]` attribute", + attr_name + ) + .emit(); } - } - } -} -impl<'tcx> ItemLikeVisitor<'_> for UnsafetyChecker<'tcx> { - fn visit_item(&mut self, item: &hir::Item<'_>) { - if let hir::ItemKind::Impl(ref impl_) = item.kind { - self.check_unsafety_coherence( - item, - Some(&impl_.generics), - impl_.unsafety, - impl_.polarity, - ); + (_, _, Unsafety::Unsafe, hir::ImplPolarity::Negative(_)) => { + // Reported in AST validation + tcx.sess.delay_span_bug(item.span, "unsafe negative impl"); + } + (_, _, Unsafety::Normal, hir::ImplPolarity::Negative(_)) + | (Unsafety::Unsafe, _, Unsafety::Unsafe, hir::ImplPolarity::Positive) + | (Unsafety::Normal, Some(_), Unsafety::Unsafe, hir::ImplPolarity::Positive) + | (Unsafety::Normal, None, Unsafety::Normal, _) => { + // OK + } } } - - fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'_>) {} - - fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'_>) {} - - fn visit_foreign_item(&mut self, _foreign_item: &hir::ForeignItem<'_>) {} } diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index c1c63c46066..c7823b444bf 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -45,23 +45,21 @@ use rustc_session::lint; use rustc_session::parse::feature_err; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; -use rustc_target::spec::{abi, PanicStrategy, SanitizerSet}; +use rustc_target::spec::{abi, SanitizerSet}; use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName; use std::iter; mod item_bounds; mod type_of; +#[derive(Debug)] struct OnlySelfBounds(bool); /////////////////////////////////////////////////////////////////////////// // Main entry point fn collect_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { - tcx.hir().visit_item_likes_in_module( - module_def_id, - &mut CollectItemTypesVisitor { tcx }.as_deep_visitor(), - ); + tcx.hir().deep_visit_item_likes_in_module(module_def_id, &mut CollectItemTypesVisitor { tcx }); } pub fn provide(providers: &mut Providers) { @@ -112,7 +110,7 @@ pub struct ItemCtxt<'tcx> { /////////////////////////////////////////////////////////////////////////// #[derive(Default)] -crate struct HirPlaceholderCollector(crate Vec<Span>); +pub(crate) struct HirPlaceholderCollector(pub(crate) Vec<Span>); impl<'v> Visitor<'v> for HirPlaceholderCollector { fn visit_ty(&mut self, t: &'v hir::Ty<'v>) { @@ -146,7 +144,7 @@ struct CollectItemTypesVisitor<'tcx> { /// If there are any placeholder types (`_`), emit an error explaining that this is not allowed /// and suggest adding type parameters in the appropriate place, taking into consideration any and /// all already existing generic type parameters to avoid suggesting a name that is already in use. -crate fn placeholder_type_error<'tcx>( +pub(crate) fn placeholder_type_error<'tcx>( tcx: TyCtxt<'tcx>, generics: Option<&hir::Generics<'_>>, placeholder_types: Vec<Span>, @@ -162,7 +160,7 @@ crate fn placeholder_type_error<'tcx>( .emit(); } -crate fn placeholder_type_error_diag<'tcx>( +pub(crate) fn placeholder_type_error_diag<'tcx>( tcx: TyCtxt<'tcx>, generics: Option<&hir::Generics<'_>>, placeholder_types: Vec<Span>, @@ -650,6 +648,7 @@ impl<'tcx> ItemCtxt<'tcx> { /// AST. We do this to avoid having to convert *all* the bounds, which /// would create artificial cycles. Instead, we can only convert the /// bounds for a type parameter `X` if `X::Foo` is used. + #[instrument(level = "trace", skip(self, ast_generics))] fn type_parameter_bounds_in_generics( &self, ast_generics: &'tcx hir::Generics<'tcx>, @@ -659,6 +658,7 @@ impl<'tcx> ItemCtxt<'tcx> { assoc_name: Option<Ident>, ) -> Vec<(ty::Predicate<'tcx>, Span)> { let param_def_id = self.tcx.hir().local_def_id(param_id).to_def_id(); + debug!(?param_def_id); ast_generics .predicates .iter() @@ -676,13 +676,12 @@ impl<'tcx> ItemCtxt<'tcx> { }; let bvars = self.tcx.late_bound_vars(bp.bounded_ty.hir_id); - bp.bounds - .iter() - .filter(|b| match assoc_name { + bp.bounds.iter().filter_map(move |b| bt.map(|bt| (bt, b, bvars))).filter( + |(_, b, _)| match assoc_name { Some(assoc_name) => self.bound_defines_assoc_item(b, assoc_name), None => true, - }) - .filter_map(move |b| bt.map(|bt| (bt, b, bvars))) + }, + ) }) .flat_map(|(bt, b, bvars)| predicates_from_bound(self, bt, b, bvars)) .collect() @@ -1140,6 +1139,7 @@ fn super_predicates_that_define_assoc_type( // Combine the two lists to form the complete set of superbounds: let superbounds = &*tcx.arena.alloc_from_iter(superbounds1.into_iter().chain(superbounds2)); + debug!(?superbounds); // Now require that immediate supertraits are converted, // which will, in turn, reach indirect supertraits. @@ -1197,9 +1197,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> ty::TraitDef { ty::trait_def::TraitSpecializationKind::None }; let must_implement_one_of = tcx - .get_attrs(def_id) - .iter() - .find(|attr| attr.has_name(sym::rustc_must_implement_one_of)) + .get_attr(def_id, sym::rustc_must_implement_one_of) // Check that there are at least 2 arguments of `#[rustc_must_implement_one_of]` // and that they are all identifiers .and_then(|attr| match attr.meta_item_list() { @@ -1558,6 +1556,18 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { Node::Expr(&Expr { kind: ExprKind::ConstBlock(_), .. }) => { Some(tcx.typeck_root_def_id(def_id)) } + // Exclude `GlobalAsm` here which cannot have generics. + Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. }) + if asm.operands.iter().any(|(op, _op_sp)| match op { + hir::InlineAsmOperand::Const { anon_const } + | hir::InlineAsmOperand::SymFn { anon_const } => { + anon_const.hir_id == hir_id + } + _ => false, + }) => + { + Some(parent_def_id.to_def_id()) + } _ => None, } } @@ -2610,7 +2620,6 @@ fn generator_kind(tcx: TyCtxt<'_>, def_id: DefId) -> Option<hir::GeneratorKind> fn from_target_feature( tcx: TyCtxt<'_>, - id: DefId, attr: &ast::Attribute, supported_target_features: &FxHashMap<String, Option<Symbol>>, target_features: &mut Vec<Symbol>, @@ -2679,7 +2688,7 @@ fn from_target_feature( Some(name) => bug!("unknown target feature gate {}", name), None => true, }; - if !allowed && id.is_local() { + if !allowed { feature_err( &tcx.sess.parse_sess, feature_gate.unwrap(), @@ -2693,7 +2702,7 @@ fn from_target_feature( } } -fn linkage_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: &str) -> Linkage { +fn linkage_by_name(tcx: TyCtxt<'_>, def_id: LocalDefId, name: &str) -> Linkage { use rustc_middle::mir::mono::Linkage::*; // Use the names from src/llvm/docs/LangRef.rst here. Most types are only @@ -2716,36 +2725,30 @@ fn linkage_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: &str) -> Linkage { "private" => Private, "weak" => WeakAny, "weak_odr" => WeakODR, - _ => { - let span = tcx.hir().span_if_local(def_id); - if let Some(span) = span { - tcx.sess.span_fatal(span, "invalid linkage specified") - } else { - tcx.sess.fatal(&format!("invalid linkage specified: {}", name)) - } - } + _ => tcx.sess.span_fatal(tcx.def_span(def_id), "invalid linkage specified"), } } -fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { - let attrs = tcx.get_attrs(id); +fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs { + if cfg!(debug_assertions) { + let def_kind = tcx.def_kind(did); + assert!( + def_kind.has_codegen_attrs(), + "unexpected `def_kind` in `codegen_fn_attrs`: {def_kind:?}", + ); + } + let did = did.expect_local(); + let attrs = tcx.hir().attrs(tcx.hir().local_def_id_to_hir_id(did)); let mut codegen_fn_attrs = CodegenFnAttrs::new(); - if tcx.should_inherit_track_caller(id) { + if tcx.should_inherit_track_caller(did) { codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER; } - // With -Z panic-in-drop=abort, drop_in_place never unwinds. - if tcx.sess.opts.debugging_opts.panic_in_drop == PanicStrategy::Abort { - if Some(id) == tcx.lang_items().drop_in_place_fn() { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND; - } - } - // The panic_no_unwind function called by TerminatorKind::Abort will never // unwind. If the panic handler that it invokes unwind then it will simply // call the panic handler again. - if Some(id) == tcx.lang_items().panic_no_unwind() { + if Some(did.to_def_id()) == tcx.lang_items().panic_no_unwind() { codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND; } @@ -2760,7 +2763,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { } else if attr.has_name(sym::rustc_allocator) { codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR; } else if attr.has_name(sym::ffi_returns_twice) { - if tcx.is_foreign_item(id) { + if tcx.is_foreign_item(did) { codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_RETURNS_TWICE; } else { // `#[ffi_returns_twice]` is only allowed `extern fn`s. @@ -2773,7 +2776,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { .emit(); } } else if attr.has_name(sym::ffi_pure) { - if tcx.is_foreign_item(id) { + if tcx.is_foreign_item(did) { if attrs.iter().any(|a| a.has_name(sym::ffi_const)) { // `#[ffi_const]` functions cannot be `#[ffi_pure]` struct_span_err!( @@ -2797,7 +2800,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { .emit(); } } else if attr.has_name(sym::ffi_const) { - if tcx.is_foreign_item(id) { + if tcx.is_foreign_item(did) { codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST; } else { // `#[ffi_const]` is only allowed on foreign functions @@ -2857,7 +2860,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { None => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED, } } else if attr.has_name(sym::cmse_nonsecure_entry) { - if !matches!(tcx.fn_sig(id).abi(), abi::Abi::C { .. }) { + if !matches!(tcx.fn_sig(did).abi(), abi::Abi::C { .. }) { struct_span_err!( tcx.sess, attr.span, @@ -2874,11 +2877,11 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { } else if attr.has_name(sym::thread_local) { codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL; } else if attr.has_name(sym::track_caller) { - if !tcx.is_closure(id) && tcx.fn_sig(id).abi() != abi::Abi::Rust { + if !tcx.is_closure(did.to_def_id()) && tcx.fn_sig(did).abi() != abi::Abi::Rust { struct_span_err!(tcx.sess, attr.span, E0737, "`#[track_caller]` requires Rust ABI") .emit(); } - if tcx.is_closure(id) && !tcx.features().closure_track_caller { + if tcx.is_closure(did.to_def_id()) && !tcx.features().closure_track_caller { feature_err( &tcx.sess.parse_sess, sym::closure_track_caller, @@ -2904,7 +2907,9 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { codegen_fn_attrs.export_name = Some(s); } } else if attr.has_name(sym::target_feature) { - if !tcx.is_closure(id) && tcx.fn_sig(id).unsafety() == hir::Unsafety::Normal { + if !tcx.is_closure(did.to_def_id()) + && tcx.fn_sig(did).unsafety() == hir::Unsafety::Normal + { if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc { // The `#[target_feature]` attribute is allowed on // WebAssembly targets on all functions, including safe @@ -2930,22 +2935,21 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { attr.span, "`#[target_feature(..)]` can only be applied to `unsafe` functions", ); - err.span_label(tcx.def_span(id), "not an `unsafe` function"); + err.span_label(tcx.def_span(did), "not an `unsafe` function"); err.emit(); - } else if let Some(local_id) = id.as_local() { - check_target_feature_trait_unsafe(tcx, local_id, attr.span); + } else { + check_target_feature_trait_unsafe(tcx, did, attr.span); } } from_target_feature( tcx, - id, attr, supported_target_features, &mut codegen_fn_attrs.target_features, ); } else if attr.has_name(sym::linkage) { if let Some(val) = attr.value_str() { - codegen_fn_attrs.linkage = Some(linkage_by_name(tcx, id, val.as_str())); + codegen_fn_attrs.linkage = Some(linkage_by_name(tcx, did, val.as_str())); } } else if attr.has_name(sym::link_section) { if let Some(val) = attr.value_str() { @@ -3161,8 +3165,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { }); // #73631: closures inherit `#[target_feature]` annotations - if tcx.features().target_feature_11 && tcx.is_closure(id) { - let owner_id = tcx.parent(id); + if tcx.features().target_feature_11 && tcx.is_closure(did.to_def_id()) { + let owner_id = tcx.parent(did.to_def_id()); codegen_fn_attrs .target_features .extend(tcx.codegen_fn_attrs(owner_id).target_features.iter().copied()) @@ -3187,7 +3191,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { if !codegen_fn_attrs.no_sanitize.is_empty() { if codegen_fn_attrs.inline == InlineAttr::Always { if let (Some(no_sanitize_span), Some(inline_span)) = (no_sanitize_span, inline_span) { - let hir_id = tcx.hir().local_def_id_to_hir_id(id.expect_local()); + let hir_id = tcx.hir().local_def_id_to_hir_id(did); tcx.struct_span_lint_hir( lint::builtin::INLINE_NO_SANITIZE, hir_id, @@ -3207,7 +3211,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { // strippable by the linker. // // Additionally weak lang items have predetermined symbol names. - if tcx.is_weak_lang_item(id) { + if tcx.is_weak_lang_item(did.to_def_id()) { codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL; } if let Some(name) = weak_lang_items::link_name(attrs) { @@ -3237,19 +3241,22 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { /// Computes the set of target features used in a function for the purposes of /// inline assembly. -fn asm_target_features<'tcx>(tcx: TyCtxt<'tcx>, id: DefId) -> &'tcx FxHashSet<Symbol> { +fn asm_target_features<'tcx>(tcx: TyCtxt<'tcx>, did: DefId) -> &'tcx FxHashSet<Symbol> { let mut target_features = tcx.sess.target_features.clone(); - let attrs = tcx.codegen_fn_attrs(id); - target_features.extend(&attrs.target_features); - match attrs.instruction_set { - None => {} - Some(InstructionSetAttr::ArmA32) => { - target_features.remove(&sym::thumb_mode); - } - Some(InstructionSetAttr::ArmT32) => { - target_features.insert(sym::thumb_mode); + if tcx.def_kind(did).has_codegen_attrs() { + let attrs = tcx.codegen_fn_attrs(did); + target_features.extend(&attrs.target_features); + match attrs.instruction_set { + None => {} + Some(InstructionSetAttr::ArmA32) => { + target_features.remove(&sym::thumb_mode); + } + Some(InstructionSetAttr::ArmT32) => { + target_features.insert(sym::thumb_mode); + } } } + tcx.arena.alloc(target_features) } diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs index 75ad584f419..4b6f80ce57a 100644 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ b/compiler/rustc_typeck/src/collect/type_of.rs @@ -14,6 +14,7 @@ use rustc_span::{Span, DUMMY_SP}; use super::ItemCtxt; use super::{bad_placeholder, is_suggestable_infer_ty}; +use crate::errors::UnconstrainedOpaqueType; /// Computes the relevant generic parameter for a potential generic const argument. /// @@ -531,7 +532,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { /// In particular, definitions of opaque types can only use other generics as arguments, /// and they cannot repeat an argument. Example: /// -/// ```rust +/// ```ignore (illustrative) /// type Foo<A, B> = impl Bar<A, B>; /// /// // Okay -- `Foo` is applied to two distinct, generic types. @@ -682,13 +683,10 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { match locator.found { Some(hidden) => hidden.ty, None => { - let span = tcx.def_span(def_id); - let name = tcx.item_name(tcx.local_parent(def_id).to_def_id()); - let label = format!( - "`{}` must be used in combination with a concrete type within the same module", - name - ); - tcx.sess.struct_span_err(span, "unconstrained opaque type").note(&label).emit(); + tcx.sess.emit_err(UnconstrainedOpaqueType { + span: tcx.def_span(def_id), + name: tcx.item_name(tcx.local_parent(def_id).to_def_id()), + }); tcx.ty_error() } } diff --git a/compiler/rustc_typeck/src/constrained_generic_params.rs b/compiler/rustc_typeck/src/constrained_generic_params.rs index 6f764a952c0..7f2e57e6109 100644 --- a/compiler/rustc_typeck/src/constrained_generic_params.rs +++ b/compiler/rustc_typeck/src/constrained_generic_params.rs @@ -109,9 +109,9 @@ pub fn identify_constrained_generic_params<'tcx>( /// constrained before it is used, if that is possible, and add the /// parameters so constrained to `input_parameters`. For example, /// imagine the following impl: -/// -/// impl<T: Debug, U: Iterator<Item = T>> Trait for U -/// +/// ```ignore (illustrative) +/// impl<T: Debug, U: Iterator<Item = T>> Trait for U +/// ``` /// The impl's predicates are collected from left to right. Ignoring /// the implicit `Sized` bounds, these are /// * T: Debug diff --git a/compiler/rustc_typeck/src/errors.rs b/compiler/rustc_typeck/src/errors.rs index 3d2f93537e4..cd3813ca4f5 100644 --- a/compiler/rustc_typeck/src/errors.rs +++ b/compiler/rustc_typeck/src/errors.rs @@ -1,7 +1,10 @@ //! Errors emitted by typeck. -use rustc_errors::Applicability; +use rustc_errors::{ + error_code, Applicability, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed, +}; use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic}; use rustc_middle::ty::Ty; +use rustc_session::{parse::ParseSess, SessionDiagnostic}; use rustc_span::{symbol::Ident, Span, Symbol}; #[derive(SessionDiagnostic)] @@ -103,6 +106,8 @@ pub struct CopyImplOnNonAdt { pub struct TraitObjectDeclaredWithNoTraits { #[primary_span] pub span: Span, + #[label = "alias-span"] + pub trait_alias_span: Option<Span>, } #[derive(SessionDiagnostic)] @@ -228,3 +233,100 @@ pub enum ExpectedReturnTypeLabel<'tcx> { expected: Ty<'tcx>, }, } + +#[derive(SessionDiagnostic)] +#[error(slug = "typeck-unconstrained-opaque-type")] +#[note] +pub struct UnconstrainedOpaqueType { + #[primary_span] + pub span: Span, + pub name: Symbol, +} + +#[derive(SessionDiagnostic)] +#[error(code = "E0632", slug = "typeck-explicit-generic-args-with-impl-trait")] +#[note] +pub struct ExplicitGenericArgsWithImplTrait { + #[primary_span] + #[label] + pub spans: Vec<Span>, + #[help] + pub is_nightly_build: Option<()>, +} + +pub struct MissingTypeParams { + pub span: Span, + pub def_span: Span, + pub missing_type_params: Vec<String>, + pub empty_generic_args: bool, +} + +// Manual implementation of `SessionDiagnostic` to be able to call `span_to_snippet`. +impl<'a> SessionDiagnostic<'a> for MissingTypeParams { + fn into_diagnostic(self, sess: &'a ParseSess) -> DiagnosticBuilder<'a, ErrorGuaranteed> { + static SLUG: &'static str = "typeck-missing-type-params"; + let mut err = sess.span_diagnostic.struct_span_err_with_code( + self.span, + DiagnosticMessage::fluent(SLUG), + error_code!(E0393), + ); + err.set_arg("parameterCount", self.missing_type_params.len()); + err.set_arg( + "parameters", + self.missing_type_params + .iter() + .map(|n| format!("`{}`", n)) + .collect::<Vec<_>>() + .join(", "), + ); + + err.span_label(self.def_span, DiagnosticMessage::fluent_attr(SLUG, "label")); + + let mut suggested = false; + if let (Ok(snippet), true) = ( + sess.source_map().span_to_snippet(self.span), + // Don't suggest setting the type params if there are some already: the order is + // tricky to get right and the user will already know what the syntax is. + self.empty_generic_args, + ) { + if snippet.ends_with('>') { + // The user wrote `Trait<'a, T>` or similar. To provide an accurate suggestion + // we would have to preserve the right order. For now, as clearly the user is + // aware of the syntax, we do nothing. + } 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<Type>`. + err.span_suggestion( + self.span, + DiagnosticMessage::fluent_attr(SLUG, "suggestion"), + format!("{}<{}>", snippet, self.missing_type_params.join(", ")), + Applicability::HasPlaceholders, + ); + suggested = true; + } + } + if !suggested { + err.span_label(self.span, DiagnosticMessage::fluent_attr(SLUG, "no-suggestion-label")); + } + + err.note(DiagnosticMessage::fluent_attr(SLUG, "note")); + err + } +} + +#[derive(SessionDiagnostic)] +#[error(code = "E0183", slug = "typeck-manual-implementation")] +#[help] +pub struct ManualImplementation { + #[primary_span] + #[label] + pub span: Span, + pub trait_name: String, +} + +#[derive(SessionDiagnostic)] +#[error(slug = "typeck-substs-on-overridden-impl")] +pub struct SubstsOnOverriddenImpl { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs index 2bcf2d3b2ed..3ebb1dd83e1 100644 --- a/compiler/rustc_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_typeck/src/expr_use_visitor.rs @@ -17,6 +17,7 @@ use rustc_middle::hir::place::ProjectionKind; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, adjustment, AdtKind, Ty, TyCtxt}; use rustc_target::abi::VariantIdx; +use ty::BorrowKind::ImmBorrow; use crate::mem_categorization as mc; @@ -69,7 +70,12 @@ pub trait Delegate<'tcx> { } /// The `place` should be a fake read because of specified `cause`. - fn fake_read(&mut self, place: Place<'tcx>, cause: FakeReadCause, diag_expr_id: hir::HirId); + fn fake_read( + &mut self, + place_with_id: &PlaceWithHirId<'tcx>, + cause: FakeReadCause, + diag_expr_id: hir::HirId, + ); } #[derive(Copy, Clone, PartialEq, Debug)] @@ -327,7 +333,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { }; self.delegate.fake_read( - discr_place.place.clone(), + &discr_place, FakeReadCause::ForMatchedPlace(closure_def_id), discr_place.hir_id, ); @@ -617,16 +623,16 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { }; self.delegate.fake_read( - discr_place.place.clone(), + discr_place, FakeReadCause::ForMatchedPlace(closure_def_id), discr_place.hir_id, ); - self.walk_pat(discr_place, arm.pat); + self.walk_pat(discr_place, arm.pat, arm.guard.is_some()); if let Some(hir::Guard::If(e)) = arm.guard { self.consume_expr(e) - } else if let Some(hir::Guard::IfLet(_, ref e)) = arm.guard { - self.consume_expr(e) + } else if let Some(hir::Guard::IfLet(ref l)) = arm.guard { + self.consume_expr(l.init) } self.consume_expr(arm.body); @@ -641,16 +647,21 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { }; self.delegate.fake_read( - discr_place.place.clone(), + discr_place, FakeReadCause::ForLet(closure_def_id), discr_place.hir_id, ); - self.walk_pat(discr_place, pat); + self.walk_pat(discr_place, pat, false); } /// The core driver for walking a pattern - fn walk_pat(&mut self, discr_place: &PlaceWithHirId<'tcx>, pat: &hir::Pat<'_>) { - debug!("walk_pat(discr_place={:?}, pat={:?})", discr_place, pat); + fn walk_pat( + &mut self, + discr_place: &PlaceWithHirId<'tcx>, + pat: &hir::Pat<'_>, + has_guard: bool, + ) { + debug!("walk_pat(discr_place={:?}, pat={:?}, has_guard={:?})", discr_place, pat, has_guard); let tcx = self.tcx(); let ExprUseVisitor { ref mc, body_owner: _, ref mut delegate } = *self; @@ -671,6 +682,13 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { delegate.bind(binding_place, binding_place.hir_id); } + // Subtle: MIR desugaring introduces immutable borrows for each pattern + // binding when lowering pattern guards to ensure that the guard does not + // modify the scrutinee. + if has_guard { + delegate.borrow(place, discr_place.hir_id, ImmBorrow); + } + // It is also a borrow or copy/move of the value being matched. // In a cases of pattern like `let pat = upvar`, don't use the span // of the pattern, as this just looks confusing, instead use the span @@ -698,12 +716,13 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { /// In the following example the closures `c` only captures `p.x` even though `incr` /// is a capture of the nested closure /// - /// ```rust,ignore(cannot-test-this-because-pseudo-code) - /// let p = ..; + /// ``` + /// struct P { x: i32 } + /// let mut p = P { x: 4 }; /// let c = || { /// let incr = 10; /// let nested = || p.x += incr; - /// } + /// }; /// ``` /// /// - When reporting the Place back to the Delegate, ensure that the UpvarId uses the enclosing @@ -763,7 +782,11 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { ); } }; - self.delegate.fake_read(fake_read.clone(), *cause, *hir_id); + self.delegate.fake_read( + &PlaceWithHirId { place: fake_read.clone(), hir_id: *hir_id }, + *cause, + *hir_id, + ); } } diff --git a/compiler/rustc_typeck/src/impl_wf_check.rs b/compiler/rustc_typeck/src/impl_wf_check.rs index 8b376e26dee..c089d25d222 100644 --- a/compiler/rustc_typeck/src/impl_wf_check.rs +++ b/compiler/rustc_typeck/src/impl_wf_check.rs @@ -14,8 +14,8 @@ use min_specialization::check_min_specialization; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::struct_span_err; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; -use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, TyCtxt, TypeFoldable}; use rustc_span::Span; @@ -63,35 +63,23 @@ pub fn impl_wf_check(tcx: TyCtxt<'_>) { fn check_mod_impl_wf(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { let min_specialization = tcx.features().min_specialization; - tcx.hir() - .visit_item_likes_in_module(module_def_id, &mut ImplWfCheck { tcx, min_specialization }); -} - -pub fn provide(providers: &mut Providers) { - *providers = Providers { check_mod_impl_wf, ..*providers }; -} - -struct ImplWfCheck<'tcx> { - tcx: TyCtxt<'tcx>, - min_specialization: bool, -} - -impl<'tcx> ItemLikeVisitor<'tcx> for ImplWfCheck<'tcx> { - fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { - if let hir::ItemKind::Impl(ref impl_) = item.kind { - enforce_impl_params_are_constrained(self.tcx, item.def_id, impl_.items); - enforce_impl_items_are_distinct(self.tcx, impl_.items); - if self.min_specialization { - check_min_specialization(self.tcx, item.def_id.to_def_id(), item.span); + let module = tcx.hir_module_items(module_def_id); + for id in module.items() { + if matches!(tcx.def_kind(id.def_id), DefKind::Impl) { + let item = tcx.hir().item(id); + if let hir::ItemKind::Impl(ref impl_) = item.kind { + enforce_impl_params_are_constrained(tcx, item.def_id, impl_.items); + enforce_impl_items_are_distinct(tcx, impl_.items); + if min_specialization { + check_min_specialization(tcx, item.def_id.to_def_id(), item.span); + } } } } +} - fn visit_trait_item(&mut self, _trait_item: &'tcx hir::TraitItem<'tcx>) {} - - fn visit_impl_item(&mut self, _impl_item: &'tcx hir::ImplItem<'tcx>) {} - - fn visit_foreign_item(&mut self, _foreign_item: &'tcx hir::ForeignItem<'tcx>) {} +pub fn provide(providers: &mut Providers) { + *providers = Providers { check_mod_impl_wf, ..*providers }; } fn enforce_impl_params_are_constrained( diff --git a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs index 6cef3e9d940..bb97d00be32 100644 --- a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs @@ -29,7 +29,7 @@ //! //! Suppose we have the following always applicable impl: //! -//! ```rust +//! ```ignore (illustrative) //! impl<T> SpecExtend<T> for std::vec::IntoIter<T> { /* specialized impl */ } //! impl<T, I: Iterator<Item=T>> SpecExtend<T> for I { /* default impl */ } //! ``` @@ -66,6 +66,7 @@ //! on traits with methods can. use crate::constrained_generic_params as cgp; +use crate::errors::SubstsOnOverriddenImpl; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -165,7 +166,7 @@ fn get_impl_substs<'tcx>( let outlives_env = OutlivesEnvironment::new(ty::ParamEnv::empty()); infcx.resolve_regions_and_report_errors(impl1_def_id, &outlives_env, RegionckMode::default()); let Ok(impl2_substs) = infcx.fully_resolve(impl2_substs) else { - tcx.sess.struct_span_err(span, "could not resolve substs on overridden impl").emit(); + tcx.sess.emit_err(SubstsOnOverriddenImpl { span }); return None; }; Some((impl1_substs, impl2_substs)) diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs index fe285820ba6..4ffd199b133 100644 --- a/compiler/rustc_typeck/src/lib.rs +++ b/compiler/rustc_typeck/src/lib.rs @@ -59,7 +59,6 @@ This API is completely unstable and subject to change. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(box_patterns)] #![feature(control_flow_enum)] -#![feature(crate_visibility_modifier)] #![feature(drain_filter)] #![feature(hash_drain_filter)] #![feature(if_let_guard)] @@ -298,17 +297,12 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { error = true; } - for attr in tcx.get_attrs(main_def_id) { - if attr.has_name(sym::track_caller) { - tcx.sess - .struct_span_err( - attr.span, - "`main` function is not allowed to be `#[track_caller]`", - ) - .span_label(main_span, "`main` function is not allowed to be `#[track_caller]`") - .emit(); - error = true; - } + for attr in tcx.get_attrs(main_def_id, sym::track_caller) { + tcx.sess + .struct_span_err(attr.span, "`main` function is not allowed to be `#[track_caller]`") + .span_label(main_span, "`main` function is not allowed to be `#[track_caller]`") + .emit(); + error = true; } if error { diff --git a/compiler/rustc_typeck/src/mem_categorization.rs b/compiler/rustc_typeck/src/mem_categorization.rs index 85e9a670ffb..21916352532 100644 --- a/compiler/rustc_typeck/src/mem_categorization.rs +++ b/compiler/rustc_typeck/src/mem_categorization.rs @@ -9,12 +9,12 @@ //! expressions of the following forms (the actual enum has many more //! possibilities, naturally, but they are all variants of these base //! forms): -//! -//! E = rvalue // some computed rvalue -//! | x // address of a local variable or argument -//! | *E // deref of a ptr -//! | E.comp // access to an interior component -//! +//! ```ignore (not-rust) +//! E = rvalue // some computed rvalue +//! | x // address of a local variable or argument +//! | *E // deref of a ptr +//! | E.comp // access to an interior component +//! ``` //! Imagine a routine ToAddr(Expr) that evaluates an expression and returns an //! address where the result is to be found. If Expr is a place, then this //! is the address of the place. If `Expr` is an rvalue, this is the address of @@ -65,7 +65,7 @@ use rustc_span::Span; use rustc_target::abi::VariantIdx; use rustc_trait_selection::infer::InferCtxtExt; -crate trait HirNode { +pub(crate) trait HirNode { fn hir_id(&self) -> hir::HirId; fn span(&self) -> Span; } @@ -89,19 +89,19 @@ impl HirNode for hir::Pat<'_> { } #[derive(Clone)] -crate struct MemCategorizationContext<'a, 'tcx> { - crate typeck_results: &'a ty::TypeckResults<'tcx>, +pub(crate) struct MemCategorizationContext<'a, 'tcx> { + pub(crate) typeck_results: &'a ty::TypeckResults<'tcx>, infcx: &'a InferCtxt<'a, 'tcx>, param_env: ty::ParamEnv<'tcx>, body_owner: LocalDefId, upvars: Option<&'tcx FxIndexMap<hir::HirId, hir::Upvar>>, } -crate type McResult<T> = Result<T, ()>; +pub(crate) type McResult<T> = Result<T, ()>; impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { /// Creates a `MemCategorizationContext`. - crate fn new( + pub(crate) fn new( infcx: &'a InferCtxt<'a, 'tcx>, param_env: ty::ParamEnv<'tcx>, body_owner: LocalDefId, @@ -116,11 +116,11 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { } } - crate fn tcx(&self) -> TyCtxt<'tcx> { + pub(crate) fn tcx(&self) -> TyCtxt<'tcx> { self.infcx.tcx } - crate fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>, span: Span) -> bool { + pub(crate) fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>, span: Span) -> bool { self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span) } @@ -162,7 +162,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { } } - crate fn node_ty(&self, hir_id: hir::HirId) -> McResult<Ty<'tcx>> { + pub(crate) fn node_ty(&self, hir_id: hir::HirId) -> McResult<Ty<'tcx>> { self.resolve_type_vars_or_error(hir_id, self.typeck_results.node_type_opt(hir_id)) } @@ -170,7 +170,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { self.resolve_type_vars_or_error(expr.hir_id, self.typeck_results.expr_ty_opt(expr)) } - crate fn expr_ty_adjusted(&self, expr: &hir::Expr<'_>) -> McResult<Ty<'tcx>> { + pub(crate) fn expr_ty_adjusted(&self, expr: &hir::Expr<'_>) -> McResult<Ty<'tcx>> { self.resolve_type_vars_or_error(expr.hir_id, self.typeck_results.expr_ty_adjusted_opt(expr)) } @@ -184,7 +184,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { /// implicit deref patterns attached (e.g., it is really /// `&Some(x)`). In that case, we return the "outermost" type /// (e.g., `&Option<T>). - crate fn pat_ty_adjusted(&self, pat: &hir::Pat<'_>) -> McResult<Ty<'tcx>> { + pub(crate) fn pat_ty_adjusted(&self, pat: &hir::Pat<'_>) -> McResult<Ty<'tcx>> { // Check for implicit `&` types wrapping the pattern; note // that these are never attached to binding patterns, so // actually this is somewhat "disjoint" from the code below @@ -236,7 +236,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { Ok(ret_ty) } - crate fn cat_expr(&self, expr: &hir::Expr<'_>) -> McResult<PlaceWithHirId<'tcx>> { + pub(crate) fn cat_expr(&self, expr: &hir::Expr<'_>) -> McResult<PlaceWithHirId<'tcx>> { // This recursion helper avoids going through *too many* // adjustments, since *only* non-overloaded deref recurses. fn helper<'a, 'tcx>( @@ -255,7 +255,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { helper(self, expr, self.typeck_results.expr_adjustments(expr)) } - crate fn cat_expr_adjusted( + pub(crate) fn cat_expr_adjusted( &self, expr: &hir::Expr<'_>, previous: PlaceWithHirId<'tcx>, @@ -298,7 +298,10 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { } } - crate fn cat_expr_unadjusted(&self, expr: &hir::Expr<'_>) -> McResult<PlaceWithHirId<'tcx>> { + pub(crate) fn cat_expr_unadjusted( + &self, + expr: &hir::Expr<'_>, + ) -> McResult<PlaceWithHirId<'tcx>> { debug!("cat_expr: id={} expr={:?}", expr.hir_id, expr); let expr_ty = self.expr_ty(expr)?; @@ -383,7 +386,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { } } - crate fn cat_res( + pub(crate) fn cat_res( &self, hir_id: hir::HirId, span: Span, @@ -440,7 +443,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { Ok(ret) } - crate fn cat_rvalue( + pub(crate) fn cat_rvalue( &self, hir_id: hir::HirId, span: Span, @@ -452,7 +455,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { ret } - crate fn cat_projection<N: HirNode>( + pub(crate) fn cat_projection<N: HirNode>( &self, node: &N, base_place: PlaceWithHirId<'tcx>, @@ -521,7 +524,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { Ok(ret) } - crate fn cat_pattern<F>( + pub(crate) fn cat_pattern<F>( &self, place: PlaceWithHirId<'tcx>, pat: &hir::Pat<'_>, diff --git a/compiler/rustc_typeck/src/outlives/implicit_infer.rs b/compiler/rustc_typeck/src/outlives/implicit_infer.rs index 6f842c6e71a..52f9e386441 100644 --- a/compiler/rustc_typeck/src/outlives/implicit_infer.rs +++ b/compiler/rustc_typeck/src/outlives/implicit_infer.rs @@ -2,7 +2,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst}; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt}; use rustc_span::Span; use super::explicit::ExplicitPredicatesMap; @@ -35,7 +35,7 @@ pub fn infer_predicates<'tcx>( debug!("InferVisitor::visit_item(item={:?})", item_did); let mut item_required_predicates = RequiredPredicates::default(); - match tcx.hir().def_kind(item_did) { + match tcx.def_kind(item_did) { DefKind::Union | DefKind::Enum | DefKind::Struct => { let adt_def = tcx.adt_def(item_did.to_def_id()); @@ -137,7 +137,7 @@ fn insert_required_predicates_to_be_wf<'tcx>( // `unsubstituted_predicate` is `U: 'b` in the // example above. So apply the substitution to // get `T: 'a` (or `predicate`): - let predicate = unsubstituted_predicate.subst(tcx, substs); + let predicate = EarlyBinder(*unsubstituted_predicate).subst(tcx, substs); insert_outlives_predicate( tcx, predicate.0, @@ -211,15 +211,15 @@ fn insert_required_predicates_to_be_wf<'tcx>( /// We also have to check the explicit predicates /// declared on the type. +/// ```ignore (illustrative) +/// struct Foo<'a, T> { +/// field1: Bar<T> +/// } /// -/// struct Foo<'a, T> { -/// field1: Bar<T> -/// } -/// -/// struct Bar<U> where U: 'static, U: Foo { -/// ... -/// } -/// +/// struct Bar<U> where U: 'static, U: Foo { +/// ... +/// } +/// ``` /// Here, we should fetch the explicit predicates, which /// will give us `U: 'static` and `U: Foo`. The latter we /// can ignore, but we will want to process `U: 'static`, @@ -287,7 +287,7 @@ pub fn check_explicit_predicates<'tcx>( continue; } - let predicate = outlives_predicate.subst(tcx, substs); + let predicate = EarlyBinder(*outlives_predicate).subst(tcx, substs); debug!("predicate = {:?}", &predicate); insert_outlives_predicate(tcx, predicate.0, predicate.1, span, required_predicates); } diff --git a/compiler/rustc_typeck/src/outlives/mod.rs b/compiler/rustc_typeck/src/outlives/mod.rs index 139be8a42de..dccfee19960 100644 --- a/compiler/rustc_typeck/src/outlives/mod.rs +++ b/compiler/rustc_typeck/src/outlives/mod.rs @@ -9,7 +9,7 @@ use rustc_span::Span; mod explicit; mod implicit_infer; -crate mod outlives_bounds; +pub(crate) mod outlives_bounds; /// Code to write unit test for outlives. pub mod test; mod utils; diff --git a/compiler/rustc_typeck/src/outlives/outlives_bounds.rs b/compiler/rustc_typeck/src/outlives/outlives_bounds.rs index 435df9c00f4..87f844fafe6 100644 --- a/compiler/rustc_typeck/src/outlives/outlives_bounds.rs +++ b/compiler/rustc_typeck/src/outlives/outlives_bounds.rs @@ -26,9 +26,9 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> { /// calling that fn, and hence the *callee* can assume that its /// argument types are well-formed. This may imply certain relationships /// between generic parameters. For example: - /// - /// fn foo<'a,T>(x: &'a T) - /// + /// ``` + /// fn foo<'a,T>(x: &'a T) {} + /// ``` /// can only be called with a `'a` and `T` such that `&'a T` is WF. /// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`. /// diff --git a/compiler/rustc_typeck/src/outlives/test.rs b/compiler/rustc_typeck/src/outlives/test.rs index b3efd9f9ec3..eb0e1203405 100644 --- a/compiler/rustc_typeck/src/outlives/test.rs +++ b/compiler/rustc_typeck/src/outlives/test.rs @@ -1,28 +1,21 @@ use rustc_errors::struct_span_err; -use rustc_hir as hir; -use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_middle::ty::TyCtxt; use rustc_span::symbol::sym; pub fn test_inferred_outlives(tcx: TyCtxt<'_>) { - tcx.hir().visit_all_item_likes(&mut OutlivesTest { tcx }); -} - -struct OutlivesTest<'tcx> { - tcx: TyCtxt<'tcx>, -} - -impl<'tcx> ItemLikeVisitor<'tcx> for OutlivesTest<'tcx> { - fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { + for id in tcx.hir().items() { // For unit testing: check for a special "rustc_outlives" // attribute and report an error with various results if found. - if self.tcx.has_attr(item.def_id.to_def_id(), sym::rustc_outlives) { - let inferred_outlives_of = self.tcx.inferred_outlives_of(item.def_id); - struct_span_err!(self.tcx.sess, item.span, E0640, "{:?}", inferred_outlives_of).emit(); + if tcx.has_attr(id.def_id.to_def_id(), sym::rustc_outlives) { + let inferred_outlives_of = tcx.inferred_outlives_of(id.def_id); + struct_span_err!( + tcx.sess, + tcx.def_span(id.def_id), + E0640, + "{:?}", + inferred_outlives_of + ) + .emit(); } } - - fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem<'tcx>) {} - fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem<'tcx>) {} - fn visit_foreign_item(&mut self, _: &'tcx hir::ForeignItem<'tcx>) {} } diff --git a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs index 5cd7a7d578e..f1dc3cbbac4 100644 --- a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs @@ -15,28 +15,28 @@ use GenericArgsInfo::*; /// Handles the `wrong number of type / lifetime / ... arguments` family of error messages. pub struct WrongNumberOfGenericArgs<'a, 'tcx> { - crate tcx: TyCtxt<'tcx>, + pub(crate) tcx: TyCtxt<'tcx>, - crate angle_brackets: AngleBrackets, + pub(crate) angle_brackets: AngleBrackets, - crate gen_args_info: GenericArgsInfo, + pub(crate) gen_args_info: GenericArgsInfo, /// Offending path segment - crate path_segment: &'a hir::PathSegment<'a>, + pub(crate) path_segment: &'a hir::PathSegment<'a>, /// Generic parameters as expected by type or trait - crate gen_params: &'a ty::Generics, + pub(crate) gen_params: &'a ty::Generics, /// Index offset into parameters. Depends on whether `Self` is included and on /// number of lifetime parameters in case we're processing missing or redundant /// type or constant arguments. - crate params_offset: usize, + pub(crate) params_offset: usize, /// Generic arguments as provided by user - crate gen_args: &'a hir::GenericArgs<'a>, + pub(crate) gen_args: &'a hir::GenericArgs<'a>, /// DefId of the generic type - crate def_id: DefId, + pub(crate) def_id: DefId, } // Provides information about the kind of arguments that were provided for diff --git a/compiler/rustc_typeck/src/variance/constraints.rs b/compiler/rustc_typeck/src/variance/constraints.rs index 76755de4964..690c362d853 100644 --- a/compiler/rustc_typeck/src/variance/constraints.rs +++ b/compiler/rustc_typeck/src/variance/constraints.rs @@ -5,7 +5,7 @@ use hir::def_id::{DefId, LocalDefId}; use rustc_hir as hir; -use rustc_hir::itemlikevisit::ItemLikeVisitor; +use rustc_hir::def::DefKind; use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -34,11 +34,11 @@ pub struct Constraint<'a> { /// To build constraints, we visit one item (type, trait) at a time /// and look at its contents. So e.g., if we have -/// -/// struct Foo<T> { -/// b: Bar<T> -/// } -/// +/// ```ignore (illustrative) +/// struct Foo<T> { +/// b: Bar<T> +/// } +/// ``` /// then while we are visiting `Bar<T>`, the `CurrentItem` would have /// the `DefId` and the start of `Foo`'s inferreds. pub struct CurrentItem { @@ -62,61 +62,71 @@ pub fn add_constraints_from_crate<'a, 'tcx>( constraints: Vec::new(), }; - tcx.hir().visit_all_item_likes(&mut constraint_cx); + let crate_items = tcx.hir_crate_items(()); + + for id in crate_items.items() { + constraint_cx.check_item(id); + } + + for id in crate_items.trait_items() { + if let DefKind::AssocFn = tcx.def_kind(id.def_id) { + constraint_cx.check_node_helper(id.hir_id()); + } + } + + for id in crate_items.impl_items() { + if let DefKind::AssocFn = tcx.def_kind(id.def_id) { + constraint_cx.check_node_helper(id.hir_id()); + } + } + + for id in crate_items.foreign_items() { + if let DefKind::Fn = tcx.def_kind(id.def_id) { + constraint_cx.check_node_helper(id.hir_id()); + } + } constraint_cx } -impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for ConstraintContext<'a, 'tcx> { - fn visit_item(&mut self, item: &hir::Item<'_>) { - match item.kind { - hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => { - self.visit_node_helper(item.hir_id()); - - if let hir::VariantData::Tuple(..) = *struct_def { - self.visit_node_helper(struct_def.ctor_hir_id().unwrap()); +impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { + fn check_item(&mut self, id: hir::ItemId) { + let def_kind = self.tcx().def_kind(id.def_id); + match def_kind { + DefKind::Struct | DefKind::Union => { + let item = self.tcx().hir().item(id); + + if let hir::ItemKind::Struct(ref struct_def, _) + | hir::ItemKind::Union(ref struct_def, _) = item.kind + { + self.check_node_helper(item.hir_id()); + + if let hir::VariantData::Tuple(..) = *struct_def { + self.check_node_helper(struct_def.ctor_hir_id().unwrap()); + } } } + DefKind::Enum => { + let item = self.tcx().hir().item(id); - hir::ItemKind::Enum(ref enum_def, _) => { - self.visit_node_helper(item.hir_id()); + if let hir::ItemKind::Enum(ref enum_def, _) = item.kind { + self.check_node_helper(item.hir_id()); - for variant in enum_def.variants { - if let hir::VariantData::Tuple(..) = variant.data { - self.visit_node_helper(variant.data.ctor_hir_id().unwrap()); + for variant in enum_def.variants { + if let hir::VariantData::Tuple(..) = variant.data { + self.check_node_helper(variant.data.ctor_hir_id().unwrap()); + } } } } - - hir::ItemKind::Fn(..) => { - self.visit_node_helper(item.hir_id()); + DefKind::Fn => { + self.check_node_helper(id.hir_id()); } - _ => {} } } - fn visit_trait_item(&mut self, trait_item: &hir::TraitItem<'_>) { - if let hir::TraitItemKind::Fn(..) = trait_item.kind { - self.visit_node_helper(trait_item.hir_id()); - } - } - - fn visit_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) { - if let hir::ImplItemKind::Fn(..) = impl_item.kind { - self.visit_node_helper(impl_item.hir_id()); - } - } - - fn visit_foreign_item(&mut self, foreign_item: &hir::ForeignItem<'_>) { - if let hir::ForeignItemKind::Fn(..) = foreign_item.kind { - self.visit_node_helper(foreign_item.hir_id()); - } - } -} - -impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { - fn visit_node_helper(&mut self, id: hir::HirId) { + fn check_node_helper(&mut self, id: hir::HirId) { let tcx = self.terms_cx.tcx; let def_id = tcx.hir().local_def_id(id); self.build_constraints_for_item(def_id); diff --git a/compiler/rustc_typeck/src/variance/terms.rs b/compiler/rustc_typeck/src/variance/terms.rs index 36fbfc21ff5..ab64befe5dc 100644 --- a/compiler/rustc_typeck/src/variance/terms.rs +++ b/compiler/rustc_typeck/src/variance/terms.rs @@ -11,7 +11,7 @@ use rustc_arena::DroplessArena; use rustc_hir as hir; -use rustc_hir::itemlikevisit::ItemLikeVisitor; +use rustc_hir::def::DefKind; use rustc_hir::HirIdMap; use rustc_middle::ty::{self, TyCtxt}; use std::fmt; @@ -79,7 +79,29 @@ pub fn determine_parameters_to_be_inferred<'a, 'tcx>( // // - https://rustc-dev-guide.rust-lang.org/query.html // - https://rustc-dev-guide.rust-lang.org/variance.html - tcx.hir().visit_all_item_likes(&mut terms_cx); + let crate_items = tcx.hir_crate_items(()); + + for id in crate_items.items() { + terms_cx.check_item(id); + } + + for id in crate_items.trait_items() { + if let DefKind::AssocFn = tcx.def_kind(id.def_id) { + terms_cx.add_inferreds_for_item(id.hir_id()); + } + } + + for id in crate_items.impl_items() { + if let DefKind::AssocFn = tcx.def_kind(id.def_id) { + terms_cx.add_inferreds_for_item(id.hir_id()); + } + } + + for id in crate_items.foreign_items() { + if let DefKind::Fn = tcx.def_kind(id.def_id) { + terms_cx.add_inferreds_for_item(id.hir_id()); + } + } terms_cx } @@ -124,54 +146,42 @@ impl<'a, 'tcx> TermsContext<'a, 'tcx> { (start..(start + count)).map(|i| &*arena.alloc(InferredTerm(InferredIndex(i)))), ); } -} -impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for TermsContext<'a, 'tcx> { - fn visit_item(&mut self, item: &hir::Item<'_>) { - debug!("add_inferreds for item {}", self.tcx.hir().node_to_string(item.hir_id())); + fn check_item(&mut self, id: hir::ItemId) { + debug!("add_inferreds for item {}", self.tcx.hir().node_to_string(id.hir_id())); + + let def_kind = self.tcx.def_kind(id.def_id); + match def_kind { + DefKind::Struct | DefKind::Union => { + let item = self.tcx.hir().item(id); - match item.kind { - hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => { - self.add_inferreds_for_item(item.hir_id()); + if let hir::ItemKind::Struct(ref struct_def, _) + | hir::ItemKind::Union(ref struct_def, _) = item.kind + { + self.add_inferreds_for_item(item.hir_id()); - if let hir::VariantData::Tuple(..) = *struct_def { - self.add_inferreds_for_item(struct_def.ctor_hir_id().unwrap()); + if let hir::VariantData::Tuple(..) = *struct_def { + self.add_inferreds_for_item(struct_def.ctor_hir_id().unwrap()); + } } } + DefKind::Enum => { + let item = self.tcx.hir().item(id); - hir::ItemKind::Enum(ref enum_def, _) => { - self.add_inferreds_for_item(item.hir_id()); + if let hir::ItemKind::Enum(ref enum_def, _) = item.kind { + self.add_inferreds_for_item(item.hir_id()); - for variant in enum_def.variants { - if let hir::VariantData::Tuple(..) = variant.data { - self.add_inferreds_for_item(variant.data.ctor_hir_id().unwrap()); + for variant in enum_def.variants { + if let hir::VariantData::Tuple(..) = variant.data { + self.add_inferreds_for_item(variant.data.ctor_hir_id().unwrap()); + } } } } - - hir::ItemKind::Fn(..) => { - self.add_inferreds_for_item(item.hir_id()); + DefKind::Fn => { + self.add_inferreds_for_item(id.hir_id()); } - _ => {} } } - - fn visit_trait_item(&mut self, trait_item: &hir::TraitItem<'_>) { - if let hir::TraitItemKind::Fn(..) = trait_item.kind { - self.add_inferreds_for_item(trait_item.hir_id()); - } - } - - fn visit_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) { - if let hir::ImplItemKind::Fn(..) = impl_item.kind { - self.add_inferreds_for_item(impl_item.hir_id()); - } - } - - fn visit_foreign_item(&mut self, foreign_item: &hir::ForeignItem<'_>) { - if let hir::ForeignItemKind::Fn(..) = foreign_item.kind { - self.add_inferreds_for_item(foreign_item.hir_id()); - } - } } diff --git a/compiler/rustc_typeck/src/variance/test.rs b/compiler/rustc_typeck/src/variance/test.rs index d6959075d88..2ba87db880b 100644 --- a/compiler/rustc_typeck/src/variance/test.rs +++ b/compiler/rustc_typeck/src/variance/test.rs @@ -1,28 +1,14 @@ use rustc_errors::struct_span_err; -use rustc_hir as hir; -use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_middle::ty::TyCtxt; use rustc_span::symbol::sym; pub fn test_variance(tcx: TyCtxt<'_>) { - tcx.hir().visit_all_item_likes(&mut VarianceTest { tcx }); -} - -struct VarianceTest<'tcx> { - tcx: TyCtxt<'tcx>, -} - -impl<'tcx> ItemLikeVisitor<'tcx> for VarianceTest<'tcx> { - fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { - // For unit testing: check for a special "rustc_variance" - // attribute and report an error with various results if found. - if self.tcx.has_attr(item.def_id.to_def_id(), sym::rustc_variance) { - let variances_of = self.tcx.variances_of(item.def_id); - struct_span_err!(self.tcx.sess, item.span, E0208, "{:?}", variances_of).emit(); + // For unit testing: check for a special "rustc_variance" + // attribute and report an error with various results if found. + for id in tcx.hir().items() { + if tcx.has_attr(id.def_id.to_def_id(), sym::rustc_variance) { + let variances_of = tcx.variances_of(id.def_id); + struct_span_err!(tcx.sess, tcx.def_span(id.def_id), E0208, "{:?}", variances_of).emit(); } } - - fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem<'tcx>) {} - fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem<'tcx>) {} - fn visit_foreign_item(&mut self, _: &'tcx hir::ForeignItem<'tcx>) {} } |
