diff options
Diffstat (limited to 'compiler')
390 files changed, 9578 insertions, 7807 deletions
diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 988918b0505..86af7769d1b 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -8,7 +8,7 @@ use crate::ast::{Path, PathSegment}; use crate::ptr::P; use crate::token::{self, CommentKind, Delimiter, Token}; use crate::tokenstream::{AttrAnnotatedTokenStream, AttrAnnotatedTokenTree}; -use crate::tokenstream::{DelimSpan, Spacing, TokenTree, TreeAndSpacing}; +use crate::tokenstream::{DelimSpan, Spacing, TokenTree}; use crate::tokenstream::{LazyTokenStream, TokenStream}; use crate::util::comments; @@ -388,7 +388,7 @@ pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool { } impl MetaItem { - fn token_trees_and_spacings(&self) -> Vec<TreeAndSpacing> { + fn token_trees(&self) -> Vec<TokenTree> { let mut idents = vec![]; let mut last_pos = BytePos(0_u32); for (i, segment) in self.path.segments.iter().enumerate() { @@ -396,12 +396,12 @@ impl MetaItem { if !is_first { let mod_sep_span = Span::new(last_pos, segment.ident.span.lo(), segment.ident.span.ctxt(), None); - idents.push(TokenTree::token(token::ModSep, mod_sep_span).into()); + idents.push(TokenTree::token_alone(token::ModSep, mod_sep_span)); } - idents.push(TokenTree::Token(Token::from_ast_ident(segment.ident)).into()); + idents.push(TokenTree::Token(Token::from_ast_ident(segment.ident), Spacing::Alone)); last_pos = segment.ident.span.hi(); } - idents.extend(self.kind.token_trees_and_spacings(self.span)); + idents.extend(self.kind.token_trees(self.span)); idents } @@ -411,12 +411,13 @@ impl MetaItem { { // FIXME: Share code with `parse_path`. let path = match tokens.next().map(TokenTree::uninterpolate) { - Some(TokenTree::Token(Token { - kind: kind @ (token::Ident(..) | token::ModSep), - span, - })) => 'arm: { + Some(TokenTree::Token( + Token { kind: kind @ (token::Ident(..) | token::ModSep), span }, + _, + )) => 'arm: { let mut segments = if let token::Ident(name, _) = kind { - if let Some(TokenTree::Token(Token { kind: token::ModSep, .. })) = tokens.peek() + if let Some(TokenTree::Token(Token { kind: token::ModSep, .. }, _)) = + tokens.peek() { tokens.next(); vec![PathSegment::from_ident(Ident::new(name, span))] @@ -427,14 +428,15 @@ impl MetaItem { vec![PathSegment::path_root(span)] }; loop { - if let Some(TokenTree::Token(Token { kind: token::Ident(name, _), span })) = + if let Some(TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) = tokens.next().map(TokenTree::uninterpolate) { segments.push(PathSegment::from_ident(Ident::new(name, span))); } else { return None; } - if let Some(TokenTree::Token(Token { kind: token::ModSep, .. })) = tokens.peek() + if let Some(TokenTree::Token(Token { kind: token::ModSep, .. }, _)) = + tokens.peek() { tokens.next(); } else { @@ -444,7 +446,7 @@ impl MetaItem { let span = span.with_hi(segments.last().unwrap().ident.span.hi()); Path { span, segments, tokens: None } } - Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. })) => match *nt { + Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. }, _)) => match *nt { token::Nonterminal::NtMeta(ref item) => return item.meta(item.path.span), token::Nonterminal::NtPath(ref path) => (**path).clone(), _ => return None, @@ -491,9 +493,9 @@ impl MetaItemKind { let mut tts = Vec::new(); for (i, item) in list.iter().enumerate() { if i > 0 { - tts.push(TokenTree::token(token::Comma, span).into()); + tts.push(TokenTree::token_alone(token::Comma, span)); } - tts.extend(item.token_trees_and_spacings()) + tts.extend(item.token_trees()) } MacArgs::Delimited( DelimSpan::from_single(span), @@ -504,31 +506,28 @@ impl MetaItemKind { } } - fn token_trees_and_spacings(&self, span: Span) -> Vec<TreeAndSpacing> { + fn token_trees(&self, span: Span) -> Vec<TokenTree> { match *self { MetaItemKind::Word => vec![], MetaItemKind::NameValue(ref lit) => { vec![ - TokenTree::token(token::Eq, span).into(), - TokenTree::Token(lit.to_token()).into(), + TokenTree::token_alone(token::Eq, span), + TokenTree::Token(lit.to_token(), Spacing::Alone), ] } MetaItemKind::List(ref list) => { let mut tokens = Vec::new(); for (i, item) in list.iter().enumerate() { if i > 0 { - tokens.push(TokenTree::token(token::Comma, span).into()); + tokens.push(TokenTree::token_alone(token::Comma, span)); } - tokens.extend(item.token_trees_and_spacings()) + tokens.extend(item.token_trees()) } - vec![ - TokenTree::Delimited( - DelimSpan::from_single(span), - Delimiter::Parenthesis, - TokenStream::new(tokens), - ) - .into(), - ] + vec![TokenTree::Delimited( + DelimSpan::from_single(span), + Delimiter::Parenthesis, + TokenStream::new(tokens), + )] } } } @@ -540,7 +539,7 @@ impl MetaItemKind { let item = NestedMetaItem::from_tokens(&mut tokens)?; result.push(item); match tokens.next() { - None | Some(TokenTree::Token(Token { kind: token::Comma, .. })) => {} + None | Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) => {} _ => return None, } } @@ -554,7 +553,7 @@ impl MetaItemKind { Some(TokenTree::Delimited(_, Delimiter::Invisible, inner_tokens)) => { MetaItemKind::name_value_from_tokens(&mut inner_tokens.into_trees()) } - Some(TokenTree::Token(token)) => { + Some(TokenTree::Token(token, _)) => { Lit::from_token(&token).ok().map(MetaItemKind::NameValue) } _ => None, @@ -586,7 +585,7 @@ impl MetaItemKind { MetaItemKind::list_from_tokens(inner_tokens) } Some(TokenTree::Delimited(..)) => None, - Some(TokenTree::Token(Token { kind: token::Eq, .. })) => { + Some(TokenTree::Token(Token { kind: token::Eq, .. }, _)) => { tokens.next(); MetaItemKind::name_value_from_tokens(tokens) } @@ -603,10 +602,12 @@ impl NestedMetaItem { } } - fn token_trees_and_spacings(&self) -> Vec<TreeAndSpacing> { + fn token_trees(&self) -> Vec<TokenTree> { match *self { - NestedMetaItem::MetaItem(ref item) => item.token_trees_and_spacings(), - NestedMetaItem::Literal(ref lit) => vec![TokenTree::Token(lit.to_token()).into()], + NestedMetaItem::MetaItem(ref item) => item.token_trees(), + NestedMetaItem::Literal(ref lit) => { + vec![TokenTree::Token(lit.to_token(), Spacing::Alone)] + } } } @@ -615,7 +616,7 @@ impl NestedMetaItem { I: Iterator<Item = TokenTree>, { match tokens.peek() { - Some(TokenTree::Token(token)) + Some(TokenTree::Token(token, _)) if let Ok(lit) = Lit::from_token(token) => { tokens.next(); diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index d933ea2da9e..01bd498b377 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -675,7 +675,7 @@ pub fn visit_attr_annotated_tt<T: MutVisitor>(tt: &mut AttrAnnotatedTokenTree, v // No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. pub fn visit_tt<T: MutVisitor>(tt: &mut TokenTree, vis: &mut T) { match tt { - TokenTree::Token(token) => { + TokenTree::Token(token, _) => { visit_token(token, vis); } TokenTree::Delimited(DelimSpan { open, close }, _delim, tts) => { @@ -690,7 +690,7 @@ pub fn visit_tt<T: MutVisitor>(tt: &mut TokenTree, vis: &mut T) { pub fn visit_tts<T: MutVisitor>(TokenStream(tts): &mut TokenStream, vis: &mut T) { if T::VISIT_TOKENS && !tts.is_empty() { let tts = Lrc::make_mut(tts); - visit_vec(tts, |(tree, _is_joint)| visit_tt(tree, vis)); + visit_vec(tts, |tree| visit_tt(tree, vis)); } } diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 37de90d64c7..9e4a22e1fa3 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -42,11 +42,15 @@ use std::{fmt, iter}; #[derive(Debug, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)] pub enum TokenTree { /// A single token. - Token(Token), + Token(Token, Spacing), /// A delimited sequence of token trees. Delimited(DelimSpan, Delimiter, TokenStream), } +// This type is used a lot. Make sure it doesn't unintentionally get bigger. +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +rustc_data_structures::static_assert_size!(TokenTree, 32); + // Ensure all fields of `TokenTree` is `Send` and `Sync`. #[cfg(parallel_compiler)] fn _dummy() @@ -62,7 +66,7 @@ impl TokenTree { /// Checks if this `TokenTree` is equal to the other, regardless of span information. pub fn eq_unspanned(&self, other: &TokenTree) -> bool { match (self, other) { - (TokenTree::Token(token), TokenTree::Token(token2)) => token.kind == token2.kind, + (TokenTree::Token(token, _), TokenTree::Token(token2, _)) => token.kind == token2.kind, (TokenTree::Delimited(_, delim, tts), TokenTree::Delimited(_, delim2, tts2)) => { delim == delim2 && tts.eq_unspanned(&tts2) } @@ -73,7 +77,7 @@ impl TokenTree { /// Retrieves the `TokenTree`'s span. pub fn span(&self) -> Span { match self { - TokenTree::Token(token) => token.span, + TokenTree::Token(token, _) => token.span, TokenTree::Delimited(sp, ..) => sp.entire(), } } @@ -81,18 +85,26 @@ impl TokenTree { /// Modify the `TokenTree`'s span in-place. pub fn set_span(&mut self, span: Span) { match self { - TokenTree::Token(token) => token.span = span, + TokenTree::Token(token, _) => token.span = span, TokenTree::Delimited(dspan, ..) => *dspan = DelimSpan::from_single(span), } } - pub fn token(kind: TokenKind, span: Span) -> TokenTree { - TokenTree::Token(Token::new(kind, span)) + // Create a `TokenTree::Token` with alone spacing. + pub fn token_alone(kind: TokenKind, span: Span) -> TokenTree { + TokenTree::Token(Token::new(kind, span), Spacing::Alone) + } + + // Create a `TokenTree::Token` with joint spacing. + pub fn token_joint(kind: TokenKind, span: Span) -> TokenTree { + TokenTree::Token(Token::new(kind, span), Spacing::Joint) } pub fn uninterpolate(self) -> TokenTree { match self { - TokenTree::Token(token) => TokenTree::Token(token.uninterpolate().into_owned()), + TokenTree::Token(token, spacing) => { + TokenTree::Token(token.uninterpolate().into_owned(), spacing) + } tt => tt, } } @@ -194,13 +206,12 @@ impl AttrAnnotatedTokenStream { .iter() .flat_map(|tree| match &tree.0 { AttrAnnotatedTokenTree::Token(inner) => { - smallvec![(TokenTree::Token(inner.clone()), tree.1)].into_iter() + smallvec![TokenTree::Token(inner.clone(), tree.1)].into_iter() + } + AttrAnnotatedTokenTree::Delimited(span, delim, stream) => { + smallvec![TokenTree::Delimited(*span, *delim, stream.to_tokenstream()),] + .into_iter() } - AttrAnnotatedTokenTree::Delimited(span, delim, stream) => smallvec![( - TokenTree::Delimited(*span, *delim, stream.to_tokenstream()), - tree.1, - )] - .into_iter(), AttrAnnotatedTokenTree::Attributes(data) => { let mut outer_attrs = Vec::new(); let mut inner_attrs = Vec::new(); @@ -226,7 +237,7 @@ impl AttrAnnotatedTokenStream { if !inner_attrs.is_empty() { let mut found = false; // Check the last two trees (to account for a trailing semi) - for (tree, _) in target_tokens.iter_mut().rev().take(2) { + for tree in target_tokens.iter_mut().rev().take(2) { if let TokenTree::Delimited(span, delim, delim_tokens) = tree { // Inner attributes are only supported on extern blocks, functions, impls, // and modules. All of these have their inner attributes placed at @@ -299,15 +310,13 @@ pub struct AttributesData { /// Today's `TokenTree`s can still contain AST via `token::Interpolated` for /// backwards compatibility. #[derive(Clone, Debug, Default, Encodable, Decodable)] -pub struct TokenStream(pub(crate) Lrc<Vec<TreeAndSpacing>>); - -pub type TreeAndSpacing = (TokenTree, Spacing); +pub struct TokenStream(pub(crate) Lrc<Vec<TokenTree>>); // `TokenStream` is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(TokenStream, 8); -#[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable)] +#[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)] pub enum Spacing { Alone, Joint, @@ -323,10 +332,10 @@ impl TokenStream { while let Some((pos, ts)) = iter.next() { if let Some((_, next)) = iter.peek() { let sp = match (&ts, &next) { - (_, (TokenTree::Token(Token { kind: token::Comma, .. }), _)) => continue, + (_, TokenTree::Token(Token { kind: token::Comma, .. }, _)) => continue, ( - (TokenTree::Token(token_left), Spacing::Alone), - (TokenTree::Token(token_right), _), + TokenTree::Token(token_left, Spacing::Alone), + TokenTree::Token(token_right, _), ) if ((token_left.is_ident() && !token_left.is_reserved_ident()) || token_left.is_lit()) && ((token_right.is_ident() && !token_right.is_reserved_ident()) @@ -334,11 +343,11 @@ impl TokenStream { { token_left.span } - ((TokenTree::Delimited(sp, ..), Spacing::Alone), _) => sp.entire(), + (TokenTree::Delimited(sp, ..), _) => sp.entire(), _ => continue, }; let sp = sp.shrink_to_hi(); - let comma = (TokenTree::token(token::Comma, sp), Spacing::Alone); + let comma = TokenTree::token_alone(token::Comma, sp); suggestion = Some((pos, comma, sp)); } } @@ -360,21 +369,9 @@ impl From<(AttrAnnotatedTokenTree, Spacing)> for AttrAnnotatedTokenStream { } } -impl From<TokenTree> for TokenStream { - fn from(tree: TokenTree) -> TokenStream { - TokenStream::new(vec![(tree, Spacing::Alone)]) - } -} - -impl From<TokenTree> for TreeAndSpacing { - fn from(tree: TokenTree) -> TreeAndSpacing { - (tree, Spacing::Alone) - } -} - impl iter::FromIterator<TokenTree> for TokenStream { fn from_iter<I: IntoIterator<Item = TokenTree>>(iter: I) -> Self { - TokenStream::new(iter.into_iter().map(Into::into).collect::<Vec<TreeAndSpacing>>()) + TokenStream::new(iter.into_iter().collect::<Vec<TokenTree>>()) } } @@ -387,7 +384,7 @@ impl PartialEq<TokenStream> for TokenStream { } impl TokenStream { - pub fn new(streams: Vec<TreeAndSpacing>) -> TokenStream { + pub fn new(streams: Vec<TokenTree>) -> TokenStream { TokenStream(Lrc::new(streams)) } @@ -420,13 +417,7 @@ impl TokenStream { } pub fn map_enumerated<F: FnMut(usize, &TokenTree) -> TokenTree>(self, mut f: F) -> TokenStream { - TokenStream(Lrc::new( - self.0 - .iter() - .enumerate() - .map(|(i, (tree, is_joint))| (f(i, tree), *is_joint)) - .collect(), - )) + TokenStream(Lrc::new(self.0.iter().enumerate().map(|(i, tree)| f(i, tree)).collect())) } fn opt_from_ast(node: &(impl HasAttrs + HasTokens)) -> Option<TokenStream> { @@ -444,6 +435,21 @@ impl TokenStream { Some(attr_annotated.to_tokenstream()) } + // Create a token stream containing a single token with alone spacing. + pub fn token_alone(kind: TokenKind, span: Span) -> TokenStream { + TokenStream::new(vec![TokenTree::token_alone(kind, span)]) + } + + // Create a token stream containing a single token with joint spacing. + pub fn token_joint(kind: TokenKind, span: Span) -> TokenStream { + TokenStream::new(vec![TokenTree::token_joint(kind, span)]) + } + + // Create a token stream containing a single `Delimited`. + pub fn delimited(span: DelimSpan, delim: Delimiter, tts: TokenStream) -> TokenStream { + TokenStream::new(vec![TokenTree::Delimited(span, delim, tts)]) + } + 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)) @@ -452,16 +458,16 @@ impl TokenStream { 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() + TokenStream::token_alone(token::Ident(ident.name, *is_raw), ident.span) } Nonterminal::NtLifetime(ident) => { - TokenTree::token(token::Lifetime(ident.name), ident.span).into() + TokenStream::token_alone(token::Lifetime(ident.name), ident.span) } 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() + TokenStream::token_alone(token::Semi, stmt.span) } Nonterminal::NtStmt(stmt) => TokenStream::from_ast(stmt), Nonterminal::NtPat(pat) => TokenStream::from_ast(pat), @@ -473,23 +479,23 @@ impl TokenStream { } } - fn flatten_token(token: &Token) -> TokenTree { + fn flatten_token(token: &Token, spacing: Spacing) -> 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) + TokenTree::Token(Token::new(token::Ident(ident.name, is_raw), ident.span), spacing) } token::Interpolated(nt) => TokenTree::Delimited( DelimSpan::from_single(token.span), Delimiter::Invisible, TokenStream::from_nonterminal_ast(&nt).flattened(), ), - _ => TokenTree::Token(token.clone()), + _ => TokenTree::Token(token.clone(), spacing), } } fn flatten_token_tree(tree: &TokenTree) -> TokenTree { match tree { - TokenTree::Token(token) => TokenStream::flatten_token(token), + TokenTree::Token(token, spacing) => TokenStream::flatten_token(token, *spacing), TokenTree::Delimited(span, delim, tts) => { TokenTree::Delimited(*span, *delim, tts.flattened()) } @@ -500,7 +506,7 @@ impl TokenStream { 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::Token(token, _) => !matches!(token.kind, token::Interpolated(_)), TokenTree::Delimited(_, _, inner) => can_skip(inner), }) } @@ -522,8 +528,8 @@ impl TokenStreamBuilder { TokenStreamBuilder(SmallVec::new()) } - pub fn push<T: Into<TokenStream>>(&mut self, stream: T) { - self.0.push(stream.into()); + pub fn push(&mut self, stream: TokenStream) { + self.0.push(stream); } pub fn build(self) -> TokenStream { @@ -564,14 +570,14 @@ impl TokenStreamBuilder { // `stream` is not empty and the first tree within it is a // token tree, and (c) the two tokens can be glued // together... - if let Some((TokenTree::Token(last_tok), Spacing::Joint)) = res_vec_mut.last() - && let Some((TokenTree::Token(tok), spacing)) = stream.0.first() + if let Some(TokenTree::Token(last_tok, Spacing::Joint)) = res_vec_mut.last() + && let Some(TokenTree::Token(tok, spacing)) = stream.0.first() && let Some(glued_tok) = last_tok.glue(&tok) { // ...then overwrite the last token tree in // `res_vec_mut` with the glued token, and skip the // first token tree from `stream`. - *res_vec_mut.last_mut().unwrap() = (TokenTree::Token(glued_tok), *spacing); + *res_vec_mut.last_mut().unwrap() = TokenTree::Token(glued_tok, *spacing); res_vec_mut.extend(stream_iter.skip(1)); } else { // Append all of `stream`. @@ -597,16 +603,8 @@ impl<'t> CursorRef<'t> { 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) + self.stream.0.get(self.index + n) } } @@ -614,7 +612,10 @@ impl<'t> Iterator for CursorRef<'t> { type Item = &'t TokenTree; fn next(&mut self) -> Option<&'t TokenTree> { - self.next_with_spacing().map(|(tree, _)| tree) + self.stream.0.get(self.index).map(|tree| { + self.index += 1; + tree + }) } } @@ -630,7 +631,10 @@ impl Iterator for Cursor { type Item = TokenTree; fn next(&mut self) -> Option<TokenTree> { - self.next_with_spacing().map(|(tree, _)| tree) + self.stream.0.get(self.index).map(|tree| { + self.index += 1; + tree.clone() + }) } } @@ -640,15 +644,7 @@ impl Cursor { } #[inline] - pub fn next_with_spacing(&mut self) -> Option<TreeAndSpacing> { - self.stream.0.get(self.index).map(|tree| { - self.index += 1; - tree.clone() - }) - } - - #[inline] - pub fn next_with_spacing_ref(&mut self) -> Option<&TreeAndSpacing> { + pub fn next_ref(&mut self) -> Option<&TokenTree> { self.stream.0.get(self.index).map(|tree| { self.index += 1; tree @@ -656,7 +652,7 @@ impl Cursor { } pub fn look_ahead(&self, n: usize) -> Option<&TokenTree> { - self.stream.0[self.index..].get(n).map(|(tree, _)| tree) + self.stream.0.get(self.index + n) } } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 3f830acbf27..d9594b323dd 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -15,7 +15,7 @@ use crate::ast::*; -use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::symbol::Ident; use rustc_span::Span; #[derive(Copy, Clone, Debug, PartialEq)] @@ -109,12 +109,7 @@ pub enum LifetimeCtxt { /// to monitor future changes to `Visitor` in case a new method with a /// new default implementation gets introduced.) pub trait Visitor<'ast>: Sized { - fn visit_name(&mut self, _span: Span, _name: Symbol) { - // Nothing to do. - } - fn visit_ident(&mut self, ident: Ident) { - walk_ident(self, ident); - } + fn visit_ident(&mut self, _ident: Ident) {} fn visit_foreign_item(&mut self, i: &'ast ForeignItem) { walk_foreign_item(self, i) } @@ -267,10 +262,6 @@ macro_rules! walk_list { } } -pub fn walk_ident<'a, V: Visitor<'a>>(visitor: &mut V, ident: Ident) { - visitor.visit_name(ident.span, ident.name); -} - pub fn walk_crate<'a, V: Visitor<'a>>(visitor: &mut V, krate: &'a Crate) { walk_list!(visitor, visit_item, &krate.items); walk_list!(visitor, visit_attribute, &krate.attrs); @@ -315,11 +306,7 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) { visitor.visit_vis(&item.vis); visitor.visit_ident(item.ident); match item.kind { - ItemKind::ExternCrate(orig_name) => { - if let Some(orig_name) = orig_name { - visitor.visit_name(item.span, orig_name); - } - } + ItemKind::ExternCrate(_) => {} ItemKind::Use(ref use_tree) => visitor.visit_use_tree(use_tree, item.id, false), ItemKind::Static(ref typ, _, ref expr) | ItemKind::Const(_, ref typ, ref expr) => { visitor.visit_ty(typ); diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index cd77dbca3c4..4166b4fc2e5 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -4,8 +4,7 @@ use super::LoweringContext; use rustc_ast::ptr::P; use rustc_ast::*; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 7cd360623ec..a1bf0f94964 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1835,7 +1835,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { debug!(?self.captured_lifetimes); let name = match res { LifetimeRes::Param { mut param, binder } => { - debug_assert_ne!(ident.name, kw::UnderscoreLifetime); let p_name = ParamName::Plain(ident); if let Some(mut captured_lifetimes) = self.captured_lifetimes.take() { if !captured_lifetimes.binders_to_ignore.contains(&binder) { @@ -1884,29 +1883,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } hir::LifetimeName::Param(param, ParamName::Fresh) } - LifetimeRes::Anonymous { binder, elided } => { - let mut l_name = None; - if let Some(mut captured_lifetimes) = self.captured_lifetimes.take() { - if !captured_lifetimes.binders_to_ignore.contains(&binder) { - let p_id = self.next_node_id(); - let p_def_id = self.create_def( - captured_lifetimes.parent_def_id, - p_id, - DefPathData::LifetimeNs(kw::UnderscoreLifetime), - ); - captured_lifetimes - .captures - .insert(p_def_id, (span, p_id, ParamName::Fresh, res)); - l_name = Some(hir::LifetimeName::Param(p_def_id, ParamName::Fresh)); - } - self.captured_lifetimes = Some(captured_lifetimes); - }; - l_name.unwrap_or(if elided { - hir::LifetimeName::Implicit - } else { - hir::LifetimeName::Underscore - }) - } + LifetimeRes::Infer => hir::LifetimeName::Infer, LifetimeRes::Static => hir::LifetimeName::Static, LifetimeRes::Error => hir::LifetimeName::Error, res => panic!("Unexpected lifetime resolution {:?} for {:?} at {:?}", res, ident, span), diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 334326e927f..60560b1c00e 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1272,7 +1272,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.visit_vis(&item.vis); self.visit_ident(item.ident); self.visit_generics(generics); - self.with_banned_tilde_const(|this| { + self.with_tilde_const_allowed(|this| { walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits) }); walk_list!(self, visit_assoc_item, items, AssocCtxt::Trait); diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 22cb27af4fb..ad9ed798e55 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -404,8 +404,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { gate_feature_post!(self, rustdoc_internals, attr.span, msg); } - if nested_meta.has_name(sym::tuple_variadic) { - let msg = "`#[doc(tuple_variadic)]` is meant for internal use only"; + if nested_meta.has_name(sym::fake_variadic) { + let msg = "`#[doc(fake_variadic)]` is meant for internal use only"; gate_feature_post!(self, rustdoc_internals, attr.span, msg); } } diff --git a/compiler/rustc_ast_passes/src/node_count.rs b/compiler/rustc_ast_passes/src/node_count.rs index ee166f75703..9c7369c83e2 100644 --- a/compiler/rustc_ast_passes/src/node_count.rs +++ b/compiler/rustc_ast_passes/src/node_count.rs @@ -16,9 +16,8 @@ impl NodeCounter { } impl<'ast> Visitor<'ast> for NodeCounter { - fn visit_ident(&mut self, ident: Ident) { + fn visit_ident(&mut self, _ident: Ident) { self.count += 1; - walk_ident(self, ident); } fn visit_foreign_item(&mut self, i: &ForeignItem) { self.count += 1; diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index c9e3a7edfa6..55ddd24c48a 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -145,7 +145,7 @@ pub fn print_crate<'a>( /// This makes printed token streams look slightly nicer, /// and also addresses some specific regressions described in #63896 and #73345. fn tt_prepend_space(tt: &TokenTree, prev: &TokenTree) -> bool { - if let TokenTree::Token(token) = prev { + if let TokenTree::Token(token, _) = prev { if matches!(token.kind, token::Dot | token::Dollar) { return false; } @@ -154,12 +154,12 @@ fn tt_prepend_space(tt: &TokenTree, prev: &TokenTree) -> bool { } } match tt { - TokenTree::Token(token) => !matches!(token.kind, token::Comma | token::Not | token::Dot), + TokenTree::Token(token, _) => !matches!(token.kind, token::Comma | token::Not | token::Dot), TokenTree::Delimited(_, Delimiter::Parenthesis, _) => { - !matches!(prev, TokenTree::Token(Token { kind: token::Ident(..), .. })) + !matches!(prev, TokenTree::Token(Token { kind: token::Ident(..), .. }, _)) } TokenTree::Delimited(_, Delimiter::Bracket, _) => { - !matches!(prev, TokenTree::Token(Token { kind: token::Pound, .. })) + !matches!(prev, TokenTree::Token(Token { kind: token::Pound, .. }, _)) } TokenTree::Delimited(..) => true, } @@ -526,7 +526,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere /// expression arguments as expressions). It can be done! I think. fn print_tt(&mut self, tt: &TokenTree, convert_dollar_crate: bool) { match tt { - TokenTree::Token(token) => { + TokenTree::Token(token, _) => { let token_str = self.token_to_string_ext(&token, convert_dollar_crate); self.word(token_str); if let token::DocComment(..) = token.kind { diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index dcfbecedfe8..10a9cfb626e 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -135,9 +135,42 @@ impl ConstStability { #[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)] #[derive(HashStable_Generic)] pub enum StabilityLevel { - // Reason for the current stability level and the relevant rust-lang issue - Unstable { reason: Option<Symbol>, issue: Option<NonZeroU32>, is_soft: bool }, - Stable { since: Symbol, allowed_through_unstable_modules: bool }, + /// `#[unstable]` + Unstable { + /// Reason for the current stability level. + reason: UnstableReason, + /// Relevant `rust-lang/rust` issue. + issue: Option<NonZeroU32>, + is_soft: bool, + /// If part of a feature is stabilized and a new feature is added for the remaining parts, + /// then the `implied_by` attribute is used to indicate which now-stable feature previously + /// contained a item. + /// + /// ```pseudo-Rust + /// #[unstable(feature = "foo", issue = "...")] + /// fn foo() {} + /// #[unstable(feature = "foo", issue = "...")] + /// fn foobar() {} + /// ``` + /// + /// ...becomes... + /// + /// ```pseudo-Rust + /// #[stable(feature = "foo", since = "1.XX.X")] + /// fn foo() {} + /// #[unstable(feature = "foobar", issue = "...", implied_by = "foo")] + /// fn foobar() {} + /// ``` + implied_by: Option<Symbol>, + }, + /// `#[stable]` + Stable { + /// Rust release which stabilized this feature. + since: Symbol, + /// Is this item allowed to be referred to on stable, despite being contained in unstable + /// modules? + allowed_through_unstable_modules: bool, + }, } impl StabilityLevel { @@ -149,6 +182,32 @@ impl StabilityLevel { } } +#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)] +#[derive(HashStable_Generic)] +pub enum UnstableReason { + None, + Default, + Some(Symbol), +} + +impl UnstableReason { + fn from_opt_reason(reason: Option<Symbol>) -> Self { + // UnstableReason::Default constructed manually + match reason { + Some(r) => Self::Some(r), + None => Self::None, + } + } + + pub fn to_opt_reason(&self) -> Option<Symbol> { + match self { + Self::None => None, + Self::Default => Some(sym::unstable_location_reason_default), + Self::Some(r) => Some(*r), + } + } +} + /// Collects stability info from all stability attributes in `attrs`. /// Returns `None` if no stability attributes are found. pub fn find_stability( @@ -243,6 +302,7 @@ where let mut issue = None; let mut issue_num = None; let mut is_soft = false; + let mut implied_by = None; for meta in metas { let Some(mi) = meta.meta_item() else { handle_errors( @@ -308,6 +368,11 @@ where } is_soft = true; } + sym::implied_by => { + if !get(mi, &mut implied_by) { + continue 'outer; + } + } _ => { handle_errors( &sess.parse_sess, @@ -332,7 +397,12 @@ where ); continue; } - let level = Unstable { reason, issue: issue_num, is_soft }; + let level = Unstable { + reason: UnstableReason::from_opt_reason(reason), + issue: issue_num, + is_soft, + implied_by, + }; if sym::unstable == meta_name { stab = Some((Stability { level, feature }, attr.span)); } else { @@ -391,7 +461,7 @@ where meta.span(), AttrError::UnknownMetaItem( pprust::path_to_string(&mi.path), - &["since", "note"], + &["feature", "since"], ), ); continue 'outer; diff --git a/compiler/rustc_borrowck/src/consumers.rs b/compiler/rustc_borrowck/src/consumers.rs index 97daad201d9..efc17a173f4 100644 --- a/compiler/rustc_borrowck/src/consumers.rs +++ b/compiler/rustc_borrowck/src/consumers.rs @@ -2,7 +2,7 @@ use rustc_hir::def_id::LocalDefId; use rustc_index::vec::IndexVec; -use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::infer::{DefiningAnchor, TyCtxtInferExt}; use rustc_middle::mir::Body; use rustc_middle::ty::{self, TyCtxt}; @@ -31,7 +31,7 @@ pub fn get_body_with_borrowck_facts<'tcx>( def: ty::WithOptConstParam<LocalDefId>, ) -> BodyWithBorrowckFacts<'tcx> { let (input_body, promoted) = tcx.mir_promoted(def); - tcx.infer_ctxt().with_opaque_type_inference(def.did).enter(|infcx| { + tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bind(def.did)).enter(|infcx| { let input_body: &Body<'_> = &input_body.borrow(); let promoted: &IndexVec<_, _> = &promoted.borrow(); *super::do_mir_borrowck(&infcx, input_body, promoted, true).1.unwrap() diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index ee96dbc2f60..255c31d0b1a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -353,9 +353,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // We use the statements were the binding was initialized, and inspect the HIR to look // for the branching codepaths that aren't covered, to point at them. - let hir_id = self.mir_hir_id(); let map = self.infcx.tcx.hir(); - let body_id = map.body_owned_by(hir_id); + let body_id = map.body_owned_by(self.mir_def_id()); let body = map.body(body_id); let mut visitor = ConditionVisitor { spans: &spans, name: &name, errors: vec![] }; @@ -2155,9 +2154,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } StorageDeadOrDrop::Destructor(_) => kind, }, - ProjectionElem::OpaqueCast { .. } - | ProjectionElem::Field(..) - | ProjectionElem::Downcast(..) => { + ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => { match place_ty.ty.kind() { ty::Adt(def, _) if def.has_dtor(tcx) => { // Report the outermost adt with a destructor diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index fada3d45fbe..53c07a3d481 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -226,7 +226,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } ProjectionElem::Downcast(..) if including_downcast.0 => return None, ProjectionElem::Downcast(..) => (), - ProjectionElem::OpaqueCast(..) => (), ProjectionElem::Field(field, _ty) => { // FIXME(project-rfc_2229#36): print capture precisely here. if let Some(field) = self.is_upvar_field_projection(PlaceRef { @@ -287,7 +286,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { PlaceRef { local, projection: proj_base }.ty(self.body, self.infcx.tcx) } ProjectionElem::Downcast(..) => place.ty(self.body, self.infcx.tcx), - ProjectionElem::OpaqueCast(ty) => PlaceTy::from_ty(*ty), ProjectionElem::Field(_, field_type) => PlaceTy::from_ty(*field_type), }, }; diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index cb7077fe621..ac26bc9c7f4 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -169,7 +169,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { .., ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::OpaqueCast { .. } | ProjectionElem::Subslice { .. } | ProjectionElem::Downcast(..), ], @@ -854,7 +853,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let closure_id = self.mir_hir_id(); let fn_call_id = hir.get_parent_node(closure_id); let node = hir.get(fn_call_id); - let item_id = hir.enclosing_body_owner(fn_call_id); + let def_id = hir.enclosing_body_owner(fn_call_id); let mut look_at_return = true; // If we can detect the expression to be an `fn` call where the closure was an argument, // we point at the `fn` definition argument... @@ -865,7 +864,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { .filter(|(_, arg)| arg.hir_id == closure_id) .map(|(pos, _)| pos) .next(); - let def_id = hir.local_def_id(item_id); let tables = self.infcx.tcx.typeck(def_id); if let Some(ty::FnDef(def_id, _)) = tables.node_type_opt(func.hir_id).as_ref().map(|ty| ty.kind()) diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 0e6a05478a0..176090c3b7a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -1,6 +1,6 @@ //! Error reporting machinery for lifetime errors. -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; @@ -78,6 +78,8 @@ pub(crate) enum RegionErrorKind<'tcx> { span: Span, /// The hidden type. hidden_ty: Ty<'tcx>, + /// The opaque type. + key: ty::OpaqueTypeKey<'tcx>, /// The unexpected region. member_region: ty::Region<'tcx>, }, @@ -205,14 +207,16 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } } - RegionErrorKind::UnexpectedHiddenRegion { span, hidden_ty, member_region } => { + RegionErrorKind::UnexpectedHiddenRegion { span, hidden_ty, key, member_region } => { let named_ty = self.regioncx.name_regions(self.infcx.tcx, hidden_ty); + let named_key = self.regioncx.name_regions(self.infcx.tcx, key); let named_region = self.regioncx.name_regions(self.infcx.tcx, member_region); self.buffer_error(unexpected_hidden_region_diagnostic( self.infcx.tcx, span, named_ty, named_region, + named_key, )); } @@ -850,13 +854,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { debug!("trait spans found: {:?}", traits); for span in &traits { let mut multi_span: MultiSpan = vec![*span].into(); - multi_span.push_span_label( - *span, - "this has an implicit `'static` lifetime requirement".to_string(), - ); + multi_span + .push_span_label(*span, "this has an implicit `'static` lifetime requirement"); multi_span.push_span_label( ident.span, - "calling this method introduces the `impl`'s 'static` requirement".to_string(), + "calling this method introduces the `impl`'s 'static` requirement", ); err.span_note(multi_span, "the used `impl` has a `'static` requirement"); err.span_suggestion_verbose( diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 4cf1ac4d7ab..0662d4d882f 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -589,8 +589,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { hir::LifetimeName::Param(_, hir::ParamName::Fresh) | hir::LifetimeName::ImplicitObjectLifetimeDefault - | hir::LifetimeName::Implicit - | hir::LifetimeName::Underscore => { + | hir::LifetimeName::Infer => { // In this case, the user left off the lifetime; so // they wrote something like: // diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 15c2a9f7aef..3e2d57ae00c 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -24,7 +24,7 @@ use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_index::bit_set::ChunkedBitSet; use rustc_index::vec::IndexVec; -use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; +use rustc_infer::infer::{DefiningAnchor, InferCtxt, TyCtxtInferExt}; use rustc_middle::mir::{ traversal, Body, ClearCrossCrate, Local, Location, Mutability, Operand, Place, PlaceElem, PlaceRef, VarDebugInfoContents, @@ -130,11 +130,14 @@ fn mir_borrowck<'tcx>( debug!("run query mir_borrowck: {}", tcx.def_path_str(def.did.to_def_id())); let hir_owner = tcx.hir().local_def_id_to_hir_id(def.did).owner; - let opt_closure_req = tcx.infer_ctxt().with_opaque_type_inference(hir_owner).enter(|infcx| { - let input_body: &Body<'_> = &input_body.borrow(); - let promoted: &IndexVec<_, _> = &promoted.borrow(); - do_mir_borrowck(&infcx, input_body, promoted, false).0 - }); + let opt_closure_req = tcx + .infer_ctxt() + .with_opaque_type_inference(DefiningAnchor::Bind(hir_owner)) + .enter(|infcx| { + let input_body: &Body<'_> = &input_body.borrow(); + let promoted: &IndexVec<_, _> = &promoted.borrow(); + do_mir_borrowck(&infcx, input_body, promoted, false).0 + }); debug!("mir_borrowck done"); tcx.arena.alloc(opt_closure_req) @@ -212,7 +215,7 @@ fn do_mir_borrowck<'a, 'tcx>( let (move_data, move_errors): (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>) = match MoveData::gather_moves(&body, tcx, param_env) { - Ok(move_data) => (move_data, Vec::new()), + Ok((_, move_data)) => (move_data, Vec::new()), Err((move_data, move_errors)) => (move_data, move_errors), }; let promoted_errors = promoted @@ -1788,7 +1791,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { for (place_base, elem) in place.iter_projections().rev() { match elem { ProjectionElem::Index(_/*operand*/) | - ProjectionElem::OpaqueCast(_) | ProjectionElem::ConstantIndex { .. } | // assigning to P[i] requires P to be valid. ProjectionElem::Downcast(_/*adt_def*/, _/*variant_idx*/) => @@ -2180,7 +2182,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { | ProjectionElem::Index(..) | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } - | ProjectionElem::OpaqueCast { .. } | ProjectionElem::Downcast(..) => { let upvar_field_projection = self.is_upvar_field_projection(place); if let Some(field) = upvar_field_projection { diff --git a/compiler/rustc_borrowck/src/member_constraints.rs b/compiler/rustc_borrowck/src/member_constraints.rs index e91fcf1472d..43253a2aab0 100644 --- a/compiler/rustc_borrowck/src/member_constraints.rs +++ b/compiler/rustc_borrowck/src/member_constraints.rs @@ -38,6 +38,8 @@ pub(crate) struct NllMemberConstraint<'tcx> { /// The hidden type in which `R0` appears. (Used in error reporting.) pub(crate) hidden_ty: Ty<'tcx>, + pub(crate) key: ty::OpaqueTypeKey<'tcx>, + /// The region `R0`. pub(crate) member_region_vid: ty::RegionVid, @@ -90,6 +92,7 @@ impl<'tcx> MemberConstraintSet<'tcx, ty::RegionVid> { member_region_vid, definition_span: m_c.definition_span, hidden_ty: m_c.hidden_ty, + key: m_c.key, start_index, end_index, }); diff --git a/compiler/rustc_borrowck/src/places_conflict.rs b/compiler/rustc_borrowck/src/places_conflict.rs index 5b67e6aa1cf..97335fd0dff 100644 --- a/compiler/rustc_borrowck/src/places_conflict.rs +++ b/compiler/rustc_borrowck/src/places_conflict.rs @@ -255,7 +255,6 @@ fn place_components_conflict<'tcx>( | (ProjectionElem::Index { .. }, _, _) | (ProjectionElem::ConstantIndex { .. }, _, _) | (ProjectionElem::Subslice { .. }, _, _) - | (ProjectionElem::OpaqueCast { .. }, _, _) | (ProjectionElem::Downcast { .. }, _, _) => { // Recursive case. This can still be disjoint on a // further iteration if this a shallow access and @@ -323,17 +322,6 @@ fn place_projection_conflict<'tcx>( debug!("place_element_conflict: DISJOINT-OR-EQ-DEREF"); Overlap::EqualOrDisjoint } - (ProjectionElem::OpaqueCast(v1), ProjectionElem::OpaqueCast(v2)) => { - if v1 == v2 { - // same type - recur. - debug!("place_element_conflict: DISJOINT-OR-EQ-OPAQUE"); - Overlap::EqualOrDisjoint - } else { - // Different types. Disjoint! - debug!("place_element_conflict: DISJOINT-OPAQUE"); - Overlap::Disjoint - } - } (ProjectionElem::Field(f1, _), ProjectionElem::Field(f2, _)) => { if f1 == f2 { // same field (e.g., `a.y` vs. `a.y`) - recur. @@ -537,7 +525,6 @@ fn place_projection_conflict<'tcx>( | ProjectionElem::Field(..) | ProjectionElem::Index(..) | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::OpaqueCast { .. } | ProjectionElem::Subslice { .. } | ProjectionElem::Downcast(..), _, diff --git a/compiler/rustc_borrowck/src/prefixes.rs b/compiler/rustc_borrowck/src/prefixes.rs index 2b50cbac9a0..bdf2becb711 100644 --- a/compiler/rustc_borrowck/src/prefixes.rs +++ b/compiler/rustc_borrowck/src/prefixes.rs @@ -81,7 +81,6 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> { } ProjectionElem::Downcast(..) | ProjectionElem::Subslice { .. } - | ProjectionElem::OpaqueCast { .. } | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Index(_) => { cursor = cursor_base; diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 9040cfcf54f..2894c6d29ec 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1763,6 +1763,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { errors_buffer.push(RegionErrorKind::UnexpectedHiddenRegion { span: m_c.definition_span, hidden_ty: m_c.hidden_ty, + key: m_c.key, member_region, }); } diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index de9da845729..d6712b6a479 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -3,8 +3,8 @@ use rustc_data_structures::vec_map::VecMap; use rustc_hir::def_id::LocalDefId; use rustc_hir::OpaqueTyOrigin; use rustc_infer::infer::error_reporting::unexpected_hidden_region_diagnostic; -use rustc_infer::infer::InferCtxt; use rustc_infer::infer::TyCtxtInferExt as _; +use rustc_infer::infer::{DefiningAnchor, InferCtxt}; use rustc_infer::traits::{Obligation, ObligationCause, TraitEngine}; use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts}; @@ -246,7 +246,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // after producing an error for each of them. let definition_ty = instantiated_ty.ty.fold_with(&mut ReverseMapper::new( self.tcx, - def_id, + opaque_type_key, map, instantiated_ty.ty, instantiated_ty.span, @@ -269,59 +269,65 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // 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); - 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); + // HACK This bubble is required for this tests to pass: + // type-alias-impl-trait/issue-67844-nested-opaque.rs + self.tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bubble).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.to_def_id(), id_substs), + definition_ty, + err, + ) + .emit(); } } - Err(err) => { - infcx - .report_mismatched_types( - &ObligationCause::misc(instantiated_ty.span, body_id), - self.tcx.mk_opaque(def_id.to_def_id(), id_substs), - definition_ty, - err, - ) - .emit(); - } - } - fulfillment_cx.register_predicate_obligation( - &infcx, - Obligation::misc(instantiated_ty.span, body_id, param_env, predicate), - ); + 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); + // 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(); + // This is still required for many(half of the tests in ui/type-alias-impl-trait) + // tests to pass + 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() - } - }) + if errors.is_empty() { + definition_ty + } else { + infcx.report_fulfillment_errors(&errors, None, false); + self.tcx.ty_error() + } + }, + ) } else { definition_ty } @@ -423,7 +429,7 @@ fn check_opaque_type_parameter_valid( struct ReverseMapper<'tcx> { tcx: TyCtxt<'tcx>, - opaque_type_def_id: LocalDefId, + key: ty::OpaqueTypeKey<'tcx>, map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>, map_missing_regions_to_empty: bool, @@ -437,14 +443,14 @@ struct ReverseMapper<'tcx> { impl<'tcx> ReverseMapper<'tcx> { fn new( tcx: TyCtxt<'tcx>, - opaque_type_def_id: LocalDefId, + key: ty::OpaqueTypeKey<'tcx>, map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>, hidden_ty: Ty<'tcx>, span: Span, ) -> Self { Self { tcx, - opaque_type_def_id, + key, map, map_missing_regions_to_empty: false, hidden_ty: Some(hidden_ty), @@ -498,7 +504,7 @@ impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> { } } - let generics = self.tcx().generics_of(self.opaque_type_def_id); + let generics = self.tcx().generics_of(self.key.def_id); match self.map.get(&r.into()).map(|k| k.unpack()) { Some(GenericArgKind::Lifetime(r1)) => r1, Some(u) => panic!("region mapped to unexpected kind: {:?}", u), @@ -507,9 +513,10 @@ impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> { if let Some(hidden_ty) = self.hidden_ty.take() { unexpected_hidden_region_diagnostic( self.tcx, - self.tcx.def_span(self.opaque_type_def_id), + self.tcx.def_span(self.key.def_id), hidden_ty, r, + self.key, ) .emit(); } diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index 3f9c0cecccc..16796091830 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -22,6 +22,16 @@ pub(crate) struct ConstraintConversion<'a, 'tcx> { infcx: &'a InferCtxt<'a, 'tcx>, tcx: TyCtxt<'tcx>, universal_regions: &'a UniversalRegions<'tcx>, + /// Each RBP `GK: 'a` is assumed to be true. These encode + /// relationships like `T: 'a` that are added via implicit bounds + /// or the `param_env`. + /// + /// Each region here is guaranteed to be a key in the `indices` + /// map. We use the "original" regions (i.e., the keys from the + /// map, and not the values) because the code in + /// `process_registered_region_obligations` has some special-cased + /// logic expecting to see (e.g.) `ReStatic`, and if we supplied + /// our special inference variable there, we would mess that up. region_bound_pairs: &'a RegionBoundPairs<'tcx>, implicit_region_bound: ty::Region<'tcx>, param_env: ty::ParamEnv<'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 fe66821ad75..cc0318ede54 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -2,6 +2,7 @@ use rustc_data_structures::frozen::Frozen; use rustc_data_structures::transitive_relation::TransitiveRelation; use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::infer::outlives; +use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::region_constraints::GenericKind; use rustc_infer::infer::InferCtxt; use rustc_middle::mir::ConstraintCategory; @@ -34,18 +35,6 @@ pub(crate) struct UniversalRegionRelations<'tcx> { inverse_outlives: TransitiveRelation<RegionVid>, } -/// Each RBP `('a, GK)` indicates that `GK: 'a` can be assumed to -/// be true. These encode relationships like `T: 'a` that are -/// added via implicit bounds. -/// -/// Each region here is guaranteed to be a key in the `indices` -/// map. We use the "original" regions (i.e., the keys from the -/// map, and not the values) because the code in -/// `process_registered_region_obligations` has some special-cased -/// logic expecting to see (e.g.) `ReStatic`, and if we supplied -/// our special inference variable there, we would mess that up. -type RegionBoundPairs<'tcx> = Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>; - /// As part of computing the free region relations, we also have to /// normalize the input-output types, which we then need later. So we /// return those. This vector consists of first the input types and @@ -71,7 +60,7 @@ pub(crate) fn create<'tcx>( implicit_region_bound, constraints, universal_regions: universal_regions.clone(), - region_bound_pairs: Vec::new(), + region_bound_pairs: Default::default(), relations: UniversalRegionRelations { universal_regions: universal_regions.clone(), outlives: Default::default(), @@ -371,11 +360,13 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { } OutlivesBound::RegionSubParam(r_a, param_b) => { - self.region_bound_pairs.push((r_a, GenericKind::Param(param_b))); + self.region_bound_pairs + .insert(ty::OutlivesPredicate(GenericKind::Param(param_b), r_a)); } OutlivesBound::RegionSubProjection(r_a, projection_b) => { - self.region_bound_pairs.push((r_a, GenericKind::Projection(projection_b))); + self.region_bound_pairs + .insert(ty::OutlivesPredicate(GenericKind::Projection(projection_b), r_a)); } } } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 8e763a02af3..cf2140097e6 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -790,19 +790,6 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { } PlaceTy::from_ty(fty) } - ProjectionElem::OpaqueCast(ty) => { - let ty = self.sanitize_type(place, ty); - let ty = self.cx.normalize(ty, location); - self.cx - .eq_types( - base.ty, - ty, - location.to_locations(), - ConstraintCategory::TypeAnnotation, - ) - .unwrap(); - PlaceTy::from_ty(ty) - } } } @@ -1208,11 +1195,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { tcx, self.param_env, proj, - |this, field, _| { + |this, field, ()| { let ty = this.field_ty(tcx, field); self.normalize(ty, locations) }, - |_, _| unreachable!(), ); curr_projected_ty = projected_ty; } @@ -2507,7 +2493,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } ProjectionElem::Field(..) | ProjectionElem::Downcast(..) - | ProjectionElem::OpaqueCast(..) | ProjectionElem::Index(..) | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 829eaa305e8..a2205c3613d 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -656,7 +656,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl let span = arg_spans.next().unwrap_or(template_sp); let operand_idx = match arg.position { - parse::ArgumentIs(idx) | parse::ArgumentImplicitlyIs(idx) => { + parse::ArgumentIs(idx, _) | parse::ArgumentImplicitlyIs(idx) => { if idx >= args.operands.len() || named_pos.contains_key(&idx) || args.reg_args.contains(&idx) diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs index 01152ff7df5..dcea883a5a3 100644 --- a/compiler/rustc_builtin_macros/src/assert/context.rs +++ b/compiler/rustc_builtin_macros/src/assert/context.rs @@ -152,7 +152,7 @@ impl<'cx, 'a> Context<'cx, 'a> { fn build_panic(&self, expr_str: &str, panic_path: Path) -> P<Expr> { let escaped_expr_str = escape_to_fmt(expr_str); let initial = [ - TokenTree::token( + TokenTree::token_alone( token::Literal(token::Lit { kind: token::LitKind::Str, symbol: Symbol::intern(&if self.fmt_string.is_empty() { @@ -167,12 +167,12 @@ impl<'cx, 'a> Context<'cx, 'a> { }), self.span, ), - TokenTree::token(token::Comma, self.span), + TokenTree::token_alone(token::Comma, self.span), ]; let captures = self.capture_decls.iter().flat_map(|cap| { [ - TokenTree::token(token::Ident(cap.ident.name, false), cap.ident.span), - TokenTree::token(token::Comma, self.span), + TokenTree::token_alone(token::Ident(cap.ident.name, false), cap.ident.span), + TokenTree::token_alone(token::Comma, self.span), ] }); self.cx.expr( diff --git a/compiler/rustc_builtin_macros/src/concat_idents.rs b/compiler/rustc_builtin_macros/src/concat_idents.rs index 239bafb266a..297c604e020 100644 --- a/compiler/rustc_builtin_macros/src/concat_idents.rs +++ b/compiler/rustc_builtin_macros/src/concat_idents.rs @@ -20,14 +20,14 @@ pub fn expand_concat_idents<'cx>( for (i, e) in tts.into_trees().enumerate() { if i & 1 == 1 { match e { - TokenTree::Token(Token { kind: token::Comma, .. }) => {} + TokenTree::Token(Token { kind: token::Comma, .. }, _) => {} _ => { cx.span_err(sp, "concat_idents! expecting comma"); return DummyResult::any(sp); } } } else { - if let TokenTree::Token(token) = e { + if let TokenTree::Token(token, _) = e { if let Some((ident, _)) = token.ident() { res_str.push_str(ident.name.as_str()); continue; diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 076b627ca79..735017aa5a8 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -727,16 +727,8 @@ impl<'a> TraitDef<'a> { let attr = cx.attribute(cx.meta_word(self.span, sym::automatically_derived)); let opt_trait_ref = Some(trait_ref); - let unused_qual = { - let word = rustc_ast::attr::mk_nested_word_item(Ident::new( - sym::unused_qualifications, - self.span, - )); - let list = rustc_ast::attr::mk_list_item(Ident::new(sym::allow, self.span), vec![word]); - cx.attribute(list) - }; - let mut a = vec![attr, unused_qual]; + let mut a = vec![attr]; a.extend(self.attributes.iter().cloned()); cx.item( diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 3b7e2102ffa..082c7893426 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -11,12 +11,11 @@ use rustc_errors::{pluralize, Applicability, MultiSpan, PResult}; use rustc_expand::base::{self, *}; use rustc_parse_format as parse; use rustc_span::symbol::{sym, Ident, Symbol}; -use rustc_span::{InnerSpan, Span}; +use rustc_span::{BytePos, InnerSpan, Span}; use smallvec::SmallVec; use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY; use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiagnostics, LintId}; -use rustc_parse_format::Count; use std::borrow::Cow; use std::collections::hash_map::Entry; @@ -32,6 +31,136 @@ enum Position { Named(Symbol, InnerSpan), } +/// Indicates how positional named argument (i.e. an named argument which is used by position +/// instead of by name) is used in format string +/// * `Arg` is the actual argument to print +/// * `Width` is width format argument +/// * `Precision` is precion format argument +/// Example: `{Arg:Width$.Precision$} +#[derive(Debug, Eq, PartialEq)] +enum PositionalNamedArgType { + Arg, + Width, + Precision, +} + +/// Contains information necessary to create a lint for a positional named argument +#[derive(Debug)] +struct PositionalNamedArg { + ty: PositionalNamedArgType, + /// The piece of the using this argument (multiple pieces can use the same argument) + cur_piece: usize, + /// The InnerSpan for in the string to be replaced with the named argument + /// This will be None when the position is implicit + inner_span_to_replace: Option<rustc_parse_format::InnerSpan>, + /// The name to use instead of the position + replacement: Symbol, + /// The span for the positional named argument (so the lint can point a message to it) + positional_named_arg_span: Span, +} + +impl PositionalNamedArg { + /// Determines what span to replace with the name of the named argument + fn get_span_to_replace(&self, cx: &Context<'_, '_>) -> Option<Span> { + if let Some(inner_span) = &self.inner_span_to_replace { + return Some( + cx.fmtsp.from_inner(InnerSpan { start: inner_span.start, end: inner_span.end }), + ); + } else if self.ty == PositionalNamedArgType::Arg { + // In the case of a named argument whose position is implicit, there will not be a span + // to replace. Instead, we insert the name after the `{`, which is the first character + // of arg_span. + return cx + .arg_spans + .get(self.cur_piece) + .map(|arg_span| arg_span.with_lo(arg_span.lo() + BytePos(1)).shrink_to_lo()); + } + + None + } +} + +/// Encapsulates all the named arguments that have been used positionally +#[derive(Debug)] +struct PositionalNamedArgsLint { + positional_named_args: Vec<PositionalNamedArg>, +} + +impl PositionalNamedArgsLint { + /// For a given positional argument, check if the index is for a named argument. + /// + /// Since positional arguments are required to come before named arguments, if the positional + /// index is greater than or equal to the start of named arguments, we know it's a named + /// argument used positionally. + /// + /// Example: + /// println!("{} {} {2}", 0, a=1, b=2); + /// + /// In this case, the first piece (`{}`) would be ArgumentImplicitlyIs with an index of 0. The + /// total number of arguments is 3 and the number of named arguments is 2, so the start of named + /// arguments is index 1. Therefore, the index of 0 is okay. + /// + /// The second piece (`{}`) would be ArgumentImplicitlyIs with an index of 1, which is the start + /// of named arguments, and so we should add a lint to use the named argument `a`. + /// + /// The third piece (`{2}`) would be ArgumentIs with an index of 2, which is greater than the + /// start of named arguments, and so we should add a lint to use the named argument `b`. + /// + /// This same check also works for width and precision formatting when either or both are + /// CountIsParam, which contains an index into the arguments. + fn maybe_add_positional_named_arg( + &mut self, + current_positional_arg: usize, + total_args_length: usize, + format_argument_index: usize, + ty: PositionalNamedArgType, + cur_piece: usize, + inner_span_to_replace: Option<rustc_parse_format::InnerSpan>, + names: &FxHashMap<Symbol, (usize, Span)>, + ) { + let start_of_named_args = total_args_length - names.len(); + if current_positional_arg >= start_of_named_args { + self.maybe_push(format_argument_index, ty, cur_piece, inner_span_to_replace, names) + } + } + + /// Try constructing a PositionalNamedArg struct and pushing it into the vec of positional + /// named arguments. If a named arg associated with `format_argument_index` cannot be found, + /// a new item will not be added as the lint cannot be emitted in this case. + fn maybe_push( + &mut self, + format_argument_index: usize, + ty: PositionalNamedArgType, + cur_piece: usize, + inner_span_to_replace: Option<rustc_parse_format::InnerSpan>, + names: &FxHashMap<Symbol, (usize, Span)>, + ) { + let named_arg = names + .iter() + .find(|&(_, &(index, _))| index == format_argument_index) + .map(|found| found.clone()); + + if let Some((&replacement, &(_, positional_named_arg_span))) = named_arg { + // In FormatSpec, `precision_span` starts at the leading `.`, which we want to keep in + // the lint suggestion, so increment `start` by 1 when `PositionalArgumentType` is + // `Precision`. + let inner_span_to_replace = if ty == PositionalNamedArgType::Precision { + inner_span_to_replace + .map(|is| rustc_parse_format::InnerSpan { start: is.start + 1, end: is.end }) + } else { + inner_span_to_replace + }; + self.positional_named_args.push(PositionalNamedArg { + ty, + cur_piece, + inner_span_to_replace, + replacement, + positional_named_arg_span, + }); + } + } +} + struct Context<'a, 'b> { ecx: &'a mut ExtCtxt<'b>, /// The macro's call site. References to unstable formatting internals must @@ -118,6 +247,7 @@ struct Context<'a, 'b> { /// Whether this format string came from a string literal, as opposed to a macro. is_literal: bool, + unused_names_lint: PositionalNamedArgsLint, } /// Parses the arguments from the given list of tokens, returning the diagnostic @@ -242,7 +372,7 @@ impl<'a, 'b> Context<'a, 'b> { self.args.len() - self.num_captured_args } - fn resolve_name_inplace(&self, p: &mut parse::Piece<'_>) { + fn resolve_name_inplace(&mut self, p: &mut parse::Piece<'_>) { // NOTE: the `unwrap_or` branch is needed in case of invalid format // arguments, e.g., `format_args!("{foo}")`. let lookup = @@ -252,7 +382,7 @@ impl<'a, 'b> Context<'a, 'b> { parse::String(_) => {} parse::NextArgument(ref mut arg) => { if let parse::ArgumentNamed(s, _) = arg.position { - arg.position = parse::ArgumentIs(lookup(s)); + arg.position = parse::ArgumentIs(lookup(s), None); } if let parse::CountIsName(s, _) = arg.format.width { arg.format.width = parse::CountIsParam(lookup(s)); @@ -273,15 +403,48 @@ impl<'a, 'b> Context<'a, 'b> { parse::NextArgument(ref arg) => { // width/precision first, if they have implicit positional // parameters it makes more sense to consume them first. - self.verify_count(arg.format.width); - self.verify_count(arg.format.precision); + self.verify_count( + arg.format.width, + &arg.format.width_span, + PositionalNamedArgType::Width, + ); + self.verify_count( + arg.format.precision, + &arg.format.precision_span, + PositionalNamedArgType::Precision, + ); // argument second, if it's an implicit positional parameter // it's written second, so it should come after width/precision. let pos = match arg.position { - parse::ArgumentIs(i) | parse::ArgumentImplicitlyIs(i) => Exact(i), + parse::ArgumentIs(i, arg_end) => { + self.unused_names_lint.maybe_add_positional_named_arg( + i, + self.args.len(), + i, + PositionalNamedArgType::Arg, + self.curpiece, + arg_end, + &self.names, + ); + + Exact(i) + } + parse::ArgumentImplicitlyIs(i) => { + self.unused_names_lint.maybe_add_positional_named_arg( + i, + self.args.len(), + i, + PositionalNamedArgType::Arg, + self.curpiece, + None, + &self.names, + ); + Exact(i) + } parse::ArgumentNamed(s, span) => { - Named(Symbol::intern(s), InnerSpan::new(span.start, span.end)) + let symbol = Symbol::intern(s); + Named(symbol, InnerSpan::new(span.start, span.end)) } }; @@ -349,10 +512,24 @@ impl<'a, 'b> Context<'a, 'b> { } } - fn verify_count(&mut self, c: parse::Count<'_>) { + fn verify_count( + &mut self, + c: parse::Count<'_>, + inner_span: &Option<rustc_parse_format::InnerSpan>, + named_arg_type: PositionalNamedArgType, + ) { match c { parse::CountImplied | parse::CountIs(..) => {} parse::CountIsParam(i) => { + self.unused_names_lint.maybe_add_positional_named_arg( + i, + self.args.len(), + i, + named_arg_type, + self.curpiece, + *inner_span, + &self.names, + ); self.verify_arg_type(Exact(i), Count); } parse::CountIsName(s, span) => { @@ -485,7 +662,7 @@ impl<'a, 'b> Context<'a, 'b> { if let Some(span) = fmt.width_span { let span = self.fmtsp.from_inner(InnerSpan::new(span.start, span.end)); match fmt.width { - parse::CountIsParam(pos) if pos > self.num_args() => { + parse::CountIsParam(pos) if pos >= self.num_args() => { e.span_label( span, &format!( @@ -673,7 +850,7 @@ impl<'a, 'b> Context<'a, 'b> { // Build the position let pos = { match arg.position { - parse::ArgumentIs(i) | parse::ArgumentImplicitlyIs(i) => { + parse::ArgumentIs(i, ..) | parse::ArgumentImplicitlyIs(i) => { // Map to index in final generated argument array // in case of multiple types specified let arg_idx = match arg_index_consumed.get_mut(i) { @@ -701,7 +878,7 @@ impl<'a, 'b> Context<'a, 'b> { // track the current argument ourselves. let i = self.curarg; self.curarg += 1; - parse::ArgumentIs(i) + parse::ArgumentIs(i, None) }, format: parse::FormatSpec { fill: arg.format.fill, @@ -971,45 +1148,27 @@ pub fn expand_format_args_nl<'cx>( expand_format_args_impl(ecx, sp, tts, true) } -fn lint_named_arguments_used_positionally( - names: FxHashMap<Symbol, (usize, Span)>, - cx: &mut Context<'_, '_>, - unverified_pieces: Vec<parse::Piece<'_>>, -) { - let mut used_argument_names = FxHashSet::<&str>::default(); - for piece in unverified_pieces { - if let rustc_parse_format::Piece::NextArgument(a) = piece { - match a.position { - rustc_parse_format::Position::ArgumentNamed(arg_name, _) => { - used_argument_names.insert(arg_name); - } - _ => {} - }; - if let Count::CountIsName(s, _) = a.format.width { - used_argument_names.insert(s); - } - if let Count::CountIsName(s, _) = a.format.precision { - used_argument_names.insert(s); - } - } - } +fn create_lints_for_named_arguments_used_positionally(cx: &mut Context<'_, '_>) { + for named_arg in &cx.unused_names_lint.positional_named_args { + let arg_span = named_arg.get_span_to_replace(cx); - for (symbol, (index, span)) in names { - if !used_argument_names.contains(symbol.as_str()) { - let msg = format!("named argument `{}` is not used by name", symbol.as_str()); - let arg_span = cx.arg_spans.get(index).copied(); - cx.ecx.buffered_early_lint.push(BufferedEarlyLint { - span: MultiSpan::from_span(span), - msg: msg.clone(), - node_id: ast::CRATE_NODE_ID, - lint_id: LintId::of(&NAMED_ARGUMENTS_USED_POSITIONALLY), - diagnostic: BuiltinLintDiagnostics::NamedArgumentUsedPositionally( - arg_span, - span, - symbol.to_string(), - ), - }); - } + let msg = format!("named argument `{}` is not used by name", named_arg.replacement); + let replacement = match named_arg.ty { + PositionalNamedArgType::Arg => named_arg.replacement.to_string(), + _ => named_arg.replacement.to_string() + "$", + }; + + cx.ecx.buffered_early_lint.push(BufferedEarlyLint { + span: MultiSpan::from_span(named_arg.positional_named_arg_span), + msg: msg.clone(), + node_id: ast::CRATE_NODE_ID, + lint_id: LintId::of(&NAMED_ARGUMENTS_USED_POSITIONALLY), + diagnostic: BuiltinLintDiagnostics::NamedArgumentUsedPositionally( + arg_span, + named_arg.positional_named_arg_span, + replacement, + ), + }); } } @@ -1121,11 +1280,6 @@ pub fn expand_preparsed_format_args( let named_pos: FxHashSet<usize> = names.values().cloned().map(|(i, _)| i).collect(); - // Clone `names` because `names` in Context get updated by verify_piece, which includes usages - // of the names of named arguments, resulting in incorrect errors if a name argument is used - // but not declared, such as: `println!("x = {x}");` - let named_arguments = names.clone(); - let mut cx = Context { ecx, args, @@ -1150,13 +1304,12 @@ pub fn expand_preparsed_format_args( arg_spans, arg_with_formatting: Vec::new(), is_literal: parser.is_literal, + unused_names_lint: PositionalNamedArgsLint { positional_named_args: vec![] }, }; - // This needs to happen *after* the Parser has consumed all pieces to create all the spans. - // unverified_pieces is used later to check named argument names are used, so clone each piece. + // This needs to happen *after* the Parser has consumed all pieces to create all the spans let pieces = unverified_pieces - .iter() - .cloned() + .into_iter() .map(|mut piece| { cx.verify_piece(&piece); cx.resolve_name_inplace(&mut piece); @@ -1166,7 +1319,7 @@ pub fn expand_preparsed_format_args( let numbered_position_args = pieces.iter().any(|arg: &parse::Piece<'_>| match *arg { parse::String(_) => false, - parse::NextArgument(arg) => matches!(arg.position, parse::Position::ArgumentIs(_)), + parse::NextArgument(arg) => matches!(arg.position, parse::Position::ArgumentIs(..)), }); cx.build_index_map(); @@ -1318,11 +1471,10 @@ pub fn expand_preparsed_format_args( } diag.emit(); - } else if cx.invalid_refs.is_empty() && !named_arguments.is_empty() { + } else if cx.invalid_refs.is_empty() && cx.ecx.sess.err_count() == 0 { // Only check for unused named argument names if there are no other errors to avoid causing // too much noise in output errors, such as when a named argument is entirely unused. - // We also only need to perform this check if there are actually named arguments. - lint_named_arguments_used_positionally(named_arguments, &mut cx, unverified_pieces); + create_lints_for_named_arguments_used_positionally(&mut cx); } cx.into_expr() diff --git a/compiler/rustc_builtin_macros/src/trace_macros.rs b/compiler/rustc_builtin_macros/src/trace_macros.rs index c17f2afe494..cc5ae6894e6 100644 --- a/compiler/rustc_builtin_macros/src/trace_macros.rs +++ b/compiler/rustc_builtin_macros/src/trace_macros.rs @@ -11,8 +11,8 @@ pub fn expand_trace_macros( let mut cursor = tt.into_trees(); let mut err = false; let value = match &cursor.next() { - Some(TokenTree::Token(token)) if token.is_keyword(kw::True) => true, - Some(TokenTree::Token(token)) if token.is_keyword(kw::False) => false, + Some(TokenTree::Token(token, _)) if token.is_keyword(kw::True) => true, + Some(TokenTree::Token(token, _)) if token.is_keyword(kw::False) => false, _ => { err = true; false diff --git a/compiler/rustc_codegen_cranelift/.vscode/settings.json b/compiler/rustc_codegen_cranelift/.vscode/settings.json index ecb20f22d8c..d88309e412e 100644 --- a/compiler/rustc_codegen_cranelift/.vscode/settings.json +++ b/compiler/rustc_codegen_cranelift/.vscode/settings.json @@ -1,10 +1,9 @@ { // source for rustc_* is not included in the rust-src component; disable the errors about this "rust-analyzer.diagnostics.disabled": ["unresolved-extern-crate", "unresolved-macro-call"], - "rust-analyzer.assist.importGranularity": "module", - "rust-analyzer.assist.importEnforceGranularity": true, - "rust-analyzer.assist.importPrefix": "crate", - "rust-analyzer.cargo.runBuildScripts": true, + "rust-analyzer.imports.granularity.enforce": true, + "rust-analyzer.imports.granularity.group": "module", + "rust-analyzer.imports.prefix": "crate", "rust-analyzer.cargo.features": ["unstable-features"], "rust-analyzer.linkedProjects": [ "./Cargo.toml", diff --git a/compiler/rustc_codegen_cranelift/Cargo.lock b/compiler/rustc_codegen_cranelift/Cargo.lock index da18ac7eacb..532049c858d 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.lock +++ b/compiler/rustc_codegen_cranelift/Cargo.lock @@ -3,6 +3,17 @@ version = 3 [[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] name = "anyhow" version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -26,6 +37,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -33,56 +50,57 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cranelift-bforest" -version = "0.83.0" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed44413e7e2fe3260d0ed73e6956ab188b69c10ee92b892e401e0f4f6808c68b" +checksum = "749d0d6022c9038dccf480bdde2a38d435937335bf2bb0f14e815d94517cdce8" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.83.0" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5d83f0f26bf213f971f45589d17e5b65e4861f9ed22392b0cbb6eaa5bd329c" +checksum = "e94370cc7b37bf652ccd8bb8f09bd900997f7ccf97520edfc75554bb5c4abbea" dependencies = [ "cranelift-bforest", "cranelift-codegen-meta", "cranelift-codegen-shared", "cranelift-entity", + "cranelift-isle", "gimli", "log", - "regalloc", + "regalloc2", "smallvec", "target-lexicon", ] [[package]] name = "cranelift-codegen-meta" -version = "0.83.0" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6800dc386177df6ecc5a32680607ed8ba1fa0d31a2a59c8c61fbf44826b8191d" +checksum = "e0a3cea8fdab90e44018c5b9a1dfd460d8ee265ac354337150222a354628bdb6" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.83.0" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c961f85070985ebc8fcdb81b838a5cf842294d1e6ed4852446161c7e246fd455" +checksum = "5ac72f76f2698598951ab26d8c96eaa854810e693e7dd52523958b5909fde6b2" [[package]] name = "cranelift-entity" -version = "0.83.0" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2347b2b8d1d5429213668f2a8e36c85ee3c73984a2f6a79007e365d3e575e7ed" +checksum = "09eaeacfcd2356fe0e66b295e8f9d59fdd1ac3ace53ba50de14d628ec902f72d" [[package]] name = "cranelift-frontend" -version = "0.83.0" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cbcdbf7bed29e363568b778649b69dabc3d727256d5d25236096ef693757654" +checksum = "dba69c9980d5ffd62c18a2bde927855fcd7c8dc92f29feaf8636052662cbd99c" dependencies = [ "cranelift-codegen", "log", @@ -91,10 +109,16 @@ dependencies = [ ] [[package]] +name = "cranelift-isle" +version = "0.85.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2920dc1e05cac40304456ed3301fde2c09bd6a9b0210bcfa2f101398d628d5b" + +[[package]] name = "cranelift-jit" -version = "0.83.0" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c769d4e0d76f59c8b2a3bf0477d89ee149bb0731b53fbb245ee081d49063095" +checksum = "1c3c5ed067f2c81577e431f3039148a9c187b33cc79e0d1731fede27d801ec56" dependencies = [ "anyhow", "cranelift-codegen", @@ -110,9 +134,9 @@ dependencies = [ [[package]] name = "cranelift-module" -version = "0.83.0" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab57d399a2401074bb0cc40b3031e420f3d66d46ec0cf21eeae53ac04bd73e2" +checksum = "eee6784303bf9af235237a4885f7417e09a35df896d38ea969a0081064b3ede4" dependencies = [ "anyhow", "cranelift-codegen", @@ -120,9 +144,9 @@ dependencies = [ [[package]] name = "cranelift-native" -version = "0.83.0" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4cdf93552e5ceb2e3c042829ebb4de4378492705f769eadc6a7c6c5251624c" +checksum = "f04dfa45f9b2a6f587c564d6b63388e00cd6589d2df6ea2758cf79e1a13285e6" dependencies = [ "cranelift-codegen", "libc", @@ -131,9 +155,9 @@ dependencies = [ [[package]] name = "cranelift-object" -version = "0.83.0" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf8e65f4839c26e6237fc0744911d79b0a2ac5e76b4e4eebd14db2b8d849fd31" +checksum = "0bf38b2c505db749276793116c0cb30bd096206c7810e471677a453134881881" dependencies = [ "anyhow", "cranelift-codegen", @@ -153,6 +177,26 @@ dependencies = [ ] [[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "getrandom" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] name = "gimli" version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -166,22 +210,31 @@ name = "hashbrown" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "indexmap" -version = "1.8.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", ] [[package]] name = "libc" -version = "0.2.119" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" [[package]] name = "libloading" @@ -219,11 +272,12 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" [[package]] name = "object" -version = "0.27.1" +version = "0.28.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" +checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" dependencies = [ "crc32fast", + "hashbrown 0.11.2", "indexmap", "memchr", ] @@ -235,13 +289,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" [[package]] -name = "regalloc" -version = "0.0.34" +name = "regalloc2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62446b1d3ebf980bdc68837700af1d77b37bc430e524bf95319c6eada2a4cc02" +checksum = "4a8d23b35d7177df3b9d31ed8a9ab4bf625c668be77a319d4f5efd4a5257701c" dependencies = [ + "fxhash", "log", - "rustc-hash", + "slice-group-by", "smallvec", ] @@ -258,12 +313,6 @@ dependencies = [ ] [[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] name = "rustc_codegen_cranelift" version = "0.1.0" dependencies = [ @@ -284,6 +333,12 @@ dependencies = [ ] [[package]] +name = "slice-group-by" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" + +[[package]] name = "smallvec" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -296,6 +351,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7fa7e55043acb85fca6b3c01485a2eeb6b69c5d21002e273c79e465f43b7ac1" [[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/compiler/rustc_codegen_cranelift/Cargo.toml b/compiler/rustc_codegen_cranelift/Cargo.toml index ec2c1f2ca71..61e977e3e69 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.toml +++ b/compiler/rustc_codegen_cranelift/Cargo.toml @@ -8,18 +8,18 @@ crate-type = ["dylib"] [dependencies] # These have to be in sync with each other -cranelift-codegen = { version = "0.83.0", features = ["unwind", "all-arch"] } -cranelift-frontend = "0.83.0" -cranelift-module = "0.83.0" -cranelift-native = "0.83.0" -cranelift-jit = { version = "0.83.0", optional = true } -cranelift-object = "0.83.0" +cranelift-codegen = { version = "0.85.3", features = ["unwind", "all-arch"] } +cranelift-frontend = "0.85.3" +cranelift-module = "0.85.3" +cranelift-native = "0.85.3" +cranelift-jit = { version = "0.85.3", optional = true } +cranelift-object = "0.85.3" target-lexicon = "0.12.0" gimli = { version = "0.26.0", default-features = false, features = ["write"]} -object = { version = "0.27.0", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } +object = { version = "0.28.0", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } ar = { git = "https://github.com/bjorn3/rust-ar.git", branch = "do_not_remove_cg_clif_ranlib" } -indexmap = "1.8.0" +indexmap = "1.9.1" libloading = { version = "0.6.0", optional = true } once_cell = "1.10.0" smallvec = "1.8.1" diff --git a/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock b/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock index efee6ef3f37..7b2cdd27336 100644 --- a/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock +++ b/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock @@ -56,9 +56,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.72" +version = "0.1.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afdbb35d279238cf77f0c9e8d90ad50d6c7bff476ab342baafa29440f0f10bff" +checksum = "c6e3183e88f659a862835db8f4b67dbeed3d93e44dd4927eef78edb1c149d784" dependencies = [ "rustc-std-workspace-core", ] @@ -112,9 +112,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ "compiler_builtins", "rustc-std-workspace-alloc", @@ -123,20 +123,21 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.2.0" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ab7905ea95c6d9af62940f9d7dd9596d54c334ae2c15300c482051292d5637f" +checksum = "7668753748e445859e4e373c3d41117235d9feed578392f5a3a73efdc751ca4a" dependencies = [ "compiler_builtins", "libc", + "rustc-std-workspace-alloc", "rustc-std-workspace-core", ] [[package]] name = "libc" -version = "0.2.125" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" dependencies = [ "rustc-std-workspace-core", ] diff --git a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs index 8682204f4fd..16cce83dd9c 100644 --- a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs +++ b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs @@ -205,7 +205,7 @@ fn build_clif_sysroot_for_triple( { let entry = entry.unwrap(); if let Some(ext) = entry.path().extension() { - if ext == "rmeta" || ext == "d" || ext == "dSYM" { + if ext == "rmeta" || ext == "d" || ext == "dSYM" || ext == "clif" { continue; } } else { diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs index 489259d1a6b..8b6042a3d66 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs @@ -458,7 +458,7 @@ pub trait FnMut<Args>: FnOnce<Args> { #[lang = "panic"] #[track_caller] -pub fn panic(_msg: &str) -> ! { +pub fn panic(_msg: &'static str) -> ! { unsafe { libc::puts("Panicking\n\0" as *const str as *const i8); intrinsics::abort(); @@ -497,7 +497,7 @@ pub trait Deref { #[repr(transparent)] #[rustc_layout_scalar_valid_range_start(1)] #[rustc_nonnull_optimization_guaranteed] -pub struct NonNull<T: ?Sized>(pub *mut T); +pub struct NonNull<T: ?Sized>(pub *const T); impl<T: ?Sized, U: ?Sized> CoerceUnsized<NonNull<U>> for NonNull<T> where T: Unsize<U> {} impl<T: ?Sized, U: ?Sized> DispatchFromDyn<NonNull<U>> for NonNull<T> where T: Unsize<U> {} @@ -521,7 +521,7 @@ impl<T: ?Sized> Drop for Box<T> { } } -impl<T> Deref for Box<T> { +impl<T: ?Sized> Deref for Box<T> { type Target = T; fn deref(&self) -> &Self::Target { diff --git a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs index 0f1245c2758..aa1f239bae2 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs @@ -124,6 +124,23 @@ fn call_return_u128_pair() { return_u128_pair(); } +#[repr(C)] +pub struct bool_11 { + field0: bool, + field1: bool, + field2: bool, + field3: bool, + field4: bool, + field5: bool, + field6: bool, + field7: bool, + field8: bool, + field9: bool, + field10: bool, +} + +extern "C" fn bool_struct_in_11(arg0: bool_11) {} + #[allow(unreachable_code)] // FIXME false positive fn main() { take_unique(Unique { @@ -134,6 +151,20 @@ fn main() { call_return_u128_pair(); + bool_struct_in_11(bool_11 { + field0: true, + field1: true, + field2: true, + field3: true, + field4: true, + field5: true, + field6: true, + field7: true, + field8: true, + field9: true, + field10: true, + }); + let slice = &[0, 1] as &[i32]; let slice_ptr = slice as *const [i32] as *const i32; @@ -299,6 +330,17 @@ fn main() { static REF1: &u8 = &42; static REF2: &u8 = REF1; assert_eq!(*REF1, *REF2); + + extern "C" { + type A; + } + + fn main() { + let x: &A = unsafe { &*(1usize as *const A) }; + + assert_eq!(unsafe { intrinsics::size_of_val(x) }, 0); + assert_eq!(unsafe { intrinsics::min_align_of_val(x) }, 1); +} } #[cfg(all(not(jit), target_arch = "x86_64", target_os = "linux"))] diff --git a/compiler/rustc_codegen_cranelift/example/std_example.rs b/compiler/rustc_codegen_cranelift/example/std_example.rs index 0a2bce2621d..0b5b6cd55d7 100644 --- a/compiler/rustc_codegen_cranelift/example/std_example.rs +++ b/compiler/rustc_codegen_cranelift/example/std_example.rs @@ -128,6 +128,25 @@ fn main() { 0 => loop {}, v => panic(v), }; + + if black_box(false) { + // Based on https://github.com/rust-lang/rust/blob/2f320a224e827b400be25966755a621779f797cc/src/test/ui/debuginfo/debuginfo_with_uninhabitable_field_and_unsized.rs + let _ = Foo::<dyn Send>::new(); + + #[allow(dead_code)] + struct Foo<T: ?Sized> { + base: Never, + value: T, + } + + impl<T: ?Sized> Foo<T> { + pub fn new() -> Box<Foo<T>> { + todo!() + } + } + + enum Never {} + } } fn panic(_: u128) { diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain index e98e92e468e..3ab395d89d5 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain +++ b/compiler/rustc_codegen_cranelift/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-05-15" +channel = "nightly-2022-07-25" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] diff --git a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh index 4d0dfa16c5e..091bfa1e992 100644 --- a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh +++ b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh @@ -29,14 +29,15 @@ diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/ru index 8431aa7b818..a3ff7e68ce5 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs -@@ -3489,11 +3489,7 @@ fn normalize_output(&self, output: &str, custom_rules: &[(String, String)]) -> S - .join("library"); - normalize_path(&src_dir, "$(echo '$SRC_DIR')"); +@@ -3489,12 +3489,7 @@ fn normalize_output(&self, output: &str, custom_rules: &[(String, String)]) -> S + let compiler_src_dir = base_dir.join("compiler"); + normalize_path(&compiler_src_dir, "$(echo '$COMPILER_DIR')"); - if let Some(virtual_rust_source_base_dir) = - option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR").map(PathBuf::from) - { - normalize_path(&virtual_rust_source_base_dir.join("library"), "$(echo '$SRC_DIR')"); +- normalize_path(&virtual_rust_source_base_dir.join("compiler"), "$(echo '$COMPILER_DIR')"); - } + normalize_path(&Path::new("$(cd ../build_sysroot/sysroot_src/library; pwd)"), "$(echo '$SRC_DIR')"); @@ -62,3 +63,6 @@ deny-warnings = false verbose-tests = false EOF popd + +# FIXME remove once inline asm is fully supported +export RUSTFLAGS="$RUSTFLAGS --cfg=rustix_use_libc" diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index 9bdb9f22c54..944787612d8 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -33,6 +33,7 @@ rm src/test/ui/test-attrs/test-fn-signature-verification-for-explicit-return-typ rm src/test/ui/async-await/async-fn-size-moved-locals.rs # -Cpanic=abort shrinks some generator by one byte rm src/test/ui/async-await/async-fn-size-uninit-locals.rs # same rm src/test/ui/generator/size-moved-locals.rs # same +rm -r src/test/ui/macros/rfc-2011-nicer-assert-messages/ # vendor intrinsics rm src/test/ui/sse2.rs # cpuid not supported, so sse2 not detected @@ -65,11 +66,13 @@ rm src/test/ui/intrinsics/intrinsic-nearby.rs # unimplemented nearbyintf32 and n rm src/test/ui/target-feature/missing-plusminus.rs # error not implemented rm src/test/ui/fn/dyn-fn-alignment.rs # wants a 256 byte alignment rm -r src/test/run-make/emit-named-files # requires full --emit support +rm src/test/ui/abi/stack-probes.rs # stack probes not yet implemented # optimization tests # ================== -rm src/test/ui/issues/issue-28950.rs # depends on stack size optimizations +rm src/test/ui/codegen/issue-28950.rs # depends on stack size optimizations rm src/test/ui/codegen/init-large-type.rs # same +rm src/test/ui/issues/issue-40883.rs # same rm -r src/test/run-make/fmt-write-bloat/ # tests an optimization # backend specific tests @@ -89,14 +92,13 @@ rm src/test/ui/consts/issue-33537.rs # same rm -r src/test/run-make/emit-shared-files # requires the rustdoc executable in build/bin/ rm -r src/test/run-make/unstable-flag-required # same rm -r src/test/run-make/rustdoc-* # same +rm -r src/test/run-make/issue-88756-default-output # same +rm -r src/test/run-make/remap-path-prefix-dwarf # requires llvm-dwarfdump # genuine bugs # ============ rm src/test/ui/allocator/no_std-alloc-error-handler-default.rs # missing rust_oom definition -rm -r src/test/ui/polymorphization/ # polymorphization not yet supported -rm src/test/codegen-units/polymorphization/unused_type_parameters.rs # same - 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 @@ -111,6 +113,8 @@ rm src/test/ui/backtrace.rs # TODO warning rm src/test/ui/empty_global_asm.rs # TODO add needs-asm-support rm src/test/ui/simple_global_asm.rs # TODO add needs-asm-support rm src/test/ui/test-attrs/test-type.rs # TODO panic message on stderr. correct stdout +# not sure if this is actually a bug in the test suite, but the symbol list shows the function without leading _ for some reason +rm -r src/test/run-make/native-link-modifier-bundle echo "[TEST] rustc test suite" RUST_TEST_NOCAPTURE=1 COMPILETEST_FORCE_STAGE0=1 ./x.py test --stage 0 src/test/{codegen-units,run-make,run-pass-valgrind,ui,incremental} diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index ffa5d747b11..815450f689e 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -4,6 +4,7 @@ mod comments; mod pass_mode; mod returning; +use cranelift_module::ModuleError; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::ty::layout::FnAbiOf; use rustc_target::abi::call::{Conv, FnAbi}; @@ -69,7 +70,17 @@ pub(crate) fn import_function<'tcx>( ) -> FuncId { let name = tcx.symbol_name(inst).name; let sig = get_function_sig(tcx, module.isa().triple(), inst); - module.declare_function(name, Linkage::Import, &sig).unwrap() + match module.declare_function(name, Linkage::Import, &sig) { + Ok(func_id) => func_id, + Err(ModuleError::IncompatibleDeclaration(_)) => tcx.sess.fatal(&format!( + "attempt to declare `{name}` as function, but it was already declared as static" + )), + Err(ModuleError::IncompatibleSignature(_, prev_sig, new_sig)) => tcx.sess.fatal(&format!( + "attempt to declare `{name}` with signature {new_sig:?}, \ + but it was already declared with signature {prev_sig:?}" + )), + Err(err) => Err::<_, _>(err).unwrap(), + } } impl<'tcx> FunctionCx<'_, '_, 'tcx> { @@ -182,6 +193,15 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_ } let fn_abi = fx.fn_abi.take().unwrap(); + + // FIXME implement variadics in cranelift + if fn_abi.c_variadic { + fx.tcx.sess.span_fatal( + fx.mir.span, + "Defining variadic functions is not yet supported by Cranelift", + ); + } + let mut arg_abis_iter = fn_abi.args.iter(); let func_params = fx @@ -376,9 +396,15 @@ pub(crate) fn codegen_terminator_call<'tcx>( RevealAllLayoutCx(fx.tcx).fn_abi_of_fn_ptr(fn_ty.fn_sig(fx.tcx), extra_args) }; - let is_cold = instance - .map(|inst| fx.tcx.codegen_fn_attrs(inst.def_id()).flags.contains(CodegenFnAttrFlags::COLD)) - .unwrap_or(false); + let is_cold = if fn_sig.abi == Abi::RustCold { + true + } else { + instance + .map(|inst| { + fx.tcx.codegen_fn_attrs(inst.def_id()).flags.contains(CodegenFnAttrFlags::COLD) + }) + .unwrap_or(false) + }; if is_cold { fx.bcx.set_cold_block(fx.bcx.current_block().unwrap()); if let Some(destination_block) = target { diff --git a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs index 9f0bd31e95f..6c10baa53d4 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs @@ -18,9 +18,9 @@ fn reg_to_abi_param(reg: Reg) -> AbiParam { let clif_ty = match (reg.kind, reg.size.bytes()) { (RegKind::Integer, 1) => types::I8, (RegKind::Integer, 2) => types::I16, - (RegKind::Integer, 4) => types::I32, - (RegKind::Integer, 8) => types::I64, - (RegKind::Integer, 16) => types::I128, + (RegKind::Integer, 3..=4) => types::I32, + (RegKind::Integer, 5..=8) => types::I64, + (RegKind::Integer, 9..=16) => types::I128, (RegKind::Float, 4) => types::F32, (RegKind::Float, 8) => types::F64, (RegKind::Vector, size) => types::I8.by(u16::try_from(size).unwrap()).unwrap(), @@ -48,23 +48,9 @@ fn cast_target_to_abi_params(cast: CastTarget) -> SmallVec<[AbiParam; 2]> { ) }; - if cast.prefix.iter().all(|x| x.is_none()) { - // Simplify to a single unit when there is no prefix and size <= unit size - if cast.rest.total <= cast.rest.unit.size { - let clif_ty = match (cast.rest.unit.kind, cast.rest.unit.size.bytes()) { - (RegKind::Integer, 1) => types::I8, - (RegKind::Integer, 2) => types::I16, - (RegKind::Integer, 3..=4) => types::I32, - (RegKind::Integer, 5..=8) => types::I64, - (RegKind::Integer, 9..=16) => types::I128, - (RegKind::Float, 4) => types::F32, - (RegKind::Float, 8) => types::F64, - (RegKind::Vector, size) => types::I8.by(u16::try_from(size).unwrap()).unwrap(), - _ => unreachable!("{:?}", cast.rest.unit), - }; - return smallvec![AbiParam::new(clif_ty)]; - } - } + // Note: Unlike the LLVM equivalent of this code we don't have separate branches for when there + // is no prefix as a single unit, an array and a heterogeneous struct are not represented using + // different types in Cranelift IR. Instead a single array of primitive types is used. // Create list of fields in the main structure let mut args = cast @@ -230,7 +216,7 @@ pub(super) fn adjust_arg_for_abi<'tcx>( arg_abi: &ArgAbi<'tcx, Ty<'tcx>>, is_owned: bool, ) -> SmallVec<[Value; 2]> { - assert_assignable(fx, arg.layout().ty, arg_abi.layout.ty); + assert_assignable(fx, arg.layout().ty, arg_abi.layout.ty, 16); match arg_abi.mode { PassMode::Ignore => smallvec![], PassMode::Direct(_) => smallvec![arg.load_scalar(fx)], diff --git a/compiler/rustc_codegen_cranelift/src/archive.rs b/compiler/rustc_codegen_cranelift/src/archive.rs index 0812f930b5d..c92c1051139 100644 --- a/compiler/rustc_codegen_cranelift/src/archive.rs +++ b/compiler/rustc_codegen_cranelift/src/archive.rs @@ -86,7 +86,7 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { let mut entries = Vec::new(); - for (entry_name, entry) in self.entries { + for (mut entry_name, entry) in self.entries { // FIXME only read the symbol table of the object files to avoid having to keep all // object files in memory at once, or read them twice. let data = match entry { @@ -109,6 +109,23 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { }; if !self.no_builtin_ranlib { + if symbol_table.contains_key(&entry_name) { + // The ar crate can't handle creating a symbol table in case of multiple archive + // members with the same name. Work around this by prepending a number until we + // get a unique name. + for i in 1.. { + let new_name = format!("{}_", i) + .into_bytes() + .into_iter() + .chain(entry_name.iter().copied()) + .collect::<Vec<_>>(); + if !symbol_table.contains_key(&new_name) { + entry_name = new_name; + break; + } + } + } + match object::File::parse(&*data) { Ok(object) => { symbol_table.insert( @@ -204,12 +221,16 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { any_members } - fn inject_dll_import_lib( - &mut self, + fn sess(&self) -> &Session { + self.sess + } + + fn create_dll_import_lib( + _sess: &Session, _lib_name: &str, _dll_imports: &[rustc_session::cstore::DllImport], - _tmpdir: &rustc_data_structures::temp_dir::MaybeTempDir, - ) { - bug!("injecting dll imports is not supported"); + _tmpdir: &Path, + ) -> PathBuf { + bug!("creating dll imports is not supported"); } } diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 54652623d94..122e103ff62 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -175,10 +175,37 @@ fn compile_fn<'tcx>( ); }); + #[cfg(any())] // This is never true + let _clif_guard = { + use std::fmt::Write; + + let func_clone = context.func.clone(); + let clif_comments_clone = clif_comments.clone(); + let mut clif = String::new(); + for flag in module.isa().flags().iter() { + writeln!(clif, "set {}", flag).unwrap(); + } + write!(clif, "target {}", module.isa().triple().architecture.to_string()).unwrap(); + for isa_flag in module.isa().isa_flags().iter() { + write!(clif, " {}", isa_flag).unwrap(); + } + writeln!(clif, "\n").unwrap(); + crate::PrintOnPanic(move || { + let mut clif = clif.clone(); + ::cranelift_codegen::write::decorate_function( + &mut &clif_comments_clone, + &mut clif, + &func_clone, + ) + .unwrap(); + clif + }) + }; + // Define function tcx.sess.time("define function", || { context.want_disasm = crate::pretty_clif::should_write_ir(tcx); - module.define_function(func_id, context).unwrap() + module.define_function(func_id, context).unwrap(); }); // Write optimized function to file for debugging @@ -815,17 +842,8 @@ pub(crate) fn codegen_place<'tcx>( for elem in place.projection { match elem { PlaceElem::Deref => { - if cplace.layout().ty.is_box() { - cplace = cplace - .place_field(fx, Field::new(0)) // Box<T> -> Unique<T> - .place_field(fx, Field::new(0)) // Unique<T> -> NonNull<T> - .place_field(fx, Field::new(0)) // NonNull<T> -> *mut T - .place_deref(fx); - } else { - cplace = cplace.place_deref(fx); - } + cplace = cplace.place_deref(fx); } - PlaceElem::OpaqueCast(ty) => cplace = cplace.place_opaque_cast(fx, ty), PlaceElem::Field(field, _ty) => { cplace = cplace.place_field(fx, field); } diff --git a/compiler/rustc_codegen_cranelift/src/cast.rs b/compiler/rustc_codegen_cranelift/src/cast.rs index b24e49e94c9..bad5d1f08a9 100644 --- a/compiler/rustc_codegen_cranelift/src/cast.rs +++ b/compiler/rustc_codegen_cranelift/src/cast.rs @@ -149,17 +149,8 @@ pub(crate) fn clif_int_or_float_cast( } let is_not_nan = fx.bcx.ins().fcmp(FloatCC::Equal, from, from); - if to_ty == types::I128 { - // FIXME(bytecodealliance/wasmtime#3963): select.i128 on fcmp eq miscompiles - let (lsb, msb) = fx.bcx.ins().isplit(val); - let zero = fx.bcx.ins().iconst(types::I64, 0); - let lsb = fx.bcx.ins().select(is_not_nan, lsb, zero); - let msb = fx.bcx.ins().select(is_not_nan, msb, zero); - fx.bcx.ins().iconcat(lsb, msb) - } else { - let zero = fx.bcx.ins().iconst(to_ty, 0); - fx.bcx.ins().select(is_not_nan, val, zero) - } + let zero = fx.bcx.ins().iconst(to_ty, 0); + fx.bcx.ins().select(is_not_nan, val, zero) } else if from_ty.is_float() && to_ty.is_float() { // float -> float match (from_ty, to_ty) { diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 48972321a9f..7f7fd0e9c57 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -195,9 +195,8 @@ pub(crate) fn codegen_const_value<'tcx>( } Scalar::Ptr(ptr, _size) => { let (alloc_id, offset) = ptr.into_parts(); // we know the `offset` is relative - let alloc_kind = fx.tcx.get_global_alloc(alloc_id); - let base_addr = match alloc_kind { - Some(GlobalAlloc::Memory(alloc)) => { + let base_addr = match fx.tcx.global_alloc(alloc_id) { + GlobalAlloc::Memory(alloc) => { let data_id = data_id_for_alloc_id( &mut fx.constants_cx, fx.module, @@ -211,13 +210,27 @@ pub(crate) fn codegen_const_value<'tcx>( } fx.bcx.ins().global_value(fx.pointer_type, local_data_id) } - Some(GlobalAlloc::Function(instance)) => { + GlobalAlloc::Function(instance) => { let func_id = crate::abi::import_function(fx.tcx, fx.module, instance); let local_func_id = fx.module.declare_func_in_func(func_id, &mut fx.bcx.func); fx.bcx.ins().func_addr(fx.pointer_type, local_func_id) } - Some(GlobalAlloc::Static(def_id)) => { + GlobalAlloc::VTable(ty, trait_ref) => { + let alloc_id = fx.tcx.vtable_allocation((ty, trait_ref)); + let alloc = fx.tcx.global_alloc(alloc_id).unwrap_memory(); + // FIXME: factor this common code with the `Memory` arm into a function? + let data_id = data_id_for_alloc_id( + &mut fx.constants_cx, + fx.module, + alloc_id, + alloc.inner().mutability, + ); + let local_data_id = + fx.module.declare_data_in_func(data_id, &mut fx.bcx.func); + fx.bcx.ins().global_value(fx.pointer_type, local_data_id) + } + GlobalAlloc::Static(def_id) => { assert!(fx.tcx.is_static(def_id)); let data_id = data_id_for_static(fx.tcx, fx.module, def_id, false); let local_data_id = @@ -227,7 +240,6 @@ pub(crate) fn codegen_const_value<'tcx>( } fx.bcx.ins().global_value(fx.pointer_type, local_data_id) } - None => bug!("missing allocation {:?}", alloc_id), }; let val = if offset.bytes() != 0 { fx.bcx.ins().iadd_imm(base_addr, i64::try_from(offset.bytes()).unwrap()) @@ -316,14 +328,18 @@ fn data_id_for_static( let attrs = tcx.codegen_fn_attrs(def_id); - let data_id = module - .declare_data( - &*symbol_name, - linkage, - is_mutable, - attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL), - ) - .unwrap(); + let data_id = match module.declare_data( + &*symbol_name, + linkage, + is_mutable, + attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL), + ) { + Ok(data_id) => data_id, + Err(ModuleError::IncompatibleDeclaration(_)) => tcx.sess.fatal(&format!( + "attempt to declare `{symbol_name}` as static, but it was already declared as function" + )), + Err(err) => Err::<_, _>(err).unwrap(), + }; if rlinkage.is_some() { // Comment copied from https://github.com/rust-lang/rust/blob/45060c2a66dfd667f88bd8b94261b28a58d85bd5/src/librustc_codegen_llvm/consts.rs#L141 @@ -357,10 +373,11 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant while let Some(todo_item) = cx.todo.pop() { let (data_id, alloc, section_name) = match todo_item { TodoItem::Alloc(alloc_id) => { - //println!("alloc_id {}", alloc_id); - let alloc = match tcx.get_global_alloc(alloc_id).unwrap() { + let alloc = match tcx.global_alloc(alloc_id) { GlobalAlloc::Memory(alloc) => alloc, - GlobalAlloc::Function(_) | GlobalAlloc::Static(_) => unreachable!(), + GlobalAlloc::Function(_) | GlobalAlloc::Static(_) | GlobalAlloc::VTable(..) => { + unreachable!() + } }; let data_id = *cx.anon_allocs.entry(alloc_id).or_insert_with(|| { module @@ -424,11 +441,12 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant read_target_uint(endianness, bytes).unwrap() }; - let reloc_target_alloc = tcx.get_global_alloc(alloc_id).unwrap(); + let reloc_target_alloc = tcx.global_alloc(alloc_id); let data_id = match reloc_target_alloc { GlobalAlloc::Function(instance) => { assert_eq!(addend, 0); - let func_id = crate::abi::import_function(tcx, module, instance); + let func_id = + crate::abi::import_function(tcx, module, instance.polymorphize(tcx)); let local_func_id = module.declare_func_in_data(func_id, &mut data_ctx); data_ctx.write_function_addr(offset.bytes() as u32, local_func_id); continue; @@ -436,6 +454,10 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant GlobalAlloc::Memory(target_alloc) => { data_id_for_alloc_id(cx, module, alloc_id, target_alloc.inner().mutability) } + GlobalAlloc::VTable(ty, trait_ref) => { + let alloc_id = tcx.vtable_allocation((ty, trait_ref)); + data_id_for_alloc_id(cx, module, alloc_id, Mutability::Not) + } GlobalAlloc::Static(def_id) => { if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) { diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs index deac5dfd3ec..241de5e3653 100644 --- a/compiler/rustc_codegen_cranelift/src/inline_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs @@ -18,86 +18,96 @@ pub(crate) fn codegen_inline_asm<'tcx>( ) { // FIXME add .eh_frame unwind info directives - if template[0] == InlineAsmTemplatePiece::String("int $$0x29".to_string()) { - let true_ = fx.bcx.ins().iconst(types::I32, 1); - fx.bcx.ins().trapnz(true_, TrapCode::User(1)); - return; - } else if template[0] == InlineAsmTemplatePiece::String("movq %rbx, ".to_string()) - && matches!( - template[1], - InlineAsmTemplatePiece::Placeholder { operand_idx: 0, modifier: Some('r'), span: _ } - ) - && template[2] == InlineAsmTemplatePiece::String("\n".to_string()) - && template[3] == InlineAsmTemplatePiece::String("cpuid".to_string()) - && template[4] == InlineAsmTemplatePiece::String("\n".to_string()) - && template[5] == InlineAsmTemplatePiece::String("xchgq %rbx, ".to_string()) - && matches!( - template[6], - InlineAsmTemplatePiece::Placeholder { operand_idx: 0, modifier: Some('r'), span: _ } - ) - { - assert_eq!(operands.len(), 4); - let (leaf, eax_place) = match operands[1] { - InlineAsmOperand::InOut { reg, late: true, ref in_value, out_place } => { - assert_eq!( - reg, - InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::ax)) - ); - ( - crate::base::codegen_operand(fx, in_value).load_scalar(fx), - crate::base::codegen_place(fx, out_place.unwrap()), - ) - } - _ => unreachable!(), - }; - let ebx_place = match operands[0] { - InlineAsmOperand::Out { reg, late: true, place } => { - assert_eq!( - reg, - InlineAsmRegOrRegClass::RegClass(InlineAsmRegClass::X86( - X86InlineAsmRegClass::reg - )) - ); - crate::base::codegen_place(fx, place.unwrap()) - } - _ => unreachable!(), - }; - let (sub_leaf, ecx_place) = match operands[2] { - InlineAsmOperand::InOut { reg, late: true, ref in_value, out_place } => { - assert_eq!( - reg, - InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::cx)) - ); - ( - crate::base::codegen_operand(fx, in_value).load_scalar(fx), - crate::base::codegen_place(fx, out_place.unwrap()), - ) - } - _ => unreachable!(), - }; - let edx_place = match operands[3] { - InlineAsmOperand::Out { reg, late: true, place } => { - assert_eq!( - reg, - InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::dx)) - ); - crate::base::codegen_place(fx, place.unwrap()) - } - _ => unreachable!(), - }; - - let (eax, ebx, ecx, edx) = crate::intrinsics::codegen_cpuid_call(fx, leaf, sub_leaf); - - eax_place.write_cvalue(fx, CValue::by_val(eax, fx.layout_of(fx.tcx.types.u32))); - ebx_place.write_cvalue(fx, CValue::by_val(ebx, fx.layout_of(fx.tcx.types.u32))); - ecx_place.write_cvalue(fx, CValue::by_val(ecx, fx.layout_of(fx.tcx.types.u32))); - edx_place.write_cvalue(fx, CValue::by_val(edx, fx.layout_of(fx.tcx.types.u32))); - return; - } else if fx.tcx.symbol_name(fx.instance).name.starts_with("___chkstk") { - // ___chkstk, ___chkstk_ms and __alloca are only used on Windows - crate::trap::trap_unimplemented(fx, "Stack probes are not supported"); - } else if fx.tcx.symbol_name(fx.instance).name == "__alloca" { - crate::trap::trap_unimplemented(fx, "Alloca is not supported"); + if !template.is_empty() { + if template[0] == InlineAsmTemplatePiece::String("int $$0x29".to_string()) { + let true_ = fx.bcx.ins().iconst(types::I32, 1); + fx.bcx.ins().trapnz(true_, TrapCode::User(1)); + return; + } else if template[0] == InlineAsmTemplatePiece::String("movq %rbx, ".to_string()) + && matches!( + template[1], + InlineAsmTemplatePiece::Placeholder { + operand_idx: 0, + modifier: Some('r'), + span: _ + } + ) + && template[2] == InlineAsmTemplatePiece::String("\n".to_string()) + && template[3] == InlineAsmTemplatePiece::String("cpuid".to_string()) + && template[4] == InlineAsmTemplatePiece::String("\n".to_string()) + && template[5] == InlineAsmTemplatePiece::String("xchgq %rbx, ".to_string()) + && matches!( + template[6], + InlineAsmTemplatePiece::Placeholder { + operand_idx: 0, + modifier: Some('r'), + span: _ + } + ) + { + assert_eq!(operands.len(), 4); + let (leaf, eax_place) = match operands[1] { + InlineAsmOperand::InOut { reg, late: true, ref in_value, out_place } => { + assert_eq!( + reg, + InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::ax)) + ); + ( + crate::base::codegen_operand(fx, in_value).load_scalar(fx), + crate::base::codegen_place(fx, out_place.unwrap()), + ) + } + _ => unreachable!(), + }; + let ebx_place = match operands[0] { + InlineAsmOperand::Out { reg, late: true, place } => { + assert_eq!( + reg, + InlineAsmRegOrRegClass::RegClass(InlineAsmRegClass::X86( + X86InlineAsmRegClass::reg + )) + ); + crate::base::codegen_place(fx, place.unwrap()) + } + _ => unreachable!(), + }; + let (sub_leaf, ecx_place) = match operands[2] { + InlineAsmOperand::InOut { reg, late: true, ref in_value, out_place } => { + assert_eq!( + reg, + InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::cx)) + ); + ( + crate::base::codegen_operand(fx, in_value).load_scalar(fx), + crate::base::codegen_place(fx, out_place.unwrap()), + ) + } + _ => unreachable!(), + }; + let edx_place = match operands[3] { + InlineAsmOperand::Out { reg, late: true, place } => { + assert_eq!( + reg, + InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::dx)) + ); + crate::base::codegen_place(fx, place.unwrap()) + } + _ => unreachable!(), + }; + + let (eax, ebx, ecx, edx) = crate::intrinsics::codegen_cpuid_call(fx, leaf, sub_leaf); + + eax_place.write_cvalue(fx, CValue::by_val(eax, fx.layout_of(fx.tcx.types.u32))); + ebx_place.write_cvalue(fx, CValue::by_val(ebx, fx.layout_of(fx.tcx.types.u32))); + ecx_place.write_cvalue(fx, CValue::by_val(ecx, fx.layout_of(fx.tcx.types.u32))); + edx_place.write_cvalue(fx, CValue::by_val(edx, fx.layout_of(fx.tcx.types.u32))); + return; + } else if fx.tcx.symbol_name(fx.instance).name.starts_with("___chkstk") { + // ___chkstk, ___chkstk_ms and __alloca are only used on Windows + crate::trap::trap_unimplemented(fx, "Stack probes are not supported"); + } else if fx.tcx.symbol_name(fx.instance).name == "__alloca" { + crate::trap::trap_unimplemented(fx, "Alloca is not supported"); + } } let mut inputs = Vec::new(); diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs index 77ac46540a9..869670c8cfa 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs @@ -13,15 +13,11 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>( ret: CPlace<'tcx>, target: Option<BasicBlock>, ) { - intrinsic_match! { - fx, intrinsic, args, - _ => { - fx.tcx.sess.warn(&format!("unsupported llvm intrinsic {}; replacing with trap", intrinsic)); - crate::trap::trap_unimplemented(fx, intrinsic); - }; - + match intrinsic { // Used by `_mm_movemask_epi8` and `_mm256_movemask_epi8` - "llvm.x86.sse2.pmovmskb.128" | "llvm.x86.avx2.pmovmskb" | "llvm.x86.sse2.movmsk.pd", (c a) { + "llvm.x86.sse2.pmovmskb.128" | "llvm.x86.avx2.pmovmskb" | "llvm.x86.sse2.movmsk.pd" => { + intrinsic_args!(fx, args => (a); intrinsic); + let (lane_count, lane_ty) = a.layout().ty.simd_size_and_type(fx.tcx); let lane_ty = fx.clif_type(lane_ty).unwrap(); assert!(lane_count <= 32); @@ -29,7 +25,8 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>( let mut res = fx.bcx.ins().iconst(types::I32, 0); for lane in (0..lane_count).rev() { - let a_lane = a.value_field(fx, mir::Field::new(lane.try_into().unwrap())).load_scalar(fx); + let a_lane = + a.value_field(fx, mir::Field::new(lane.try_into().unwrap())).load_scalar(fx); // cast float to int let a_lane = match lane_ty { @@ -49,26 +46,29 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>( let res = CValue::by_val(res, fx.layout_of(fx.tcx.types.i32)); ret.write_cvalue(fx, res); - }; - "llvm.x86.sse2.cmp.ps" | "llvm.x86.sse2.cmp.pd", (c x, c y, o kind) { - let kind = crate::constant::mir_operand_get_const_val(fx, kind).expect("llvm.x86.sse2.cmp.* kind not const"); - let flt_cc = match kind.try_to_bits(Size::from_bytes(1)).unwrap_or_else(|| panic!("kind not scalar: {:?}", kind)) { + } + "llvm.x86.sse2.cmp.ps" | "llvm.x86.sse2.cmp.pd" => { + let (x, y, kind) = match args { + [x, y, kind] => (x, y, kind), + _ => bug!("wrong number of args for intrinsic {intrinsic}"), + }; + let x = codegen_operand(fx, x); + let y = codegen_operand(fx, y); + let kind = crate::constant::mir_operand_get_const_val(fx, kind) + .expect("llvm.x86.sse2.cmp.* kind not const"); + + let flt_cc = match kind + .try_to_bits(Size::from_bytes(1)) + .unwrap_or_else(|| panic!("kind not scalar: {:?}", kind)) + { 0 => FloatCC::Equal, 1 => FloatCC::LessThan, 2 => FloatCC::LessThanOrEqual, - 7 => { - unimplemented!("Compares corresponding elements in `a` and `b` to see if neither is `NaN`."); - } - 3 => { - unimplemented!("Compares corresponding elements in `a` and `b` to see if either is `NaN`."); - } + 7 => FloatCC::Ordered, + 3 => FloatCC::Unordered, 4 => FloatCC::NotEqual, - 5 => { - unimplemented!("not less than"); - } - 6 => { - unimplemented!("not less than or equal"); - } + 5 => FloatCC::UnorderedOrGreaterThanOrEqual, + 6 => FloatCC::UnorderedOrGreaterThan, kind => unreachable!("kind {:?}", kind), }; @@ -79,50 +79,67 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>( }; bool_to_zero_or_max_uint(fx, res_lane_ty, res_lane) }); - }; - "llvm.x86.sse2.psrli.d", (c a, o imm8) { - let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8).expect("llvm.x86.sse2.psrli.d imm8 not const"); - simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| { - match imm8.try_to_bits(Size::from_bytes(4)).unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) { - imm8 if imm8 < 32 => fx.bcx.ins().ushr_imm(lane, i64::from(imm8 as u8)), - _ => fx.bcx.ins().iconst(types::I32, 0), - } + } + "llvm.x86.sse2.psrli.d" => { + let (a, imm8) = match args { + [a, imm8] => (a, imm8), + _ => bug!("wrong number of args for intrinsic {intrinsic}"), + }; + let a = codegen_operand(fx, a); + let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8) + .expect("llvm.x86.sse2.psrli.d imm8 not const"); + + simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8 + .try_to_bits(Size::from_bytes(4)) + .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) + { + imm8 if imm8 < 32 => fx.bcx.ins().ushr_imm(lane, i64::from(imm8 as u8)), + _ => fx.bcx.ins().iconst(types::I32, 0), }); - }; - "llvm.x86.sse2.pslli.d", (c a, o imm8) { - let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8).expect("llvm.x86.sse2.psrli.d imm8 not const"); - simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| { - match imm8.try_to_bits(Size::from_bytes(4)).unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) { - imm8 if imm8 < 32 => fx.bcx.ins().ishl_imm(lane, i64::from(imm8 as u8)), - _ => fx.bcx.ins().iconst(types::I32, 0), - } + } + "llvm.x86.sse2.pslli.d" => { + let (a, imm8) = match args { + [a, imm8] => (a, imm8), + _ => bug!("wrong number of args for intrinsic {intrinsic}"), + }; + let a = codegen_operand(fx, a); + let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8) + .expect("llvm.x86.sse2.psrli.d imm8 not const"); + + simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8 + .try_to_bits(Size::from_bytes(4)) + .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) + { + imm8 if imm8 < 32 => fx.bcx.ins().ishl_imm(lane, i64::from(imm8 as u8)), + _ => fx.bcx.ins().iconst(types::I32, 0), }); - }; - "llvm.x86.sse2.storeu.dq", (v mem_addr, c a) { + } + "llvm.x86.sse2.storeu.dq" => { + intrinsic_args!(fx, args => (mem_addr, a); intrinsic); + let mem_addr = mem_addr.load_scalar(fx); + // FIXME correctly handle the unalignment let dest = CPlace::for_ptr(Pointer::new(mem_addr), a.layout()); dest.write_cvalue(fx, a); - }; - "llvm.x86.addcarry.64", (v c_in, c a, c b) { - llvm_add_sub( - fx, - BinOp::Add, - ret, - c_in, - a, - b - ); - }; - "llvm.x86.subborrow.64", (v b_in, c a, c b) { - llvm_add_sub( - fx, - BinOp::Sub, - ret, - b_in, - a, - b - ); - }; + } + "llvm.x86.addcarry.64" => { + intrinsic_args!(fx, args => (c_in, a, b); intrinsic); + let c_in = c_in.load_scalar(fx); + + llvm_add_sub(fx, BinOp::Add, ret, c_in, a, b); + } + "llvm.x86.subborrow.64" => { + intrinsic_args!(fx, args => (b_in, a, b); intrinsic); + let b_in = b_in.load_scalar(fx); + + llvm_add_sub(fx, BinOp::Sub, ret, b_in, a, b); + } + _ => { + fx.tcx + .sess + .warn(&format!("unsupported llvm intrinsic {}; replacing with trap", intrinsic)); + crate::trap::trap_unimplemented(fx, intrinsic); + } } let dest = target.expect("all llvm intrinsics used by stdlib should return"); diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 4b2207f3758..b2a83e1d4eb 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -1,50 +1,14 @@ //! Codegen of intrinsics. This includes `extern "rust-intrinsic"`, `extern "platform-intrinsic"` //! and LLVM intrinsics that have symbol names starting with `llvm.`. -macro_rules! intrinsic_pat { - (_) => { - _ - }; - ($name:ident) => { - sym::$name - }; - (kw.$name:ident) => { - kw::$name - }; - ($name:literal) => { - $name - }; -} - -macro_rules! intrinsic_arg { - (o $fx:expr, $arg:ident) => {}; - (c $fx:expr, $arg:ident) => { - let $arg = codegen_operand($fx, $arg); - }; - (v $fx:expr, $arg:ident) => { - let $arg = codegen_operand($fx, $arg).load_scalar($fx); - }; -} - -macro_rules! intrinsic_match { - ($fx:expr, $intrinsic:expr, $args:expr, - _ => $unknown:block; - $( - $($($name:tt).*)|+ $(if $cond:expr)?, ($($a:ident $arg:ident),*) $content:block; - )*) => { - match $intrinsic { - $( - $(intrinsic_pat!($($name).*))|* $(if $cond)? => { - if let [$($arg),*] = $args { - $(intrinsic_arg!($a $fx, $arg);)* - $content - } else { - bug!("wrong number of args for intrinsic {:?}", $intrinsic); - } - } - )* - _ => $unknown, - } +macro_rules! intrinsic_args { + ($fx:expr, $args:expr => ($($arg:tt),*); $intrinsic:expr) => { + #[allow(unused_parens)] + let ($($arg),*) = if let [$($arg),*] = $args { + ($(codegen_operand($fx, $arg)),*) + } else { + $crate::intrinsics::bug_on_incorrect_arg_count($intrinsic); + }; } } @@ -62,6 +26,10 @@ use rustc_span::symbol::{kw, sym, Symbol}; use crate::prelude::*; use cranelift_codegen::ir::AtomicRmwOp; +fn bug_on_incorrect_arg_count(intrinsic: impl std::fmt::Display) -> ! { + bug!("wrong number of args for intrinsic {}", intrinsic); +} + fn report_atomic_type_validation_error<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, intrinsic: Symbol, @@ -351,28 +319,31 @@ fn codegen_regular_intrinsic_call<'tcx>( ) { let usize_layout = fx.layout_of(fx.tcx.types.usize); - intrinsic_match! { - fx, intrinsic, args, - _ => { - fx.tcx.sess.span_fatal(source_info.span, &format!("unsupported intrinsic {}", intrinsic)); - }; + match intrinsic { + sym::assume => { + intrinsic_args!(fx, args => (_a); intrinsic); + } + sym::likely | sym::unlikely => { + intrinsic_args!(fx, args => (a); intrinsic); - assume, (c _a) {}; - likely | unlikely, (c a) { ret.write_cvalue(fx, a); - }; - breakpoint, () { + } + sym::breakpoint => { + intrinsic_args!(fx, args => (); intrinsic); + fx.bcx.ins().debugtrap(); - }; - copy | copy_nonoverlapping, (v src, v dst, v count) { + } + sym::copy | sym::copy_nonoverlapping => { + intrinsic_args!(fx, args => (src, dst, count); intrinsic); + let src = src.load_scalar(fx); + let dst = dst.load_scalar(fx); + let count = count.load_scalar(fx); + let elem_ty = substs.type_at(0); let elem_size: u64 = fx.layout_of(elem_ty).size.bytes(); assert_eq!(args.len(), 3); - let byte_amount = if elem_size != 1 { - fx.bcx.ins().imul_imm(count, elem_size as i64) - } else { - count - }; + let byte_amount = + if elem_size != 1 { fx.bcx.ins().imul_imm(count, elem_size as i64) } else { count }; if intrinsic == sym::copy_nonoverlapping { // FIXME emit_small_memcpy @@ -381,17 +352,19 @@ fn codegen_regular_intrinsic_call<'tcx>( // FIXME emit_small_memmove fx.bcx.call_memmove(fx.target_config, dst, src, byte_amount); } - }; - // NOTE: the volatile variants have src and dst swapped - volatile_copy_memory | volatile_copy_nonoverlapping_memory, (v dst, v src, v count) { + } + sym::volatile_copy_memory | sym::volatile_copy_nonoverlapping_memory => { + // NOTE: the volatile variants have src and dst swapped + intrinsic_args!(fx, args => (dst, src, count); intrinsic); + let dst = dst.load_scalar(fx); + let src = src.load_scalar(fx); + let count = count.load_scalar(fx); + let elem_ty = substs.type_at(0); let elem_size: u64 = fx.layout_of(elem_ty).size.bytes(); assert_eq!(args.len(), 3); - let byte_amount = if elem_size != 1 { - fx.bcx.ins().imul_imm(count, elem_size as i64) - } else { - count - }; + let byte_amount = + if elem_size != 1 { fx.bcx.ins().imul_imm(count, elem_size as i64) } else { count }; // FIXME make the copy actually volatile when using emit_small_mem{cpy,move} if intrinsic == sym::volatile_copy_nonoverlapping_memory { @@ -401,38 +374,64 @@ fn codegen_regular_intrinsic_call<'tcx>( // FIXME emit_small_memmove fx.bcx.call_memmove(fx.target_config, dst, src, byte_amount); } - }; - size_of_val, (c ptr) { + } + sym::size_of_val => { + intrinsic_args!(fx, args => (ptr); intrinsic); + let layout = fx.layout_of(substs.type_at(0)); - let size = if layout.is_unsized() { + // Note: Can't use is_unsized here as truly unsized types need to take the fixed size + // branch + let size = if let Abi::ScalarPair(_, _) = ptr.layout().abi { let (_ptr, info) = ptr.load_scalar_pair(fx); let (size, _align) = crate::unsize::size_and_align_of_dst(fx, layout, info); size } else { - fx - .bcx - .ins() - .iconst(fx.pointer_type, layout.size.bytes() as i64) + fx.bcx.ins().iconst(fx.pointer_type, layout.size.bytes() as i64) }; ret.write_cvalue(fx, CValue::by_val(size, usize_layout)); - }; - min_align_of_val, (c ptr) { + } + sym::min_align_of_val => { + intrinsic_args!(fx, args => (ptr); intrinsic); + let layout = fx.layout_of(substs.type_at(0)); - let align = if layout.is_unsized() { + // Note: Can't use is_unsized here as truly unsized types need to take the fixed size + // branch + let align = if let Abi::ScalarPair(_, _) = ptr.layout().abi { let (_ptr, info) = ptr.load_scalar_pair(fx); let (_size, align) = crate::unsize::size_and_align_of_dst(fx, layout, info); align } else { - fx - .bcx - .ins() - .iconst(fx.pointer_type, layout.align.abi.bytes() as i64) + fx.bcx.ins().iconst(fx.pointer_type, layout.align.abi.bytes() as i64) }; ret.write_cvalue(fx, CValue::by_val(align, usize_layout)); - }; + } + + sym::vtable_size => { + intrinsic_args!(fx, args => (vtable); intrinsic); + let vtable = vtable.load_scalar(fx); + + let size = crate::vtable::size_of_obj(fx, vtable); + ret.write_cvalue(fx, CValue::by_val(size, usize_layout)); + } + + sym::vtable_align => { + intrinsic_args!(fx, args => (vtable); intrinsic); + let vtable = vtable.load_scalar(fx); + + let align = crate::vtable::min_align_of_obj(fx, vtable); + ret.write_cvalue(fx, CValue::by_val(align, usize_layout)); + } + + sym::unchecked_add + | sym::unchecked_sub + | sym::unchecked_mul + | sym::unchecked_div + | sym::exact_div + | sym::unchecked_rem + | sym::unchecked_shl + | sym::unchecked_shr => { + intrinsic_args!(fx, args => (x, y); intrinsic); - unchecked_add | unchecked_sub | unchecked_mul | unchecked_div | exact_div | unchecked_rem - | unchecked_shl | unchecked_shr, (c x, c y) { // FIXME trap on overflow let bin_op = match intrinsic { sym::unchecked_add => BinOp::Add, @@ -446,8 +445,10 @@ fn codegen_regular_intrinsic_call<'tcx>( }; let res = crate::num::codegen_int_binop(fx, bin_op, x, y); ret.write_cvalue(fx, res); - }; - add_with_overflow | sub_with_overflow | mul_with_overflow, (c x, c y) { + } + sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => { + intrinsic_args!(fx, args => (x, y); intrinsic); + assert_eq!(x.layout().ty, y.layout().ty); let bin_op = match intrinsic { sym::add_with_overflow => BinOp::Add, @@ -456,15 +457,12 @@ fn codegen_regular_intrinsic_call<'tcx>( _ => unreachable!(), }; - let res = crate::num::codegen_checked_int_binop( - fx, - bin_op, - x, - y, - ); + let res = crate::num::codegen_checked_int_binop(fx, bin_op, x, y); ret.write_cvalue(fx, res); - }; - saturating_add | saturating_sub, (c lhs, c rhs) { + } + sym::saturating_add | sym::saturating_sub => { + intrinsic_args!(fx, args => (lhs, rhs); intrinsic); + assert_eq!(lhs.layout().ty, rhs.layout().ty); let bin_op = match intrinsic { sym::saturating_add => BinOp::Add, @@ -474,12 +472,7 @@ fn codegen_regular_intrinsic_call<'tcx>( let signed = type_sign(lhs.layout().ty); - let checked_res = crate::num::codegen_checked_int_binop( - fx, - bin_op, - lhs, - rhs, - ); + let checked_res = crate::num::codegen_checked_int_binop(fx, bin_op, lhs, rhs); let (val, has_overflow) = checked_res.load_scalar_pair(fx); let clif_ty = fx.clif_type(lhs.layout().ty).unwrap(); @@ -491,13 +484,15 @@ fn codegen_regular_intrinsic_call<'tcx>( (sym::saturating_sub, false) => fx.bcx.ins().select(has_overflow, min, val), (sym::saturating_add, true) => { let rhs = rhs.load_scalar(fx); - let rhs_ge_zero = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThanOrEqual, rhs, 0); + let rhs_ge_zero = + fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThanOrEqual, rhs, 0); let sat_val = fx.bcx.ins().select(rhs_ge_zero, max, min); fx.bcx.ins().select(has_overflow, sat_val, val) } (sym::saturating_sub, true) => { let rhs = rhs.load_scalar(fx); - let rhs_ge_zero = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThanOrEqual, rhs, 0); + let rhs_ge_zero = + fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThanOrEqual, rhs, 0); let sat_val = fx.bcx.ins().select(rhs_ge_zero, min, max); fx.bcx.ins().select(has_overflow, sat_val, val) } @@ -507,23 +502,32 @@ fn codegen_regular_intrinsic_call<'tcx>( let res = CValue::by_val(val, lhs.layout()); ret.write_cvalue(fx, res); - }; - rotate_left, (c x, v y) { + } + sym::rotate_left => { + intrinsic_args!(fx, args => (x, y); intrinsic); + let y = y.load_scalar(fx); + let layout = x.layout(); let x = x.load_scalar(fx); let res = fx.bcx.ins().rotl(x, y); ret.write_cvalue(fx, CValue::by_val(res, layout)); - }; - rotate_right, (c x, v y) { + } + sym::rotate_right => { + intrinsic_args!(fx, args => (x, y); intrinsic); + let y = y.load_scalar(fx); + let layout = x.layout(); let x = x.load_scalar(fx); let res = fx.bcx.ins().rotr(x, y); ret.write_cvalue(fx, CValue::by_val(res, layout)); - }; + } // The only difference between offset and arith_offset is regarding UB. Because Cranelift // doesn't have UB both are codegen'ed the same way - offset | arith_offset, (c base, v offset) { + sym::offset | sym::arith_offset => { + intrinsic_args!(fx, args => (base, offset); intrinsic); + let offset = offset.load_scalar(fx); + let pointee_ty = base.layout().ty.builtin_deref(true).unwrap().ty; let pointee_size = fx.layout_of(pointee_ty).size.bytes(); let ptr_diff = if pointee_size != 1 { @@ -534,12 +538,18 @@ fn codegen_regular_intrinsic_call<'tcx>( let base_val = base.load_scalar(fx); let res = fx.bcx.ins().iadd(base_val, ptr_diff); ret.write_cvalue(fx, CValue::by_val(res, base.layout())); - }; + } + + sym::transmute => { + intrinsic_args!(fx, args => (from); intrinsic); - transmute, (c from) { ret.write_cvalue_transmute(fx, from); - }; - write_bytes | volatile_set_memory, (c dst, v val, v count) { + } + sym::write_bytes | sym::volatile_set_memory => { + intrinsic_args!(fx, args => (dst, val, count); intrinsic); + let val = val.load_scalar(fx); + let count = count.load_scalar(fx); + let pointee_ty = dst.layout().ty.builtin_deref(true).unwrap().ty; let pointee_size = fx.layout_of(pointee_ty).size.bytes(); let count = if pointee_size != 1 { @@ -551,34 +561,42 @@ fn codegen_regular_intrinsic_call<'tcx>( // FIXME make the memset actually volatile when switching to emit_small_memset // FIXME use emit_small_memset fx.bcx.call_memset(fx.target_config, dst_ptr, val, count); - }; - ctlz | ctlz_nonzero, (c arg) { + } + sym::ctlz | sym::ctlz_nonzero => { + intrinsic_args!(fx, args => (arg); intrinsic); let val = arg.load_scalar(fx); + // FIXME trap on `ctlz_nonzero` with zero arg. let res = fx.bcx.ins().clz(val); let res = CValue::by_val(res, arg.layout()); ret.write_cvalue(fx, res); - }; - cttz | cttz_nonzero, (c arg) { + } + sym::cttz | sym::cttz_nonzero => { + intrinsic_args!(fx, args => (arg); intrinsic); let val = arg.load_scalar(fx); + // FIXME trap on `cttz_nonzero` with zero arg. let res = fx.bcx.ins().ctz(val); let res = CValue::by_val(res, arg.layout()); ret.write_cvalue(fx, res); - }; - ctpop, (c arg) { + } + sym::ctpop => { + intrinsic_args!(fx, args => (arg); intrinsic); let val = arg.load_scalar(fx); + let res = fx.bcx.ins().popcnt(val); let res = CValue::by_val(res, arg.layout()); ret.write_cvalue(fx, res); - }; - bitreverse, (c arg) { + } + sym::bitreverse => { + intrinsic_args!(fx, args => (arg); intrinsic); let val = arg.load_scalar(fx); + let res = fx.bcx.ins().bitrev(val); let res = CValue::by_val(res, arg.layout()); ret.write_cvalue(fx, res); - }; - bswap, (c arg) { + } + sym::bswap => { // FIXME(CraneStation/cranelift#794) add bswap instruction to cranelift fn swap(bcx: &mut FunctionBuilder<'_>, v: Value) -> Value { match bcx.func.dfg.value_type(v) { @@ -654,11 +672,15 @@ fn codegen_regular_intrinsic_call<'tcx>( ty => unreachable!("bswap {}", ty), } } + intrinsic_args!(fx, args => (arg); intrinsic); let val = arg.load_scalar(fx); + let res = CValue::by_val(swap(&mut fx.bcx, val), arg.layout()); ret.write_cvalue(fx, res); - }; - assert_inhabited | assert_zero_valid | assert_uninit_valid, () { + } + sym::assert_inhabited | sym::assert_zero_valid | sym::assert_uninit_valid => { + intrinsic_args!(fx, args => (); intrinsic); + let layout = fx.layout_of(substs.type_at(0)); if layout.abi.is_uninhabited() { with_no_trimmed_paths!({ @@ -675,7 +697,10 @@ fn codegen_regular_intrinsic_call<'tcx>( with_no_trimmed_paths!({ crate::base::codegen_panic( fx, - &format!("attempted to zero-initialize type `{}`, which is invalid", layout.ty), + &format!( + "attempted to zero-initialize type `{}`, which is invalid", + layout.ty + ), source_info, ); }); @@ -686,41 +711,53 @@ fn codegen_regular_intrinsic_call<'tcx>( with_no_trimmed_paths!({ crate::base::codegen_panic( fx, - &format!("attempted to leave type `{}` uninitialized, which is invalid", layout.ty), + &format!( + "attempted to leave type `{}` uninitialized, which is invalid", + layout.ty + ), source_info, ) }); return; } - }; + } + + sym::volatile_load | sym::unaligned_volatile_load => { + intrinsic_args!(fx, args => (ptr); intrinsic); - volatile_load | unaligned_volatile_load, (c ptr) { // Cranelift treats loads as volatile by default // FIXME correctly handle unaligned_volatile_load - let inner_layout = - fx.layout_of(ptr.layout().ty.builtin_deref(true).unwrap().ty); + let inner_layout = fx.layout_of(ptr.layout().ty.builtin_deref(true).unwrap().ty); let val = CValue::by_ref(Pointer::new(ptr.load_scalar(fx)), inner_layout); ret.write_cvalue(fx, val); - }; - volatile_store | unaligned_volatile_store, (v ptr, c val) { + } + sym::volatile_store | sym::unaligned_volatile_store => { + intrinsic_args!(fx, args => (ptr, val); intrinsic); + let ptr = ptr.load_scalar(fx); + // Cranelift treats stores as volatile by default // FIXME correctly handle unaligned_volatile_store let dest = CPlace::for_ptr(Pointer::new(ptr), val.layout()); dest.write_cvalue(fx, val); - }; + } + + sym::pref_align_of + | sym::needs_drop + | sym::type_id + | sym::type_name + | sym::variant_count => { + intrinsic_args!(fx, args => (); intrinsic); - pref_align_of | needs_drop | type_id | type_name | variant_count, () { let const_val = fx.tcx.const_eval_instance(ParamEnv::reveal_all(), instance, None).unwrap(); - let val = crate::constant::codegen_const_value( - fx, - const_val, - ret.layout().ty, - ); + let val = crate::constant::codegen_const_value(fx, const_val, ret.layout().ty); ret.write_cvalue(fx, val); - }; + } - ptr_offset_from | ptr_offset_from_unsigned, (v ptr, v base) { + sym::ptr_offset_from | sym::ptr_offset_from_unsigned => { + intrinsic_args!(fx, args => (ptr, base); intrinsic); + let ptr = ptr.load_scalar(fx); + let base = base.load_scalar(fx); let ty = substs.type_at(0); let pointee_size: u64 = fx.layout_of(ty).size.bytes(); @@ -736,31 +773,44 @@ fn codegen_regular_intrinsic_call<'tcx>( CValue::by_val(fx.bcx.ins().sdiv_imm(diff_bytes, pointee_size as i64), isize_layout) }; ret.write_cvalue(fx, val); - }; + } + + sym::ptr_guaranteed_eq => { + intrinsic_args!(fx, args => (a, b); intrinsic); - ptr_guaranteed_eq, (c a, c b) { let val = crate::num::codegen_ptr_binop(fx, BinOp::Eq, a, b); ret.write_cvalue(fx, val); - }; + } + + sym::ptr_guaranteed_ne => { + intrinsic_args!(fx, args => (a, b); intrinsic); - ptr_guaranteed_ne, (c a, c b) { let val = crate::num::codegen_ptr_binop(fx, BinOp::Ne, a, b); ret.write_cvalue(fx, val); - }; + } + + sym::caller_location => { + intrinsic_args!(fx, args => (); intrinsic); - caller_location, () { let caller_location = fx.get_caller_location(source_info); ret.write_cvalue(fx, caller_location); - }; + } + + _ if intrinsic.as_str().starts_with("atomic_fence") => { + intrinsic_args!(fx, args => (); intrinsic); - _ if intrinsic.as_str().starts_with("atomic_fence"), () { fx.bcx.ins().fence(); - }; - _ if intrinsic.as_str().starts_with("atomic_singlethreadfence"), () { + } + _ if intrinsic.as_str().starts_with("atomic_singlethreadfence") => { + intrinsic_args!(fx, args => (); intrinsic); + // FIXME use a compiler fence once Cranelift supports it fx.bcx.ins().fence(); - }; - _ if intrinsic.as_str().starts_with("atomic_load"), (v ptr) { + } + _ if intrinsic.as_str().starts_with("atomic_load") => { + intrinsic_args!(fx, args => (ptr); intrinsic); + let ptr = ptr.load_scalar(fx); + let ty = substs.type_at(0); match ty.kind() { ty::Uint(UintTy::U128) | ty::Int(IntTy::I128) => { @@ -772,7 +822,9 @@ fn codegen_regular_intrinsic_call<'tcx>( fx.bcx.ins().jump(ret_block, &[]); return; } else { - fx.tcx.sess.span_fatal(source_info.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(..) => {} @@ -787,8 +839,11 @@ fn codegen_regular_intrinsic_call<'tcx>( let val = CValue::by_val(val, fx.layout_of(ty)); ret.write_cvalue(fx, val); - }; - _ if intrinsic.as_str().starts_with("atomic_store"), (v ptr, c val) { + } + _ if intrinsic.as_str().starts_with("atomic_store") => { + intrinsic_args!(fx, args => (ptr, val); intrinsic); + let ptr = ptr.load_scalar(fx); + let ty = substs.type_at(0); match ty.kind() { ty::Uint(UintTy::U128) | ty::Int(IntTy::I128) => { @@ -800,7 +855,9 @@ fn codegen_regular_intrinsic_call<'tcx>( fx.bcx.ins().jump(ret_block, &[]); return; } else { - fx.tcx.sess.span_fatal(source_info.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(..) => {} @@ -813,8 +870,11 @@ fn codegen_regular_intrinsic_call<'tcx>( let val = val.load_scalar(fx); fx.bcx.ins().atomic_store(MemFlags::trusted(), val, ptr); - }; - _ if intrinsic.as_str().starts_with("atomic_xchg"), (v ptr, c new) { + } + _ if intrinsic.as_str().starts_with("atomic_xchg") => { + intrinsic_args!(fx, args => (ptr, new); intrinsic); + let ptr = ptr.load_scalar(fx); + let layout = new.layout(); match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} @@ -831,8 +891,12 @@ fn codegen_regular_intrinsic_call<'tcx>( let old = CValue::by_val(old, layout); ret.write_cvalue(fx, old); - }; - _ if intrinsic.as_str().starts_with("atomic_cxchg"), (v ptr, c test_old, c new) { // both atomic_cxchg_* and atomic_cxchgweak_* + } + _ if intrinsic.as_str().starts_with("atomic_cxchg") => { + // both atomic_cxchg_* and atomic_cxchgweak_* + intrinsic_args!(fx, args => (ptr, test_old, new); intrinsic); + let ptr = ptr.load_scalar(fx); + let layout = new.layout(); match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} @@ -848,11 +912,15 @@ fn codegen_regular_intrinsic_call<'tcx>( let old = fx.bcx.ins().atomic_cas(MemFlags::trusted(), ptr, test_old, new); let is_eq = fx.bcx.ins().icmp(IntCC::Equal, old, test_old); - let ret_val = CValue::by_val_pair(old, fx.bcx.ins().bint(types::I8, is_eq), ret.layout()); + let ret_val = + CValue::by_val_pair(old, fx.bcx.ins().bint(types::I8, is_eq), ret.layout()); ret.write_cvalue(fx, ret_val) - }; + } + + _ if intrinsic.as_str().starts_with("atomic_xadd") => { + intrinsic_args!(fx, args => (ptr, amount); intrinsic); + let ptr = ptr.load_scalar(fx); - _ if intrinsic.as_str().starts_with("atomic_xadd"), (v ptr, c amount) { let layout = amount.layout(); match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} @@ -865,12 +933,16 @@ fn codegen_regular_intrinsic_call<'tcx>( let amount = amount.load_scalar(fx); - let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Add, ptr, amount); + let old = + fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Add, ptr, amount); let old = CValue::by_val(old, layout); ret.write_cvalue(fx, old); - }; - _ if intrinsic.as_str().starts_with("atomic_xsub"), (v ptr, c amount) { + } + _ if intrinsic.as_str().starts_with("atomic_xsub") => { + intrinsic_args!(fx, args => (ptr, amount); intrinsic); + let ptr = ptr.load_scalar(fx); + let layout = amount.layout(); match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} @@ -883,12 +955,16 @@ fn codegen_regular_intrinsic_call<'tcx>( let amount = amount.load_scalar(fx); - let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Sub, ptr, amount); + let old = + fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Sub, ptr, amount); let old = CValue::by_val(old, layout); ret.write_cvalue(fx, old); - }; - _ if intrinsic.as_str().starts_with("atomic_and"), (v ptr, c src) { + } + _ if intrinsic.as_str().starts_with("atomic_and") => { + intrinsic_args!(fx, args => (ptr, src); intrinsic); + let ptr = ptr.load_scalar(fx); + let layout = src.layout(); match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} @@ -905,8 +981,11 @@ fn codegen_regular_intrinsic_call<'tcx>( let old = CValue::by_val(old, layout); ret.write_cvalue(fx, old); - }; - _ if intrinsic.as_str().starts_with("atomic_or"), (v ptr, c src) { + } + _ if intrinsic.as_str().starts_with("atomic_or") => { + intrinsic_args!(fx, args => (ptr, src); intrinsic); + let ptr = ptr.load_scalar(fx); + let layout = src.layout(); match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} @@ -923,8 +1002,11 @@ fn codegen_regular_intrinsic_call<'tcx>( let old = CValue::by_val(old, layout); ret.write_cvalue(fx, old); - }; - _ if intrinsic.as_str().starts_with("atomic_xor"), (v ptr, c src) { + } + _ if intrinsic.as_str().starts_with("atomic_xor") => { + intrinsic_args!(fx, args => (ptr, src); intrinsic); + let ptr = ptr.load_scalar(fx); + let layout = src.layout(); match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} @@ -941,8 +1023,11 @@ fn codegen_regular_intrinsic_call<'tcx>( let old = CValue::by_val(old, layout); ret.write_cvalue(fx, old); - }; - _ if intrinsic.as_str().starts_with("atomic_nand"), (v ptr, c src) { + } + _ if intrinsic.as_str().starts_with("atomic_nand") => { + intrinsic_args!(fx, args => (ptr, src); intrinsic); + let ptr = ptr.load_scalar(fx); + let layout = src.layout(); match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} @@ -959,8 +1044,11 @@ fn codegen_regular_intrinsic_call<'tcx>( let old = CValue::by_val(old, layout); ret.write_cvalue(fx, old); - }; - _ if intrinsic.as_str().starts_with("atomic_max"), (v ptr, c src) { + } + _ if intrinsic.as_str().starts_with("atomic_max") => { + intrinsic_args!(fx, args => (ptr, src); intrinsic); + let ptr = ptr.load_scalar(fx); + let layout = src.layout(); match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} @@ -977,8 +1065,11 @@ fn codegen_regular_intrinsic_call<'tcx>( let old = CValue::by_val(old, layout); ret.write_cvalue(fx, old); - }; - _ if intrinsic.as_str().starts_with("atomic_umax"), (v ptr, c src) { + } + _ if intrinsic.as_str().starts_with("atomic_umax") => { + intrinsic_args!(fx, args => (ptr, src); intrinsic); + let ptr = ptr.load_scalar(fx); + let layout = src.layout(); match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} @@ -995,8 +1086,11 @@ fn codegen_regular_intrinsic_call<'tcx>( let old = CValue::by_val(old, layout); ret.write_cvalue(fx, old); - }; - _ if intrinsic.as_str().starts_with("atomic_min"), (v ptr, c src) { + } + _ if intrinsic.as_str().starts_with("atomic_min") => { + intrinsic_args!(fx, args => (ptr, src); intrinsic); + let ptr = ptr.load_scalar(fx); + let layout = src.layout(); match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} @@ -1013,8 +1107,11 @@ fn codegen_regular_intrinsic_call<'tcx>( let old = CValue::by_val(old, layout); ret.write_cvalue(fx, old); - }; - _ if intrinsic.as_str().starts_with("atomic_umin"), (v ptr, c src) { + } + _ if intrinsic.as_str().starts_with("atomic_umin") => { + intrinsic_args!(fx, args => (ptr, src); intrinsic); + let ptr = ptr.load_scalar(fx); + let layout = src.layout(); match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} @@ -1031,30 +1128,51 @@ fn codegen_regular_intrinsic_call<'tcx>( let old = CValue::by_val(old, layout); ret.write_cvalue(fx, old); - }; + } + + sym::minnumf32 => { + intrinsic_args!(fx, args => (a, b); intrinsic); + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); - minnumf32, (v a, v b) { let val = crate::num::codegen_float_min(fx, a, b); let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f32)); ret.write_cvalue(fx, val); - }; - minnumf64, (v a, v b) { + } + sym::minnumf64 => { + intrinsic_args!(fx, args => (a, b); intrinsic); + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + let val = crate::num::codegen_float_min(fx, a, b); let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f64)); ret.write_cvalue(fx, val); - }; - maxnumf32, (v a, v b) { + } + sym::maxnumf32 => { + intrinsic_args!(fx, args => (a, b); intrinsic); + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + let val = crate::num::codegen_float_max(fx, a, b); let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f32)); ret.write_cvalue(fx, val); - }; - maxnumf64, (v a, v b) { + } + sym::maxnumf64 => { + intrinsic_args!(fx, args => (a, b); intrinsic); + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + let val = crate::num::codegen_float_max(fx, a, b); let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f64)); ret.write_cvalue(fx, val); - }; + } + + kw::Try => { + intrinsic_args!(fx, args => (f, data, catch_fn); intrinsic); + let f = f.load_scalar(fx); + let data = data.load_scalar(fx); + let _catch_fn = catch_fn.load_scalar(fx); - kw.Try, (v f, v data, v _catch_fn) { // FIXME once unwinding is supported, change this to actually catch panics let f_sig = fx.bcx.func.import_signature(Signature { call_conv: fx.target_config.default_call_conv, @@ -1067,20 +1185,30 @@ fn codegen_regular_intrinsic_call<'tcx>( let layout = ret.layout(); let ret_val = CValue::const_val(fx, layout, ty::ScalarInt::null(layout.size)); ret.write_cvalue(fx, ret_val); - }; + } - fadd_fast | fsub_fast | fmul_fast | fdiv_fast | frem_fast, (c x, c y) { - let res = crate::num::codegen_float_binop(fx, match intrinsic { - sym::fadd_fast => BinOp::Add, - sym::fsub_fast => BinOp::Sub, - sym::fmul_fast => BinOp::Mul, - sym::fdiv_fast => BinOp::Div, - sym::frem_fast => BinOp::Rem, - _ => unreachable!(), - }, x, y); + sym::fadd_fast | sym::fsub_fast | sym::fmul_fast | sym::fdiv_fast | sym::frem_fast => { + intrinsic_args!(fx, args => (x, y); intrinsic); + + let res = crate::num::codegen_float_binop( + fx, + match intrinsic { + sym::fadd_fast => BinOp::Add, + sym::fsub_fast => BinOp::Sub, + sym::fmul_fast => BinOp::Mul, + sym::fdiv_fast => BinOp::Div, + sym::frem_fast => BinOp::Rem, + _ => unreachable!(), + }, + x, + y, + ); ret.write_cvalue(fx, res); - }; - float_to_int_unchecked, (v f) { + } + sym::float_to_int_unchecked => { + intrinsic_args!(fx, args => (f); intrinsic); + let f = f.load_scalar(fx); + let res = crate::cast::clif_int_or_float_cast( fx, f, @@ -1089,52 +1217,74 @@ fn codegen_regular_intrinsic_call<'tcx>( type_sign(ret.layout().ty), ); ret.write_cvalue(fx, CValue::by_val(res, ret.layout())); - }; + } + + sym::raw_eq => { + intrinsic_args!(fx, args => (lhs_ref, rhs_ref); intrinsic); + let lhs_ref = lhs_ref.load_scalar(fx); + let rhs_ref = rhs_ref.load_scalar(fx); - raw_eq, (v lhs_ref, v rhs_ref) { let size = fx.layout_of(substs.type_at(0)).layout.size(); // FIXME add and use emit_small_memcmp - let is_eq_value = - if size == Size::ZERO { - // No bytes means they're trivially equal - fx.bcx.ins().iconst(types::I8, 1) - } else if let Some(clty) = size.bits().try_into().ok().and_then(Type::int) { - // Can't use `trusted` for these loads; they could be unaligned. - let mut flags = MemFlags::new(); - flags.set_notrap(); - let lhs_val = fx.bcx.ins().load(clty, flags, lhs_ref, 0); - let rhs_val = fx.bcx.ins().load(clty, flags, rhs_ref, 0); - let eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_val, rhs_val); - fx.bcx.ins().bint(types::I8, eq) - } else { - // Just call `memcmp` (like slices do in core) when the - // size is too large or it's not a power-of-two. - let signed_bytes = i64::try_from(size.bytes()).unwrap(); - let bytes_val = fx.bcx.ins().iconst(fx.pointer_type, signed_bytes); - let params = vec![AbiParam::new(fx.pointer_type); 3]; - let returns = vec![AbiParam::new(types::I32)]; - let args = &[lhs_ref, rhs_ref, bytes_val]; - let cmp = fx.lib_call("memcmp", params, returns, args)[0]; - let eq = fx.bcx.ins().icmp_imm(IntCC::Equal, cmp, 0); - fx.bcx.ins().bint(types::I8, eq) - }; + let is_eq_value = if size == Size::ZERO { + // No bytes means they're trivially equal + fx.bcx.ins().iconst(types::I8, 1) + } else if let Some(clty) = size.bits().try_into().ok().and_then(Type::int) { + // Can't use `trusted` for these loads; they could be unaligned. + let mut flags = MemFlags::new(); + flags.set_notrap(); + let lhs_val = fx.bcx.ins().load(clty, flags, lhs_ref, 0); + let rhs_val = fx.bcx.ins().load(clty, flags, rhs_ref, 0); + let eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_val, rhs_val); + fx.bcx.ins().bint(types::I8, eq) + } else { + // Just call `memcmp` (like slices do in core) when the + // size is too large or it's not a power-of-two. + let signed_bytes = i64::try_from(size.bytes()).unwrap(); + let bytes_val = fx.bcx.ins().iconst(fx.pointer_type, signed_bytes); + let params = vec![AbiParam::new(fx.pointer_type); 3]; + let returns = vec![AbiParam::new(types::I32)]; + let args = &[lhs_ref, rhs_ref, bytes_val]; + let cmp = fx.lib_call("memcmp", params, returns, args)[0]; + let eq = fx.bcx.ins().icmp_imm(IntCC::Equal, cmp, 0); + fx.bcx.ins().bint(types::I8, eq) + }; ret.write_cvalue(fx, CValue::by_val(is_eq_value, ret.layout())); - }; + } + + sym::const_allocate => { + intrinsic_args!(fx, args => (_size, _align); intrinsic); - const_allocate, (c _size, c _align) { // returns a null pointer at runtime. let null = fx.bcx.ins().iconst(fx.pointer_type, 0); ret.write_cvalue(fx, CValue::by_val(null, ret.layout())); - }; + } - const_deallocate, (c _ptr, c _size, c _align) { + sym::const_deallocate => { + intrinsic_args!(fx, args => (_ptr, _size, _align); intrinsic); // nop at runtime. - }; + } + + sym::black_box => { + intrinsic_args!(fx, args => (a); intrinsic); - black_box, (c a) { // FIXME implement black_box semantics ret.write_cvalue(fx, a); - }; + } + + // FIXME implement variadics in cranelift + sym::va_copy | sym::va_arg | sym::va_end => { + fx.tcx.sess.span_fatal( + source_info.span, + "Defining variadic functions is not yet supported by Cranelift", + ); + } + + _ => { + fx.tcx + .sess + .span_fatal(source_info.span, &format!("unsupported intrinsic {}", intrinsic)); + } } let ret_block = fx.get_block(destination.unwrap()); diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs index d1ca9edf2e0..30e3d112594 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs @@ -25,13 +25,10 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( ret: CPlace<'tcx>, span: Span, ) { - intrinsic_match! { - fx, intrinsic, args, - _ => { - fx.tcx.sess.span_fatal(span, &format!("Unknown SIMD intrinsic {}", intrinsic)); - }; + match intrinsic { + sym::simd_cast => { + intrinsic_args!(fx, args => (a); intrinsic); - simd_cast, (c a) { if !a.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, a.layout().ty); return; @@ -45,9 +42,11 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( clif_int_or_float_cast(fx, lane, from_signed, ret_lane_clif_ty, to_signed) }); - }; + } + + sym::simd_eq | sym::simd_ne | sym::simd_lt | sym::simd_le | sym::simd_gt | sym::simd_ge => { + intrinsic_args!(fx, args => (x, y); intrinsic); - simd_eq | simd_ne | simd_lt | simd_le | simd_gt | simd_ge, (c x, c y) { if !x.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, x.layout().ty); return; @@ -57,7 +56,9 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( simd_pair_for_each_lane(fx, x, y, ret, &|fx, lane_ty, res_lane_ty, x_lane, y_lane| { let res_lane = match (lane_ty.kind(), intrinsic) { (ty::Uint(_), sym::simd_eq) => fx.bcx.ins().icmp(IntCC::Equal, x_lane, y_lane), - (ty::Uint(_), sym::simd_ne) => fx.bcx.ins().icmp(IntCC::NotEqual, x_lane, y_lane), + (ty::Uint(_), sym::simd_ne) => { + fx.bcx.ins().icmp(IntCC::NotEqual, x_lane, y_lane) + } (ty::Uint(_), sym::simd_lt) => { fx.bcx.ins().icmp(IntCC::UnsignedLessThan, x_lane, y_lane) } @@ -72,8 +73,12 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( } (ty::Int(_), sym::simd_eq) => fx.bcx.ins().icmp(IntCC::Equal, x_lane, y_lane), - (ty::Int(_), sym::simd_ne) => fx.bcx.ins().icmp(IntCC::NotEqual, x_lane, y_lane), - (ty::Int(_), sym::simd_lt) => fx.bcx.ins().icmp(IntCC::SignedLessThan, x_lane, y_lane), + (ty::Int(_), sym::simd_ne) => { + fx.bcx.ins().icmp(IntCC::NotEqual, x_lane, y_lane) + } + (ty::Int(_), sym::simd_lt) => { + fx.bcx.ins().icmp(IntCC::SignedLessThan, x_lane, y_lane) + } (ty::Int(_), sym::simd_le) => { fx.bcx.ins().icmp(IntCC::SignedLessThanOrEqual, x_lane, y_lane) } @@ -84,13 +89,21 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( fx.bcx.ins().icmp(IntCC::SignedGreaterThanOrEqual, x_lane, y_lane) } - (ty::Float(_), sym::simd_eq) => fx.bcx.ins().fcmp(FloatCC::Equal, x_lane, y_lane), - (ty::Float(_), sym::simd_ne) => fx.bcx.ins().fcmp(FloatCC::NotEqual, x_lane, y_lane), - (ty::Float(_), sym::simd_lt) => fx.bcx.ins().fcmp(FloatCC::LessThan, x_lane, y_lane), + (ty::Float(_), sym::simd_eq) => { + fx.bcx.ins().fcmp(FloatCC::Equal, x_lane, y_lane) + } + (ty::Float(_), sym::simd_ne) => { + fx.bcx.ins().fcmp(FloatCC::NotEqual, x_lane, y_lane) + } + (ty::Float(_), sym::simd_lt) => { + fx.bcx.ins().fcmp(FloatCC::LessThan, x_lane, y_lane) + } (ty::Float(_), sym::simd_le) => { fx.bcx.ins().fcmp(FloatCC::LessThanOrEqual, x_lane, y_lane) } - (ty::Float(_), sym::simd_gt) => fx.bcx.ins().fcmp(FloatCC::GreaterThan, x_lane, y_lane), + (ty::Float(_), sym::simd_gt) => { + fx.bcx.ins().fcmp(FloatCC::GreaterThan, x_lane, y_lane) + } (ty::Float(_), sym::simd_ge) => { fx.bcx.ins().fcmp(FloatCC::GreaterThanOrEqual, x_lane, y_lane) } @@ -103,10 +116,19 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( let res_lane = fx.bcx.ins().bint(ty, res_lane); fx.bcx.ins().ineg(res_lane) }); - }; + } // simd_shuffle32<T, U>(x: T, y: T, idx: [u32; 32]) -> U - _ if intrinsic.as_str().starts_with("simd_shuffle"), (c x, c y, o idx) { + _ if intrinsic.as_str().starts_with("simd_shuffle") => { + let (x, y, idx) = match args { + [x, y, idx] => (x, y, idx), + _ => { + bug!("wrong number of args for intrinsic {intrinsic}"); + } + }; + let x = codegen_operand(fx, x); + let y = codegen_operand(fx, y); + if !x.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, x.layout().ty); return; @@ -119,11 +141,13 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( // version of this intrinsic. let idx_ty = fx.monomorphize(idx.ty(fx.mir, fx.tcx)); match idx_ty.kind() { - ty::Array(ty, len) if matches!(ty.kind(), ty::Uint(ty::UintTy::U32)) => { - len.try_eval_usize(fx.tcx, ty::ParamEnv::reveal_all()).unwrap_or_else(|| { + ty::Array(ty, len) if matches!(ty.kind(), ty::Uint(ty::UintTy::U32)) => len + .try_eval_usize(fx.tcx, ty::ParamEnv::reveal_all()) + .unwrap_or_else(|| { span_bug!(span, "could not evaluate shuffle index array length") - }).try_into().unwrap() - } + }) + .try_into() + .unwrap(), _ => { fx.tcx.sess.span_err( span, @@ -154,24 +178,30 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( let indexes = { use rustc_middle::mir::interpret::*; - let idx_const = crate::constant::mir_operand_get_const_val(fx, idx).expect("simd_shuffle* idx not const"); + let idx_const = crate::constant::mir_operand_get_const_val(fx, idx) + .expect("simd_shuffle* idx not const"); let idx_bytes = match idx_const { ConstValue::ByRef { alloc, offset } => { - let size = Size::from_bytes(4 * ret_lane_count /* size_of([u32; ret_lane_count]) */); + let size = Size::from_bytes( + 4 * ret_lane_count, /* size_of([u32; ret_lane_count]) */ + ); alloc.inner().get_bytes(fx, alloc_range(offset, size)).unwrap() } _ => unreachable!("{:?}", idx_const), }; - (0..ret_lane_count).map(|i| { - let i = usize::try_from(i).unwrap(); - let idx = rustc_middle::mir::interpret::read_target_uint( - fx.tcx.data_layout.endian, - &idx_bytes[4*i.. 4*i + 4], - ).expect("read_target_uint"); - u16::try_from(idx).expect("try_from u32") - }).collect::<Vec<u16>>() + (0..ret_lane_count) + .map(|i| { + let i = usize::try_from(i).unwrap(); + let idx = rustc_middle::mir::interpret::read_target_uint( + fx.tcx.data_layout.endian, + &idx_bytes[4 * i..4 * i + 4], + ) + .expect("read_target_uint"); + u16::try_from(idx).expect("try_from u32") + }) + .collect::<Vec<u16>>() }; for &idx in &indexes { @@ -187,43 +217,63 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( let out_lane = ret.place_lane(fx, u64::try_from(out_idx).unwrap()); out_lane.write_cvalue(fx, in_lane); } - }; + } + + sym::simd_insert => { + let (base, idx, val) = match args { + [base, idx, val] => (base, idx, val), + _ => { + bug!("wrong number of args for intrinsic {intrinsic}"); + } + }; + let base = codegen_operand(fx, base); + let val = codegen_operand(fx, val); - simd_insert, (c base, o idx, c val) { // FIXME validate - let idx_const = if let Some(idx_const) = crate::constant::mir_operand_get_const_val(fx, idx) { + let idx_const = if let Some(idx_const) = + crate::constant::mir_operand_get_const_val(fx, idx) + { idx_const } else { - fx.tcx.sess.span_fatal( - span, - "Index argument for `simd_insert` is not a constant", - ); + fx.tcx.sess.span_fatal(span, "Index argument for `simd_insert` is not a constant"); }; - let idx = idx_const.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const)); + let idx = idx_const + .try_to_bits(Size::from_bytes(4 /* u32*/)) + .unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const)); let (lane_count, _lane_ty) = base.layout().ty.simd_size_and_type(fx.tcx); if idx >= lane_count.into() { - fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_insert] idx {} >= lane_count {}", idx, lane_count)); + fx.tcx.sess.span_fatal( + fx.mir.span, + &format!("[simd_insert] idx {} >= lane_count {}", idx, lane_count), + ); } ret.write_cvalue(fx, base); let ret_lane = ret.place_field(fx, mir::Field::new(idx.try_into().unwrap())); ret_lane.write_cvalue(fx, val); - }; + } + + sym::simd_extract => { + let (v, idx) = match args { + [v, idx] => (v, idx), + _ => { + bug!("wrong number of args for intrinsic {intrinsic}"); + } + }; + let v = codegen_operand(fx, v); - simd_extract, (c v, o idx) { if !v.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty); return; } - let idx_const = if let Some(idx_const) = crate::constant::mir_operand_get_const_val(fx, idx) { + let idx_const = if let Some(idx_const) = + crate::constant::mir_operand_get_const_val(fx, idx) + { idx_const } else { - fx.tcx.sess.span_warn( - span, - "Index argument for `simd_extract` is not a constant", - ); + fx.tcx.sess.span_warn(span, "Index argument for `simd_extract` is not a constant"); let res = crate::trap::trap_unimplemented_ret_value( fx, ret.layout(), @@ -233,89 +283,105 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( return; }; - let idx = idx_const.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const)); + let idx = idx_const + .try_to_bits(Size::from_bytes(4 /* u32*/)) + .unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const)); let (lane_count, _lane_ty) = v.layout().ty.simd_size_and_type(fx.tcx); if idx >= lane_count.into() { - fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_extract] idx {} >= lane_count {}", idx, lane_count)); + fx.tcx.sess.span_fatal( + fx.mir.span, + &format!("[simd_extract] idx {} >= lane_count {}", idx, lane_count), + ); } let ret_lane = v.value_lane(fx, idx.try_into().unwrap()); ret.write_cvalue(fx, ret_lane); - }; + } + + sym::simd_neg => { + intrinsic_args!(fx, args => (a); intrinsic); - simd_neg, (c a) { if !a.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, a.layout().ty); return; } - simd_for_each_lane(fx, a, ret, &|fx, lane_ty, _ret_lane_ty, lane| { - match lane_ty.kind() { + simd_for_each_lane( + fx, + a, + ret, + &|fx, lane_ty, _ret_lane_ty, lane| match lane_ty.kind() { ty::Int(_) => fx.bcx.ins().ineg(lane), ty::Float(_) => fx.bcx.ins().fneg(lane), _ => unreachable!(), - } - }); - }; - - simd_add | simd_sub | simd_mul | simd_div | simd_rem - | simd_shl | simd_shr | simd_and | simd_or | simd_xor, (c x, c y) { - if !x.layout().ty.is_simd() { - report_simd_type_validation_error(fx, intrinsic, span, x.layout().ty); - return; - } + }, + ); + } + + sym::simd_add + | sym::simd_sub + | sym::simd_mul + | sym::simd_div + | sym::simd_rem + | sym::simd_shl + | sym::simd_shr + | sym::simd_and + | sym::simd_or + | sym::simd_xor => { + intrinsic_args!(fx, args => (x, y); intrinsic); // FIXME use vector instructions when possible - simd_pair_for_each_lane(fx, x, y, ret, &|fx, lane_ty, _ret_lane_ty, x_lane, y_lane| match ( - lane_ty.kind(), - intrinsic, - ) { - (ty::Uint(_), sym::simd_add) => fx.bcx.ins().iadd(x_lane, y_lane), - (ty::Uint(_), sym::simd_sub) => fx.bcx.ins().isub(x_lane, y_lane), - (ty::Uint(_), sym::simd_mul) => fx.bcx.ins().imul(x_lane, y_lane), - (ty::Uint(_), sym::simd_div) => fx.bcx.ins().udiv(x_lane, y_lane), - (ty::Uint(_), sym::simd_rem) => fx.bcx.ins().urem(x_lane, y_lane), - - (ty::Int(_), sym::simd_add) => fx.bcx.ins().iadd(x_lane, y_lane), - (ty::Int(_), sym::simd_sub) => fx.bcx.ins().isub(x_lane, y_lane), - (ty::Int(_), sym::simd_mul) => fx.bcx.ins().imul(x_lane, y_lane), - (ty::Int(_), sym::simd_div) => fx.bcx.ins().sdiv(x_lane, y_lane), - (ty::Int(_), sym::simd_rem) => fx.bcx.ins().srem(x_lane, y_lane), - - (ty::Float(_), sym::simd_add) => fx.bcx.ins().fadd(x_lane, y_lane), - (ty::Float(_), sym::simd_sub) => fx.bcx.ins().fsub(x_lane, y_lane), - (ty::Float(_), sym::simd_mul) => fx.bcx.ins().fmul(x_lane, y_lane), - (ty::Float(_), sym::simd_div) => fx.bcx.ins().fdiv(x_lane, y_lane), - (ty::Float(FloatTy::F32), sym::simd_rem) => fx.lib_call( - "fmodf", - vec![AbiParam::new(types::F32), AbiParam::new(types::F32)], - vec![AbiParam::new(types::F32)], - &[x_lane, y_lane], - )[0], - (ty::Float(FloatTy::F64), sym::simd_rem) => fx.lib_call( - "fmod", - vec![AbiParam::new(types::F64), AbiParam::new(types::F64)], - vec![AbiParam::new(types::F64)], - &[x_lane, y_lane], - )[0], - - (ty::Uint(_), sym::simd_shl) => fx.bcx.ins().ishl(x_lane, y_lane), - (ty::Uint(_), sym::simd_shr) => fx.bcx.ins().ushr(x_lane, y_lane), - (ty::Uint(_), sym::simd_and) => fx.bcx.ins().band(x_lane, y_lane), - (ty::Uint(_), sym::simd_or) => fx.bcx.ins().bor(x_lane, y_lane), - (ty::Uint(_), sym::simd_xor) => fx.bcx.ins().bxor(x_lane, y_lane), - - (ty::Int(_), sym::simd_shl) => fx.bcx.ins().ishl(x_lane, y_lane), - (ty::Int(_), sym::simd_shr) => fx.bcx.ins().sshr(x_lane, y_lane), - (ty::Int(_), sym::simd_and) => fx.bcx.ins().band(x_lane, y_lane), - (ty::Int(_), sym::simd_or) => fx.bcx.ins().bor(x_lane, y_lane), - (ty::Int(_), sym::simd_xor) => fx.bcx.ins().bxor(x_lane, y_lane), - - _ => unreachable!(), + simd_pair_for_each_lane(fx, x, y, ret, &|fx, lane_ty, _ret_lane_ty, x_lane, y_lane| { + match (lane_ty.kind(), intrinsic) { + (ty::Uint(_), sym::simd_add) => fx.bcx.ins().iadd(x_lane, y_lane), + (ty::Uint(_), sym::simd_sub) => fx.bcx.ins().isub(x_lane, y_lane), + (ty::Uint(_), sym::simd_mul) => fx.bcx.ins().imul(x_lane, y_lane), + (ty::Uint(_), sym::simd_div) => fx.bcx.ins().udiv(x_lane, y_lane), + (ty::Uint(_), sym::simd_rem) => fx.bcx.ins().urem(x_lane, y_lane), + + (ty::Int(_), sym::simd_add) => fx.bcx.ins().iadd(x_lane, y_lane), + (ty::Int(_), sym::simd_sub) => fx.bcx.ins().isub(x_lane, y_lane), + (ty::Int(_), sym::simd_mul) => fx.bcx.ins().imul(x_lane, y_lane), + (ty::Int(_), sym::simd_div) => fx.bcx.ins().sdiv(x_lane, y_lane), + (ty::Int(_), sym::simd_rem) => fx.bcx.ins().srem(x_lane, y_lane), + + (ty::Float(_), sym::simd_add) => fx.bcx.ins().fadd(x_lane, y_lane), + (ty::Float(_), sym::simd_sub) => fx.bcx.ins().fsub(x_lane, y_lane), + (ty::Float(_), sym::simd_mul) => fx.bcx.ins().fmul(x_lane, y_lane), + (ty::Float(_), sym::simd_div) => fx.bcx.ins().fdiv(x_lane, y_lane), + (ty::Float(FloatTy::F32), sym::simd_rem) => fx.lib_call( + "fmodf", + vec![AbiParam::new(types::F32), AbiParam::new(types::F32)], + vec![AbiParam::new(types::F32)], + &[x_lane, y_lane], + )[0], + (ty::Float(FloatTy::F64), sym::simd_rem) => fx.lib_call( + "fmod", + vec![AbiParam::new(types::F64), AbiParam::new(types::F64)], + vec![AbiParam::new(types::F64)], + &[x_lane, y_lane], + )[0], + + (ty::Uint(_), sym::simd_shl) => fx.bcx.ins().ishl(x_lane, y_lane), + (ty::Uint(_), sym::simd_shr) => fx.bcx.ins().ushr(x_lane, y_lane), + (ty::Uint(_), sym::simd_and) => fx.bcx.ins().band(x_lane, y_lane), + (ty::Uint(_), sym::simd_or) => fx.bcx.ins().bor(x_lane, y_lane), + (ty::Uint(_), sym::simd_xor) => fx.bcx.ins().bxor(x_lane, y_lane), + + (ty::Int(_), sym::simd_shl) => fx.bcx.ins().ishl(x_lane, y_lane), + (ty::Int(_), sym::simd_shr) => fx.bcx.ins().sshr(x_lane, y_lane), + (ty::Int(_), sym::simd_and) => fx.bcx.ins().band(x_lane, y_lane), + (ty::Int(_), sym::simd_or) => fx.bcx.ins().bor(x_lane, y_lane), + (ty::Int(_), sym::simd_xor) => fx.bcx.ins().bxor(x_lane, y_lane), + + _ => unreachable!(), + } }); - }; + } + + sym::simd_fma => { + intrinsic_args!(fx, args => (a, b, c); intrinsic); - simd_fma, (c a, c b, c c) { if !a.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, a.layout().ty); return; @@ -333,16 +399,22 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( let c_lane = c.value_lane(fx, lane); let res_lane = match lane_ty.kind() { - ty::Float(FloatTy::F32) => fx.easy_call("fmaf", &[a_lane, b_lane, c_lane], lane_ty), - ty::Float(FloatTy::F64) => fx.easy_call("fma", &[a_lane, b_lane, c_lane], lane_ty), + ty::Float(FloatTy::F32) => { + fx.easy_call("fmaf", &[a_lane, b_lane, c_lane], lane_ty) + } + ty::Float(FloatTy::F64) => { + fx.easy_call("fma", &[a_lane, b_lane, c_lane], lane_ty) + } _ => unreachable!(), }; ret.place_lane(fx, lane).write_cvalue(fx, res_lane); } - }; + } + + sym::simd_fmin | sym::simd_fmax => { + intrinsic_args!(fx, args => (x, y); intrinsic); - simd_fmin | simd_fmax, (c x, c y) { if !x.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, x.layout().ty); return; @@ -351,7 +423,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( // FIXME use vector instructions when possible simd_pair_for_each_lane(fx, x, y, ret, &|fx, lane_ty, _ret_lane_ty, x_lane, y_lane| { match lane_ty.kind() { - ty::Float(_) => {}, + ty::Float(_) => {} _ => unreachable!("{:?}", lane_ty), } match intrinsic { @@ -360,16 +432,21 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( _ => unreachable!(), } }); - }; + } + + sym::simd_round => { + intrinsic_args!(fx, args => (a); intrinsic); - simd_round, (c a) { if !a.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, a.layout().ty); return; } - simd_for_each_lane(fx, a, ret, &|fx, lane_ty, _ret_lane_ty, lane| { - match lane_ty.kind() { + simd_for_each_lane( + fx, + a, + ret, + &|fx, lane_ty, _ret_lane_ty, lane| match lane_ty.kind() { ty::Float(FloatTy::F32) => fx.lib_call( "roundf", vec![AbiParam::new(types::F32)], @@ -383,11 +460,13 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( &[lane], )[0], _ => unreachable!("{:?}", lane_ty), - } - }); - }; + }, + ); + } + + sym::simd_fabs | sym::simd_fsqrt | sym::simd_ceil | sym::simd_floor | sym::simd_trunc => { + intrinsic_args!(fx, args => (a); intrinsic); - simd_fabs | simd_fsqrt | simd_ceil | simd_floor | simd_trunc, (c a) { if !a.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, a.layout().ty); return; @@ -395,7 +474,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( simd_for_each_lane(fx, a, ret, &|fx, lane_ty, _ret_lane_ty, lane| { match lane_ty.kind() { - ty::Float(_) => {}, + ty::Float(_) => {} _ => unreachable!("{:?}", lane_ty), } match intrinsic { @@ -407,9 +486,12 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( _ => unreachable!(), } }); - }; + } + + sym::simd_reduce_add_ordered | sym::simd_reduce_add_unordered => { + intrinsic_args!(fx, args => (v, acc); intrinsic); + let acc = acc.load_scalar(fx); - simd_reduce_add_ordered | simd_reduce_add_unordered, (c v, v acc) { // FIXME there must be no acc param for integer vectors if !v.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty); @@ -423,9 +505,12 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( fx.bcx.ins().iadd(a, b) } }); - }; + } + + sym::simd_reduce_mul_ordered | sym::simd_reduce_mul_unordered => { + intrinsic_args!(fx, args => (v, acc); intrinsic); + let acc = acc.load_scalar(fx); - simd_reduce_mul_ordered | simd_reduce_mul_unordered, (c v, v acc) { // FIXME there must be no acc param for integer vectors if !v.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty); @@ -439,54 +524,66 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( fx.bcx.ins().imul(a, b) } }); - }; + } + + sym::simd_reduce_all => { + intrinsic_args!(fx, args => (v); intrinsic); - simd_reduce_all, (c v) { if !v.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty); return; } simd_reduce_bool(fx, v, ret, &|fx, a, b| fx.bcx.ins().band(a, b)); - }; + } + + sym::simd_reduce_any => { + intrinsic_args!(fx, args => (v); intrinsic); - simd_reduce_any, (c v) { if !v.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty); return; } simd_reduce_bool(fx, v, ret, &|fx, a, b| fx.bcx.ins().bor(a, b)); - }; + } + + sym::simd_reduce_and => { + intrinsic_args!(fx, args => (v); intrinsic); - simd_reduce_and, (c v) { if !v.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty); return; } simd_reduce(fx, v, None, ret, &|fx, _ty, a, b| fx.bcx.ins().band(a, b)); - }; + } + + sym::simd_reduce_or => { + intrinsic_args!(fx, args => (v); intrinsic); - simd_reduce_or, (c v) { if !v.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty); return; } simd_reduce(fx, v, None, ret, &|fx, _ty, a, b| fx.bcx.ins().bor(a, b)); - }; + } + + sym::simd_reduce_xor => { + intrinsic_args!(fx, args => (v); intrinsic); - simd_reduce_xor, (c v) { if !v.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty); return; } simd_reduce(fx, v, None, ret, &|fx, _ty, a, b| fx.bcx.ins().bxor(a, b)); - }; + } + + sym::simd_reduce_min => { + intrinsic_args!(fx, args => (v); intrinsic); - simd_reduce_min, (c v) { if !v.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty); return; @@ -501,9 +598,11 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( }; fx.bcx.ins().select(lt, a, b) }); - }; + } + + sym::simd_reduce_max => { + intrinsic_args!(fx, args => (v); intrinsic); - simd_reduce_max, (c v) { if !v.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty); return; @@ -518,9 +617,11 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( }; fx.bcx.ins().select(gt, a, b) }); - }; + } + + sym::simd_select => { + intrinsic_args!(fx, args => (m, a, b); intrinsic); - simd_select, (c m, c a, c b) { if !m.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, m.layout().ty); return; @@ -540,15 +641,19 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( let b_lane = b.value_lane(fx, lane).load_scalar(fx); let m_lane = fx.bcx.ins().icmp_imm(IntCC::Equal, m_lane, 0); - let res_lane = CValue::by_val(fx.bcx.ins().select(m_lane, b_lane, a_lane), lane_layout); + let res_lane = + CValue::by_val(fx.bcx.ins().select(m_lane, b_lane, a_lane), lane_layout); ret.place_lane(fx, lane).write_cvalue(fx, res_lane); } - }; + } // simd_saturating_* // simd_bitmask // simd_scatter // simd_gather + _ => { + fx.tcx.sess.span_fatal(span, &format!("Unknown SIMD intrinsic {}", intrinsic)); + } } } diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 3ed3453c6c7..568bb20a3f4 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -141,7 +141,11 @@ impl<'tcx> CodegenCx<'tcx> { let unwind_context = UnwindContext::new(isa, matches!(backend_config.codegen_mode, CodegenMode::Aot)); - let debug_context = if debug_info { Some(DebugContext::new(tcx, isa)) } else { None }; + let debug_context = if debug_info && !tcx.sess.target.options.is_like_windows { + Some(DebugContext::new(tcx, isa)) + } else { + None + }; CodegenCx { tcx, global_asm: String::new(), @@ -243,6 +247,7 @@ fn build_isa(sess: &Session, backend_config: &BackendConfig) -> Box<dyn isa::Tar flags_builder.set("enable_probestack", "false").unwrap(); // __cranelift_probestack is not provided let enable_verifier = if backend_config.enable_verifier { "true" } else { "false" }; flags_builder.set("enable_verifier", enable_verifier).unwrap(); + flags_builder.set("regalloc_checker", enable_verifier).unwrap(); let tls_model = match target_triple.binary_format { BinaryFormat::Elf => "elf_gd", diff --git a/compiler/rustc_codegen_cranelift/src/main_shim.rs b/compiler/rustc_codegen_cranelift/src/main_shim.rs index 2f71a70a449..c67b6e98b32 100644 --- a/compiler/rustc_codegen_cranelift/src/main_shim.rs +++ b/compiler/rustc_codegen_cranelift/src/main_shim.rs @@ -109,7 +109,8 @@ pub(crate) fn maybe_create_entry_wrapper( tcx.mk_substs([GenericArg::from(main_ret_ty)].iter()), ) .unwrap() - .unwrap(); + .unwrap() + .polymorphize(tcx); let report_name = tcx.symbol_name(report).name; let report_sig = get_function_sig(tcx, m.isa().triple(), report); diff --git a/compiler/rustc_codegen_cranelift/src/pretty_clif.rs b/compiler/rustc_codegen_cranelift/src/pretty_clif.rs index ca7116b887d..1d1ec21680e 100644 --- a/compiler/rustc_codegen_cranelift/src/pretty_clif.rs +++ b/compiler/rustc_codegen_cranelift/src/pretty_clif.rs @@ -66,7 +66,7 @@ use rustc_session::config::OutputType; use crate::prelude::*; -#[derive(Debug)] +#[derive(Clone, Debug)] pub(crate) struct CommentWriter { enabled: bool, global_comments: Vec<String>, @@ -237,6 +237,7 @@ pub(crate) fn write_clif_file<'tcx>( func: &cranelift_codegen::ir::Function, mut clif_comments: &CommentWriter, ) { + // FIXME work around filename too long errors write_ir_file( tcx, || format!("{}.{}.clif", tcx.symbol_name(instance).name, postfix), diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index fd63c3ecddb..052ca0a082b 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -153,11 +153,7 @@ pub(crate) fn size_and_align_of_dst<'tcx>( layout: TyAndLayout<'tcx>, info: Value, ) -> (Value, Value) { - if !layout.is_unsized() { - let size = fx.bcx.ins().iconst(fx.pointer_type, layout.size.bytes() as i64); - let align = fx.bcx.ins().iconst(fx.pointer_type, layout.align.abi.bytes() as i64); - return (size, align); - } + assert!(layout.is_unsized() || layout.abi == Abi::Uninhabited); match layout.ty.kind() { ty::Dynamic(..) => { // load size/align from vtable diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index 8ff35d2f76d..45ae2bd8f07 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -324,6 +324,12 @@ impl<'tcx> CPlace<'tcx> { }; } + if layout.size.bytes() >= u64::from(u32::MAX - 16) { + fx.tcx + .sess + .fatal(&format!("values of type {} are too big to store on the stack", layout.ty)); + } + let stack_slot = fx.bcx.create_stack_slot(StackSlotData { kind: StackSlotKind::ExplicitSlot, // FIXME Don't force the size to a multiple of 16 bytes once Cranelift gets a way to @@ -420,7 +426,7 @@ impl<'tcx> CPlace<'tcx> { } pub(crate) fn write_cvalue(self, fx: &mut FunctionCx<'_, '_, 'tcx>, from: CValue<'tcx>) { - assert_assignable(fx, from.layout().ty, self.layout().ty); + assert_assignable(fx, from.layout().ty, self.layout().ty, 16); self.write_cvalue_maybe_transmute(fx, from, "write_cvalue"); } @@ -615,14 +621,6 @@ impl<'tcx> CPlace<'tcx> { } } - pub(crate) fn place_opaque_cast( - self, - fx: &mut FunctionCx<'_, '_, 'tcx>, - ty: Ty<'tcx>, - ) -> CPlace<'tcx> { - CPlace { inner: self.inner, layout: fx.layout_of(ty) } - } - pub(crate) fn place_field( self, fx: &mut FunctionCx<'_, '_, 'tcx>, @@ -782,18 +780,25 @@ pub(crate) fn assert_assignable<'tcx>( fx: &FunctionCx<'_, '_, 'tcx>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>, + limit: usize, ) { + if limit == 0 { + // assert_assignable exists solely to catch bugs in cg_clif. it isn't necessary for + // soundness. don't attempt to check deep types to avoid exponential behavior in certain + // cases. + return; + } match (from_ty.kind(), to_ty.kind()) { (ty::Ref(_, a, _), ty::Ref(_, b, _)) | ( ty::RawPtr(TypeAndMut { ty: a, mutbl: _ }), ty::RawPtr(TypeAndMut { ty: b, mutbl: _ }), ) => { - assert_assignable(fx, *a, *b); + assert_assignable(fx, *a, *b, limit - 1); } (ty::Ref(_, a, _), ty::RawPtr(TypeAndMut { ty: b, mutbl: _ })) | (ty::RawPtr(TypeAndMut { ty: a, mutbl: _ }), ty::Ref(_, b, _)) => { - assert_assignable(fx, *a, *b); + assert_assignable(fx, *a, *b, limit - 1); } (ty::FnPtr(_), ty::FnPtr(_)) => { let from_sig = fx.tcx.normalize_erasing_late_bound_regions( @@ -823,6 +828,17 @@ pub(crate) fn assert_assignable<'tcx>( } // dyn for<'r> Trait<'r> -> dyn Trait<'_> is allowed } + (&ty::Tuple(types_a), &ty::Tuple(types_b)) => { + let mut types_a = types_a.iter(); + let mut types_b = types_b.iter(); + loop { + match (types_a.next(), types_b.next()) { + (Some(a), Some(b)) => assert_assignable(fx, a, b, limit - 1), + (None, None) => return, + (Some(_), None) | (None, Some(_)) => panic!("{:#?}/{:#?}", from_ty, to_ty), + } + } + } (&ty::Adt(adt_def_a, substs_a), &ty::Adt(adt_def_b, substs_b)) if adt_def_a.did() == adt_def_b.did() => { @@ -830,18 +846,37 @@ pub(crate) fn assert_assignable<'tcx>( let mut types_b = substs_b.types(); loop { match (types_a.next(), types_b.next()) { - (Some(a), Some(b)) => assert_assignable(fx, a, b), + (Some(a), Some(b)) => assert_assignable(fx, a, b, limit - 1), (None, None) => return, (Some(_), None) | (None, Some(_)) => panic!("{:#?}/{:#?}", from_ty, to_ty), } } } - (ty::Array(a, _), ty::Array(b, _)) => assert_assignable(fx, *a, *b), + (ty::Array(a, _), ty::Array(b, _)) => assert_assignable(fx, *a, *b, limit - 1), + (&ty::Closure(def_id_a, substs_a), &ty::Closure(def_id_b, substs_b)) + if def_id_a == def_id_b => + { + let mut types_a = substs_a.types(); + let mut types_b = substs_b.types(); + loop { + match (types_a.next(), types_b.next()) { + (Some(a), Some(b)) => assert_assignable(fx, a, b, limit - 1), + (None, None) => return, + (Some(_), None) | (None, Some(_)) => panic!("{:#?}/{:#?}", from_ty, to_ty), + } + } + } + (ty::Param(_), _) | (_, ty::Param(_)) if fx.tcx.sess.opts.unstable_opts.polymorphize => { + // No way to check if it is correct or not with polymorphization enabled + } _ => { assert_eq!( - from_ty, to_ty, + from_ty, + to_ty, "Can't write value with incompatible type {:?} to place with type {:?}\n\n{:#?}", - from_ty, to_ty, fx, + from_ty.kind(), + to_ty.kind(), + fx, ); } } diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs index 2c796d0f69e..0ed3e1fbe93 100644 --- a/compiler/rustc_codegen_gcc/src/abi.rs +++ b/compiler/rustc_codegen_gcc/src/abi.rs @@ -1,6 +1,6 @@ use gccjit::{ToLValue, ToRValue, Type}; use rustc_codegen_ssa::traits::{AbiBuilderMethods, BaseTypeMethods}; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_middle::bug; use rustc_middle::ty::Ty; use rustc_target::abi::call::{CastTarget, FnAbi, PassMode, Reg, RegKind}; diff --git a/compiler/rustc_codegen_gcc/src/archive.rs b/compiler/rustc_codegen_gcc/src/archive.rs index 411ec27139e..21f62a6b009 100644 --- a/compiler/rustc_codegen_gcc/src/archive.rs +++ b/compiler/rustc_codegen_gcc/src/archive.rs @@ -4,7 +4,6 @@ use std::path::{Path, PathBuf}; use rustc_codegen_ssa::back::archive::ArchiveBuilder; use rustc_session::Session; -use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_session::cstore::DllImport; struct ArchiveConfig<'a> { @@ -177,7 +176,16 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { any_members } - fn inject_dll_import_lib(&mut self, _lib_name: &str, _dll_imports: &[DllImport], _tmpdir: &MaybeTempDir) { + fn sess(&self) -> &Session { + self.config.sess + } + + fn create_dll_import_lib( + _sess: &Session, + _lib_name: &str, + _dll_imports: &[DllImport], + _tmpdir: &Path, + ) -> PathBuf { unimplemented!(); } } diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index fa490fe3f22..4d40dd0994d 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -30,7 +30,7 @@ use rustc_codegen_ssa::traits::{ OverflowOp, StaticBuilderMethods, }; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_middle::ty::{ParamEnv, Ty, TyCtxt}; use rustc_middle::ty::layout::{FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout}; use rustc_span::Span; @@ -784,16 +784,6 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { // TODO(antoyo) } - fn type_metadata(&mut self, _function: RValue<'gcc>, _typeid: String) { - // Unsupported. - } - - fn typeid_metadata(&mut self, _typeid: String) -> RValue<'gcc> { - // Unsupported. - self.context.new_rvalue_from_int(self.int_type, 0) - } - - fn store(&mut self, val: RValue<'gcc>, ptr: RValue<'gcc>, align: Align) -> RValue<'gcc> { self.store_with_flags(val, ptr, align, MemFlags::empty()) } diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index fc391f53f18..ccb6cbbc2c8 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -201,6 +201,11 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> { GlobalAlloc::Function(fn_instance) => { self.get_fn_addr(fn_instance) }, + GlobalAlloc::VTable(ty, trait_ref) => { + let alloc = self.tcx.global_alloc(self.tcx.vtable_allocation((ty, trait_ref))).unwrap_memory(); + let init = const_alloc_to_gcc(self, alloc); + self.static_addr_of(init, alloc.inner().align, None) + } GlobalAlloc::Static(def_id) => { assert!(self.tcx.is_static(def_id)); self.get_static(def_id).get_address(None) diff --git a/compiler/rustc_codegen_gcc/src/type_.rs b/compiler/rustc_codegen_gcc/src/type_.rs index 002b95db36d..68bdb8d4e55 100644 --- a/compiler/rustc_codegen_gcc/src/type_.rs +++ b/compiler/rustc_codegen_gcc/src/type_.rs @@ -1,7 +1,7 @@ use std::convert::TryInto; use gccjit::{RValue, Struct, Type}; -use rustc_codegen_ssa::traits::{BaseTypeMethods, DerivedTypeMethods}; +use rustc_codegen_ssa::traits::{BaseTypeMethods, DerivedTypeMethods, TypeMembershipMethods}; use rustc_codegen_ssa::common::TypeKind; use rustc_middle::{bug, ty}; use rustc_middle::ty::layout::TyAndLayout; @@ -290,3 +290,14 @@ pub fn struct_fields<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, layout: TyAndLayout (result, packed) } + +impl<'gcc, 'tcx> TypeMembershipMethods<'tcx> for CodegenCx<'gcc, 'tcx> { + fn set_type_metadata(&self, _function: RValue<'gcc>, _typeid: String) { + // Unsupported. + } + + fn typeid_metadata(&self, _typeid: String) -> RValue<'gcc> { + // Unsupported. + self.context.new_rvalue_from_int(self.int_type, 0) + } +} diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index d8fbd0a84fb..f9a5463efcd 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -18,7 +18,6 @@ rustc_middle = { path = "../rustc_middle" } rustc-demangle = "0.1.21" rustc_attr = { path = "../rustc_attr" } rustc_codegen_ssa = { path = "../rustc_codegen_ssa" } -rustc_symbol_mangling = { path = "../rustc_symbol_mangling" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fs_util = { path = "../rustc_fs_util" } @@ -30,6 +29,7 @@ rustc_metadata = { path = "../rustc_metadata" } rustc_query_system = { path = "../rustc_query_system" } rustc_session = { path = "../rustc_session" } rustc_serialize = { path = "../rustc_serialize" } +rustc_symbol_mangling = { path = "../rustc_symbol_mangling" } rustc_target = { path = "../rustc_target" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } rustc_ast = { path = "../rustc_ast" } diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index d4437bd449d..9eb3574e77b 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -569,6 +569,22 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { &[cmse_nonsecure_call], ); } + + // Some intrinsics require that an elementtype attribute (with the pointee type of a + // pointer argument) is added to the callsite. + let element_type_index = unsafe { llvm::LLVMRustGetElementTypeArgIndex(callsite) }; + if element_type_index >= 0 { + let arg_ty = self.args[element_type_index as usize].layout.ty; + let pointee_ty = arg_ty.builtin_deref(true).expect("Must be pointer argument").ty; + let element_type_attr = unsafe { + llvm::LLVMRustCreateElementTypeAttr(bx.llcx, bx.layout_of(pointee_ty).llvm_type(bx)) + }; + attributes::apply_to_callsite( + callsite, + llvm::AttributePlace::Argument(element_type_index as u32), + &[element_type_attr], + ); + } } } diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 62da99ac3fb..aabbe8ac276 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -13,7 +13,7 @@ use smallvec::SmallVec; use crate::attributes; use crate::llvm::AttributePlace::Function; -use crate::llvm::{self, Attribute, AttributeKind, AttributePlace}; +use crate::llvm::{self, AllocKindFlags, Attribute, AttributeKind, AttributePlace}; use crate::llvm_util; pub use rustc_attr::{InlineAttr, InstructionSetAttr, OptimizeAttr}; @@ -69,6 +69,9 @@ pub fn sanitize_attrs<'ll>( if enabled.contains(SanitizerSet::HWADDRESS) { attrs.push(llvm::AttributeKind::SanitizeHWAddress.create_attr(cx.llcx)); } + if enabled.contains(SanitizerSet::SHADOWCALLSTACK) { + attrs.push(llvm::AttributeKind::ShadowCallStack.create_attr(cx.llcx)); + } if enabled.contains(SanitizerSet::MEMTAG) { // Check to make sure the mte target feature is actually enabled. let features = cx.tcx.global_backend_features(()); @@ -224,6 +227,10 @@ pub(crate) fn default_optimisation_attrs<'ll>( attrs } +fn create_alloc_family_attr(llcx: &llvm::Context) -> &llvm::Attribute { + llvm::CreateAttrStringValue(llcx, "alloc-family", "__rust_alloc") +} + /// Composite function which sets LLVM attributes for function depending on its AST (`#[attribute]`) /// attributes. pub fn from_fn_attrs<'ll, 'tcx>( @@ -306,11 +313,54 @@ pub fn from_fn_attrs<'ll, 'tcx>( // Need this for AArch64. to_add.push(llvm::CreateAttrStringValue(cx.llcx, "branch-target-enforcement", "false")); } - if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) { + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) + || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR_ZEROED) + { + if llvm_util::get_version() >= (15, 0, 0) { + to_add.push(create_alloc_family_attr(cx.llcx)); + // apply to argument place instead of function + let alloc_align = AttributeKind::AllocAlign.create_attr(cx.llcx); + attributes::apply_to_llfn(llfn, AttributePlace::Argument(1), &[alloc_align]); + to_add.push(llvm::CreateAllocSizeAttr(cx.llcx, 0)); + let mut flags = AllocKindFlags::Alloc | AllocKindFlags::Aligned; + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) { + flags |= AllocKindFlags::Uninitialized; + } else { + flags |= AllocKindFlags::Zeroed; + } + to_add.push(llvm::CreateAllocKindAttr(cx.llcx, flags)); + } // apply to return place instead of function (unlike all other attributes applied in this function) let no_alias = AttributeKind::NoAlias.create_attr(cx.llcx); attributes::apply_to_llfn(llfn, AttributePlace::ReturnValue, &[no_alias]); } + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::REALLOCATOR) { + if llvm_util::get_version() >= (15, 0, 0) { + to_add.push(create_alloc_family_attr(cx.llcx)); + to_add.push(llvm::CreateAllocKindAttr( + cx.llcx, + AllocKindFlags::Realloc | AllocKindFlags::Aligned, + )); + // applies to argument place instead of function place + let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx); + attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]); + // apply to argument place instead of function + let alloc_align = AttributeKind::AllocAlign.create_attr(cx.llcx); + attributes::apply_to_llfn(llfn, AttributePlace::Argument(2), &[alloc_align]); + to_add.push(llvm::CreateAllocSizeAttr(cx.llcx, 3)); + } + let no_alias = AttributeKind::NoAlias.create_attr(cx.llcx); + attributes::apply_to_llfn(llfn, AttributePlace::ReturnValue, &[no_alias]); + } + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::DEALLOCATOR) { + if llvm_util::get_version() >= (15, 0, 0) { + to_add.push(create_alloc_family_attr(cx.llcx)); + to_add.push(llvm::CreateAllocKindAttr(cx.llcx, AllocKindFlags::Free)); + // applies to argument place instead of function place + let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx); + attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]); + } + } if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::CMSE_NONSECURE_ENTRY) { to_add.push(llvm::CreateAttrString(cx.llcx, "cmse_nonsecure_entry")); } diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs index bccc2a995a3..baa858709a0 100644 --- a/compiler/rustc_codegen_llvm/src/back/archive.rs +++ b/compiler/rustc_codegen_llvm/src/back/archive.rs @@ -11,7 +11,6 @@ use std::str; use crate::llvm::archive_ro::{ArchiveRO, Child}; use crate::llvm::{self, ArchiveKind, LLVMMachineType, LLVMRustCOFFShortExport}; use rustc_codegen_ssa::back::archive::ArchiveBuilder; -use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_session::cstore::{DllCallingConvention, DllImport}; use rustc_session::Session; @@ -96,19 +95,23 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> { } } - fn inject_dll_import_lib( - &mut self, + fn sess(&self) -> &Session { + self.sess + } + + fn create_dll_import_lib( + sess: &Session, lib_name: &str, dll_imports: &[DllImport], - tmpdir: &MaybeTempDir, - ) { + tmpdir: &Path, + ) -> PathBuf { let output_path = { - let mut output_path: PathBuf = tmpdir.as_ref().to_path_buf(); + let mut output_path: PathBuf = tmpdir.to_path_buf(); output_path.push(format!("{}_imports", lib_name)); output_path.with_extension("lib") }; - let target = &self.sess.target; + let target = &sess.target; let mingw_gnu_toolchain = target.vendor == "pc" && target.os == "windows" && target.env == "gnu" @@ -117,7 +120,7 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> { let import_name_and_ordinal_vector: Vec<(String, Option<u16>)> = dll_imports .iter() .map(|import: &DllImport| { - if self.sess.target.arch == "x86" { + if sess.target.arch == "x86" { ( LlvmArchiveBuilder::i686_decorated_name(import, mingw_gnu_toolchain), import.ordinal, @@ -134,8 +137,7 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> { // that loaded but crashed with an AV upon calling one of the imported // functions. Therefore, use binutils to create the import library instead, // by writing a .DEF file to the temp dir and calling binutils's dlltool. - let def_file_path = - tmpdir.as_ref().join(format!("{}_imports", lib_name)).with_extension("def"); + let def_file_path = tmpdir.join(format!("{}_imports", lib_name)).with_extension("def"); let def_file_content = format!( "EXPORTS\n{}", @@ -154,11 +156,11 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> { match std::fs::write(&def_file_path, def_file_content) { Ok(_) => {} Err(e) => { - self.sess.fatal(&format!("Error writing .DEF file: {}", e)); + sess.fatal(&format!("Error writing .DEF file: {}", e)); } }; - let dlltool = find_binutils_dlltool(self.sess); + let dlltool = find_binutils_dlltool(sess); let result = std::process::Command::new(dlltool) .args([ "-d", @@ -172,9 +174,9 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> { match result { Err(e) => { - self.sess.fatal(&format!("Error calling dlltool: {}", e)); + sess.fatal(&format!("Error calling dlltool: {}", e)); } - Ok(output) if !output.status.success() => self.sess.fatal(&format!( + Ok(output) if !output.status.success() => sess.fatal(&format!( "Dlltool could not create import library: {}\n{}", String::from_utf8_lossy(&output.stdout), String::from_utf8_lossy(&output.stderr) @@ -220,13 +222,13 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> { output_path_z.as_ptr(), ffi_exports.as_ptr(), ffi_exports.len(), - llvm_machine_type(&self.sess.target.arch) as u16, - !self.sess.target.is_like_msvc, + llvm_machine_type(&sess.target.arch) as u16, + !sess.target.is_like_msvc, ) }; if result == crate::llvm::LLVMRustResult::Failure { - self.sess.fatal(&format!( + sess.fatal(&format!( "Error creating import library for {}: {}", lib_name, llvm::last_error().unwrap_or("unknown LLVM error".to_string()) @@ -234,13 +236,7 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> { } }; - self.add_archive(&output_path, |_| false).unwrap_or_else(|e| { - self.sess.fatal(&format!( - "failed to add native library {}: {}", - output_path.display(), - e - )); - }); + output_path } } diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index be539499b56..3731c6bcfe7 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -199,7 +199,7 @@ pub(crate) fn run_thin( pub(crate) fn prepare_thin(module: ModuleCodegen<ModuleLlvm>) -> (String, ThinBuffer) { let name = module.name.clone(); - let buffer = ThinBuffer::new(module.module_llvm.llmod()); + let buffer = ThinBuffer::new(module.module_llvm.llmod(), true); (name, buffer) } @@ -695,9 +695,9 @@ unsafe impl Send for ThinBuffer {} unsafe impl Sync for ThinBuffer {} impl ThinBuffer { - pub fn new(m: &llvm::Module) -> ThinBuffer { + pub fn new(m: &llvm::Module, is_thin: bool) -> ThinBuffer { unsafe { - let buffer = llvm::LLVMRustThinLTOBufferCreate(m); + let buffer = llvm::LLVMRustThinLTOBufferCreate(m, is_thin); ThinBuffer(buffer) } } diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 2b465ce40e7..534d32e8a90 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -790,7 +790,7 @@ pub(crate) unsafe fn codegen( let _timer = cgcx .prof .generic_activity_with_arg("LLVM_module_codegen_make_bitcode", &*module.name); - let thin = ThinBuffer::new(llmod); + let thin = ThinBuffer::new(llmod, config.emit_thin_lto); let data = thin.data(); if let Some(bitcode_filename) = bc_out.file_name() { diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 4a4cccb490d..d3096c73a8a 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -626,32 +626,6 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } } - fn type_metadata(&mut self, function: &'ll Value, typeid: String) { - let typeid_metadata = self.typeid_metadata(typeid); - let v = [self.const_usize(0), typeid_metadata]; - unsafe { - llvm::LLVMGlobalSetMetadata( - function, - llvm::MD_type as c_uint, - llvm::LLVMValueAsMetadata(llvm::LLVMMDNodeInContext( - self.cx.llcx, - v.as_ptr(), - v.len() as c_uint, - )), - ) - } - } - - fn typeid_metadata(&mut self, typeid: String) -> Self::Value { - unsafe { - llvm::LLVMMDStringInContext( - self.cx.llcx, - typeid.as_ptr() as *const c_char, - typeid.as_bytes().len() as c_uint, - ) - } - } - fn store(&mut self, val: &'ll Value, ptr: &'ll Value, align: Align) -> &'ll Value { self.store_with_flags(val, ptr, align, MemFlags::empty()) } diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 77cbbf4c6ca..fb4da9a5f33 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -257,6 +257,15 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { self.get_fn_addr(fn_instance.polymorphize(self.tcx)), self.data_layout().instruction_address_space, ), + GlobalAlloc::VTable(ty, trait_ref) => { + let alloc = self + .tcx + .global_alloc(self.tcx.vtable_allocation((ty, trait_ref))) + .unwrap_memory(); + let init = const_alloc_to_llvm(self, alloc); + let value = self.static_addr_of(init, alloc.inner().align, None); + (value, AddressSpace::DATA) + } GlobalAlloc::Static(def_id) => { assert!(self.tcx.is_static(def_id)); assert!(!self.tcx.is_thread_local_static(def_id)); diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 2b16ae1a88d..18467e37082 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -101,7 +101,9 @@ pub fn const_alloc_to_llvm<'ll>(cx: &CodegenCx<'ll, '_>, alloc: ConstAllocation< let address_space = match cx.tcx.global_alloc(alloc_id) { GlobalAlloc::Function(..) => cx.data_layout().instruction_address_space, - GlobalAlloc::Static(..) | GlobalAlloc::Memory(..) => AddressSpace::DATA, + GlobalAlloc::Static(..) | GlobalAlloc::Memory(..) | GlobalAlloc::VTable(..) => { + AddressSpace::DATA + } }; llvals.push(cx.scalar_to_backend( @@ -535,10 +537,20 @@ impl<'ll> StaticMethods for CodegenCx<'ll, '_> { // The semantics of #[used] in Rust only require the symbol to make it into the // object file. It is explicitly allowed for the linker to strip the symbol if it - // is dead. As such, use llvm.compiler.used instead of llvm.used. + // is dead, which means we are allowed use `llvm.compiler.used` instead of + // `llvm.used` here. + // // Additionally, https://reviews.llvm.org/D97448 in LLVM 13 started emitting unique // sections with SHF_GNU_RETAIN flag for llvm.used symbols, which may trigger bugs - // in some versions of the gold linker. + // in the handling of `.init_array` (the static constructor list) in versions of + // the gold linker (prior to the one released with binutils 2.36). + // + // That said, we only ever emit these when compiling for ELF targets, unless + // `#[used(compiler)]` is explicitly requested. This is to avoid similar breakage + // on other targets, in particular MachO targets have *their* static constructor + // lists broken if `llvm.compiler.used` is emitted rather than llvm.used. However, + // that check happens when assigning the `CodegenFnAttrFlags` in `rustc_typeck`, + // so we don't need to take care of it here. self.add_compiler_used_global(g); } if attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) { diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index f8bd2d234f3..bd84100e0e8 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -1420,7 +1420,7 @@ fn build_vtable_type_di_node<'ll, 'tcx>( cx, type_map::stub( cx, - Stub::VtableTy { vtable_holder }, + Stub::VTableTy { vtable_holder }, unique_type_id, &vtable_type_name, (size, pointer_align), diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs index 8fc8118849b..ce2f419c4ac 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs @@ -146,7 +146,7 @@ impl<'ll> DINodeCreationResult<'ll> { pub enum Stub<'ll> { Struct, Union, - VtableTy { vtable_holder: &'ll DIType }, + VTableTy { vtable_holder: &'ll DIType }, } pub struct StubInfo<'ll, 'tcx> { @@ -180,9 +180,9 @@ pub(super) fn stub<'ll, 'tcx>( let unique_type_id_str = unique_type_id.generate_unique_id_string(cx.tcx); let metadata = match kind { - Stub::Struct | Stub::VtableTy { .. } => { + Stub::Struct | Stub::VTableTy { .. } => { let vtable_holder = match kind { - Stub::VtableTy { vtable_holder } => Some(vtable_holder), + Stub::VTableTy { vtable_holder } => Some(vtable_holder), _ => None, }; unsafe { diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index 5a5c4f7f860..fa0ecd18fc8 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -18,7 +18,9 @@ use crate::llvm; use crate::llvm::AttributePlace::Function; use crate::type_::Type; use crate::value::Value; +use rustc_codegen_ssa::traits::TypeMembershipMethods; use rustc_middle::ty::Ty; +use rustc_symbol_mangling::typeid::typeid_for_fnabi; use smallvec::SmallVec; use tracing::debug; @@ -97,6 +99,12 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { fn_abi.llvm_type(self), ); fn_abi.apply_attrs_llfn(self, llfn); + + if self.tcx.sess.is_sanitizer_cfi_enabled() { + let typeid = typeid_for_fnabi(self.tcx, fn_abi); + self.set_type_metadata(llfn, typeid); + } + llfn } diff --git a/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs b/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs index e2fa5e488ed..45de284d22a 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs @@ -20,19 +20,6 @@ pub enum OptimizationDiagnosticKind { OptimizationRemarkOther, } -impl OptimizationDiagnosticKind { - pub fn describe(self) -> &'static str { - match self { - OptimizationRemark | OptimizationRemarkOther => "remark", - OptimizationMissed => "missed", - OptimizationAnalysis => "analysis", - OptimizationAnalysisFPCommute => "floating-point", - OptimizationAnalysisAliasing => "aliasing", - OptimizationFailure => "failure", - } - } -} - pub struct OptimizationDiagnostic<'ll> { pub kind: OptimizationDiagnosticKind, pub pass_name: String, diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 73cedb59349..3139f93bfef 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -192,6 +192,10 @@ pub enum AttributeKind { NoUndef = 33, SanitizeMemTag = 34, NoCfCheck = 35, + ShadowCallStack = 36, + AllocSize = 37, + AllocatedPointer = 38, + AllocAlign = 39, } /// LLVMIntPredicate @@ -568,16 +572,6 @@ pub enum ArchiveKind { K_COFF, } -/// LLVMRustPassKind -#[derive(Copy, Clone, PartialEq, Debug)] -#[repr(C)] -#[allow(dead_code)] // Variants constructed by C++. -pub enum PassKind { - Other, - Function, - Module, -} - // LLVMRustThinLTOData extern "C" { pub type ThinLTOData; @@ -588,10 +582,6 @@ extern "C" { pub type ThinLTOBuffer; } -// LLVMRustModuleNameCallback -pub type ThinLTOModuleNameCallback = - unsafe extern "C" fn(*mut c_void, *const c_char, *const c_char); - /// LLVMRustThinLTOModule #[repr(C)] pub struct ThinLTOModule { @@ -657,9 +647,6 @@ extern "C" { } #[repr(C)] pub struct Builder<'a>(InvariantOpaque<'a>); -extern "C" { - pub type MemoryBuffer; -} #[repr(C)] pub struct PassManager<'a>(InvariantOpaque<'a>); extern "C" { @@ -985,6 +972,22 @@ pub mod debuginfo { } } +use bitflags::bitflags; +// These values **must** match with LLVMRustAllocKindFlags +bitflags! { + #[repr(transparent)] + #[derive(Default)] + pub struct AllocKindFlags : u64 { + const Unknown = 0; + const Alloc = 1; + const Realloc = 1 << 1; + const Free = 1 << 2; + const Uninitialized = 1 << 3; + const Zeroed = 1 << 4; + const Aligned = 1 << 5; + } +} + extern "C" { pub type ModuleBuffer; } @@ -1012,7 +1015,6 @@ extern "C" { pub fn LLVMSetDataLayout(M: &Module, Triple: *const c_char); /// See Module::setModuleInlineAsm. - pub fn LLVMSetModuleInlineAsm2(M: &Module, Asm: *const c_char, AsmLen: size_t); pub fn LLVMRustAppendModuleInlineAsm(M: &Module, Asm: *const c_char, AsmLen: size_t); /// See llvm::LLVMTypeKind::getTypeID. @@ -1166,7 +1168,6 @@ extern "C" { pub fn LLVMGetInitializer(GlobalVar: &Value) -> Option<&Value>; pub fn LLVMSetInitializer<'a>(GlobalVar: &'a Value, ConstantVal: &'a Value); pub fn LLVMIsThreadLocal(GlobalVar: &Value) -> Bool; - pub fn LLVMSetThreadLocal(GlobalVar: &Value, IsThreadLocal: Bool); pub fn LLVMSetThreadLocalMode(GlobalVar: &Value, Mode: ThreadLocalMode); pub fn LLVMIsGlobalConstant(GlobalVar: &Value) -> Bool; pub fn LLVMSetGlobalConstant(GlobalVar: &Value, IsConstant: Bool); @@ -1191,7 +1192,10 @@ extern "C" { pub fn LLVMRustCreateDereferenceableOrNullAttr(C: &Context, bytes: u64) -> &Attribute; pub fn LLVMRustCreateByValAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute; pub fn LLVMRustCreateStructRetAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute; + pub fn LLVMRustCreateElementTypeAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute; pub fn LLVMRustCreateUWTableAttr(C: &Context, async_: bool) -> &Attribute; + pub fn LLVMRustCreateAllocSizeAttr(C: &Context, size_arg: u32) -> &Attribute; + pub fn LLVMRustCreateAllocKindAttr(C: &Context, size_arg: u64) -> &Attribute; // Operations on functions pub fn LLVMRustGetOrInsertFunction<'a>( @@ -2245,7 +2249,6 @@ extern "C" { pub fn LLVMIsAConstantInt(value_ref: &Value) -> Option<&ConstantInt>; - pub fn LLVMRustPassKind(Pass: &Pass) -> PassKind; pub fn LLVMRustFindAndCreatePass(Pass: *const c_char) -> Option<&'static mut Pass>; pub fn LLVMRustCreateAddressSanitizerFunctionPass(Recover: bool) -> &'static mut Pass; pub fn LLVMRustCreateModuleAddressSanitizerPass(Recover: bool) -> &'static mut Pass; @@ -2362,7 +2365,6 @@ extern "C" { ) -> LLVMRustResult; pub fn LLVMRustSetLLVMOptions(Argc: c_int, Argv: *const *const c_char); pub fn LLVMRustPrintPasses(); - pub fn LLVMRustGetInstructionCount(M: &Module) -> u32; pub fn LLVMRustSetNormalizedTarget(M: &Module, triple: *const c_char); pub fn LLVMRustAddAlwaysInlinePass(P: &PassManagerBuilder, AddLifetimes: bool); pub fn LLVMRustRunRestrictionPass(M: &Module, syms: *const *const c_char, len: size_t); @@ -2460,7 +2462,6 @@ extern "C" { pub fn LLVMRustPositionBuilderAtStart<'a>(B: &Builder<'a>, BB: &'a BasicBlock); pub fn LLVMRustSetComdat<'a>(M: &'a Module, V: &'a Value, Name: *const c_char, NameLen: size_t); - pub fn LLVMRustUnsetComdat(V: &Value); pub fn LLVMRustSetModulePICLevel(M: &Module); pub fn LLVMRustSetModulePIELevel(M: &Module); pub fn LLVMRustSetModuleCodeModel(M: &Module, Model: CodeModel); @@ -2470,7 +2471,7 @@ extern "C" { pub fn LLVMRustModuleBufferFree(p: &'static mut ModuleBuffer); pub fn LLVMRustModuleCost(M: &Module) -> u64; - pub fn LLVMRustThinLTOBufferCreate(M: &Module) -> &'static mut ThinLTOBuffer; + pub fn LLVMRustThinLTOBufferCreate(M: &Module, is_thin: bool) -> &'static mut ThinLTOBuffer; pub fn LLVMRustThinLTOBufferFree(M: &'static mut ThinLTOBuffer); pub fn LLVMRustThinLTOBufferPtr(M: &ThinLTOBuffer) -> *const c_char; pub fn LLVMRustThinLTOBufferLen(M: &ThinLTOBuffer) -> size_t; @@ -2492,11 +2493,6 @@ extern "C" { Module: &Module, Target: &TargetMachine, ) -> bool; - pub fn LLVMRustGetThinLTOModuleImports( - Data: *const ThinLTOData, - ModuleNameCallback: ThinLTOModuleNameCallback, - CallbackPayload: *mut c_void, - ); pub fn LLVMRustFreeThinLTOData(Data: &'static mut ThinLTOData); pub fn LLVMRustParseBitcodeForLTO( Context: &Context, @@ -2546,4 +2542,6 @@ extern "C" { #[allow(improper_ctypes)] pub fn LLVMRustGetMangledName(V: &Value, out: &RustString); + + pub fn LLVMRustGetElementTypeArgIndex(CallSite: &Value) -> i32; } diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index 48fbc1de8ee..6602a4ab863 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -95,6 +95,14 @@ pub fn CreateUWTableAttr(llcx: &Context, async_: bool) -> &Attribute { unsafe { LLVMRustCreateUWTableAttr(llcx, async_) } } +pub fn CreateAllocSizeAttr(llcx: &Context, size_arg: u32) -> &Attribute { + unsafe { LLVMRustCreateAllocSizeAttr(llcx, size_arg) } +} + +pub fn CreateAllocKindAttr(llcx: &Context, kind_arg: AllocKindFlags) -> &Attribute { + unsafe { LLVMRustCreateAllocKindAttr(llcx, kind_arg.bits()) } +} + #[derive(Copy, Clone)] pub enum AttributePlace { ReturnValue, @@ -158,12 +166,6 @@ pub fn SetUniqueComdat(llmod: &Module, val: &Value) { } } -pub fn UnsetComdat(val: &Value) { - unsafe { - LLVMRustUnsetComdat(val); - } -} - pub fn SetUnnamedAddress(global: &Value, unnamed: UnnamedAddr) { unsafe { LLVMSetUnnamedAddress(global, unnamed); diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index cf2d3c423c3..eeb38d4ecf5 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -19,7 +19,7 @@ use rustc_target::abi::{AddressSpace, Align, Integer, Size}; use std::fmt; use std::ptr; -use libc::c_uint; +use libc::{c_char, c_uint}; impl PartialEq for Type { fn eq(&self, other: &Self) -> bool { @@ -289,3 +289,31 @@ impl<'ll, 'tcx> LayoutTypeMethods<'tcx> for CodegenCx<'ll, 'tcx> { ty.llvm_type(self) } } + +impl<'ll, 'tcx> TypeMembershipMethods<'tcx> for CodegenCx<'ll, 'tcx> { + fn set_type_metadata(&self, function: &'ll Value, typeid: String) { + let typeid_metadata = self.typeid_metadata(typeid); + let v = [self.const_usize(0), typeid_metadata]; + unsafe { + llvm::LLVMGlobalSetMetadata( + function, + llvm::MD_type as c_uint, + llvm::LLVMValueAsMetadata(llvm::LLVMMDNodeInContext( + self.llcx, + v.as_ptr(), + v.len() as c_uint, + )), + ) + } + } + + fn typeid_metadata(&self, typeid: String) -> &'ll Value { + unsafe { + llvm::LLVMMDStringInContext( + self.llcx, + typeid.as_ptr() as *const c_char, + typeid.len() as c_uint, + ) + } + } +} diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 81c8b9ceb13..46d6344dbb2 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -14,7 +14,7 @@ tracing = "0.1" libc = "0.2.50" jobserver = "0.1.22" tempfile = "3.2" -thorin-dwp = "0.2" +thorin-dwp = "0.3" pathdiff = "0.2.0" serde_json = "1.0.59" snap = "1" diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index 1cb8d342381..53550b049db 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -51,10 +51,37 @@ pub trait ArchiveBuilder<'a> { fn build(self) -> bool; + fn sess(&self) -> &Session; + + /// Creates a DLL Import Library <https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-creation#creating-an-import-library>. + /// and returns the path on disk to that import library. + /// This functions doesn't take `self` so that it can be called from + /// `linker_with_args`, which is specialized on `ArchiveBuilder` but + /// doesn't take or create an instance of that type. + fn create_dll_import_lib( + sess: &Session, + lib_name: &str, + dll_imports: &[DllImport], + tmpdir: &Path, + ) -> PathBuf; + + /// Creates a DLL Import Library <https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-creation#creating-an-import-library> + /// and adds it to the current compilation's set of archives. fn inject_dll_import_lib( &mut self, lib_name: &str, dll_imports: &[DllImport], tmpdir: &MaybeTempDir, - ); + ) { + let output_path = + Self::create_dll_import_lib(self.sess(), lib_name, dll_imports, tmpdir.as_ref()); + + self.add_archive(&output_path, |_| false).unwrap_or_else(|e| { + self.sess().fatal(&format!( + "failed to add native library {}: {}", + output_path.display(), + e + )); + }); + } } diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 878a670cba3..c26e8d99bbb 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -120,7 +120,7 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>( &out_filename, codegen_results, path.as_ref(), - ); + )?; } } if sess.opts.json_artifact_notifications { @@ -336,6 +336,7 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>( | NativeLibKind::Dylib { .. } | NativeLibKind::Framework { .. } | NativeLibKind::RawDylib + | NativeLibKind::LinkArg | NativeLibKind::Unspecified => continue, } if let Some(name) = lib.name { @@ -650,7 +651,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( out_filename: &Path, codegen_results: &CodegenResults, tmpdir: &Path, -) { +) -> Result<(), ErrorGuaranteed> { info!("preparing {:?} to {:?}", crate_type, out_filename); let (linker_path, flavor) = linker_and_flavor(sess); let mut cmd = linker_with_args::<B>( @@ -661,7 +662,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( tmpdir, out_filename, codegen_results, - ); + )?; linker::disable_localization(&mut cmd); @@ -1000,6 +1001,8 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( (Strip::None, _) => {} } } + + Ok(()) } // Temporarily support both -Z strip and -C strip @@ -1287,6 +1290,7 @@ fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLib]) { } // These are included, no need to print them NativeLibKind::Static { bundle: None | Some(true), .. } + | NativeLibKind::LinkArg | NativeLibKind::RawDylib => None, } }) @@ -1848,7 +1852,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( tmpdir: &Path, out_filename: &Path, codegen_results: &CodegenResults, -) -> Command { +) -> Result<Command, ErrorGuaranteed> { let crt_objects_fallback = crt_objects_fallback(sess, crate_type); let cmd = &mut *super::linker::get_linker( sess, @@ -1955,6 +1959,18 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( add_upstream_native_libraries(cmd, sess, codegen_results); } + // Link with the import library generated for any raw-dylib functions. + for (raw_dylib_name, raw_dylib_imports) in + collate_raw_dylibs(sess, &codegen_results.crate_info.used_libraries)? + { + cmd.add_object(&B::create_dll_import_lib( + sess, + &raw_dylib_name, + &raw_dylib_imports, + tmpdir, + )); + } + // Library linking above uses some global state for things like `-Bstatic`/`-Bdynamic` to make // command line shorter, reset it to default here before adding more libraries. cmd.reset_per_library_state(); @@ -1998,7 +2014,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( // to it and remove the option. add_post_link_args(cmd, sess, flavor); - cmd.take_cmd() + Ok(cmd.take_cmd()) } fn add_order_independent_options( @@ -2082,7 +2098,12 @@ fn add_order_independent_options( // sections to ensure we have all the data for PGO. let keep_metadata = crate_type == CrateType::Dylib || sess.opts.cg.profile_generate.enabled(); - cmd.gc_sections(keep_metadata); + if crate_type != CrateType::Executable || !sess.opts.unstable_opts.export_executable_symbols + { + cmd.gc_sections(keep_metadata); + } else { + cmd.no_gc_sections(); + } } cmd.set_output_kind(link_output_kind, out_filename); @@ -2222,8 +2243,10 @@ fn add_local_native_libraries( } } NativeLibKind::RawDylib => { - // FIXME(#58713): Proper handling for raw dylibs. - bug!("raw_dylib feature not yet implemented"); + // Ignore RawDylib here, they are handled separately in linker_with_args(). + } + NativeLibKind::LinkArg => { + cmd.arg(name); } } } @@ -2362,19 +2385,34 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>( (lib.name, lib.kind, lib.verbatim) }; - if let NativeLibKind::Static { bundle: Some(false), whole_archive } = - lib.kind - { - let verbatim = lib.verbatim.unwrap_or(false); - if whole_archive == Some(true) { + match lib.kind { + NativeLibKind::Static { + bundle: Some(false), + whole_archive: Some(true), + } => { cmd.link_whole_staticlib( name, - verbatim, + lib.verbatim.unwrap_or(false), search_path.get_or_init(|| archive_search_paths(sess)), ); - } else { - cmd.link_staticlib(name, verbatim); } + NativeLibKind::Static { + bundle: Some(false), + whole_archive: Some(false) | None, + } => { + cmd.link_staticlib(name, lib.verbatim.unwrap_or(false)); + } + NativeLibKind::LinkArg => { + cmd.arg(name); + } + NativeLibKind::Dylib { .. } + | NativeLibKind::Framework { .. } + | NativeLibKind::Unspecified + | NativeLibKind::RawDylib => {} + NativeLibKind::Static { + bundle: Some(true) | None, + whole_archive: _, + } => {} } } } @@ -2565,7 +2603,7 @@ fn add_upstream_native_libraries( // already included them in add_local_native_libraries and // add_upstream_rust_crates NativeLibKind::Static { .. } => {} - NativeLibKind::RawDylib => {} + NativeLibKind::RawDylib | NativeLibKind::LinkArg => {} } } } diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index d4a9db4af23..a4bbdefbb87 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -566,9 +566,7 @@ impl<'a> Linker for GccLinker<'a> { } fn no_gc_sections(&mut self) { - if self.sess.target.is_like_osx { - self.linker_arg("-no_dead_strip"); - } else if self.sess.target.linker_is_gnu || self.sess.target.is_like_wasm { + if self.sess.target.linker_is_gnu || self.sess.target.is_like_wasm { self.linker_arg("--no-gc-sections"); } } @@ -640,9 +638,14 @@ impl<'a> Linker for GccLinker<'a> { fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]) { // Symbol visibility in object files typically takes care of this. - if crate_type == CrateType::Executable && self.sess.target.override_export_symbols.is_none() - { - return; + if crate_type == CrateType::Executable { + let should_export_executable_symbols = + self.sess.opts.unstable_opts.export_executable_symbols; + if self.sess.target.override_export_symbols.is_none() + && !should_export_executable_symbols + { + return; + } } // We manually create a list of exported symbols to ensure we don't expose any more. @@ -969,7 +972,11 @@ impl<'a> Linker for MsvcLinker<'a> { fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]) { // Symbol visibility takes care of this typically if crate_type == CrateType::Executable { - return; + let should_export_executable_symbols = + self.sess.opts.unstable_opts.export_executable_symbols; + if !should_export_executable_symbols { + return; + } } let path = tmpdir.join("lib.def"); diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index c2ac21eec67..1b5ad87107a 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -37,6 +37,7 @@ use rustc_target::spec::{MergeFunctions, SanitizerSet}; use std::any::Any; use std::fs; use std::io; +use std::marker::PhantomData; use std::mem; use std::path::{Path, PathBuf}; use std::str; @@ -99,6 +100,7 @@ pub struct ModuleConfig { pub emit_ir: bool, pub emit_asm: bool, pub emit_obj: EmitObj, + pub emit_thin_lto: bool, pub bc_cmdline: String, // Miscellaneous flags. These are mostly copied from command-line @@ -218,6 +220,7 @@ impl ModuleConfig { false ), emit_obj, + emit_thin_lto: sess.opts.unstable_opts.emit_thin_lto, bc_cmdline: sess.target.bitcode_llvm_cmdline.to_string(), verify_llvm_ir: sess.verify_llvm_ir(), @@ -473,10 +476,13 @@ pub fn start_async_codegen<B: ExtraBackendMethods>( metadata_module, crate_info, - coordinator_send, codegen_worker_receive, shared_emitter_main, - future: coordinator_thread, + coordinator: Coordinator { + sender: coordinator_send, + future: Some(coordinator_thread), + phantom: PhantomData, + }, output_filenames: tcx.output_filenames(()).clone(), } } @@ -1271,6 +1277,7 @@ fn start_executing_work<B: ExtraBackendMethods>( // work to be done. while !codegen_done || running > 0 + || main_thread_worker_state == MainThreadWorkerState::LLVMing || (!codegen_aborted && !(work_items.is_empty() && needs_fat_lto.is_empty() @@ -1468,14 +1475,12 @@ fn start_executing_work<B: ExtraBackendMethods>( if !cgcx.opts.unstable_opts.no_parallel_llvm { helper.request_token(); } - assert!(!codegen_aborted); assert_eq!(main_thread_worker_state, MainThreadWorkerState::Codegenning); main_thread_worker_state = MainThreadWorkerState::Idle; } Message::CodegenComplete => { codegen_done = true; - assert!(!codegen_aborted); assert_eq!(main_thread_worker_state, MainThreadWorkerState::Codegenning); main_thread_worker_state = MainThreadWorkerState::Idle; } @@ -1487,10 +1492,8 @@ fn start_executing_work<B: ExtraBackendMethods>( // then conditions above will ensure no more work is spawned but // we'll keep executing this loop until `running` hits 0. Message::CodegenAborted => { - assert!(!codegen_aborted); codegen_done = true; codegen_aborted = true; - assert_eq!(main_thread_worker_state, MainThreadWorkerState::Codegenning); } Message::Done { result: Ok(compiled_module), worker_id } => { free_worker(worker_id); @@ -1530,13 +1533,20 @@ fn start_executing_work<B: ExtraBackendMethods>( Message::Done { result: Err(None), worker_id: _ } => { bug!("worker thread panicked"); } - Message::Done { result: Err(Some(WorkerFatalError)), worker_id: _ } => { - return Err(()); + Message::Done { result: Err(Some(WorkerFatalError)), worker_id } => { + // Similar to CodegenAborted, wait for remaining work to finish. + free_worker(worker_id); + codegen_done = true; + codegen_aborted = true; } Message::CodegenItem => bug!("the coordinator should not receive codegen requests"), } } + if codegen_aborted { + return Err(()); + } + let needs_link = mem::take(&mut needs_link); if !needs_link.is_empty() { assert!(compiled_modules.is_empty()); @@ -1826,16 +1836,39 @@ impl SharedEmitterMain { } } +pub struct Coordinator<B: ExtraBackendMethods> { + pub sender: Sender<Box<dyn Any + Send>>, + future: Option<thread::JoinHandle<Result<CompiledModules, ()>>>, + // Only used for the Message type. + phantom: PhantomData<B>, +} + +impl<B: ExtraBackendMethods> Coordinator<B> { + fn join(mut self) -> std::thread::Result<Result<CompiledModules, ()>> { + self.future.take().unwrap().join() + } +} + +impl<B: ExtraBackendMethods> Drop for Coordinator<B> { + fn drop(&mut self) { + if let Some(future) = self.future.take() { + // If we haven't joined yet, signal to the coordinator that it should spawn no more + // work, and wait for worker threads to finish. + drop(self.sender.send(Box::new(Message::CodegenAborted::<B>))); + drop(future.join()); + } + } +} + pub struct OngoingCodegen<B: ExtraBackendMethods> { pub backend: B, pub metadata: EncodedMetadata, pub metadata_module: Option<CompiledModule>, pub crate_info: CrateInfo, - pub coordinator_send: Sender<Box<dyn Any + Send>>, pub codegen_worker_receive: Receiver<Message<B>>, pub shared_emitter_main: SharedEmitterMain, - pub future: thread::JoinHandle<Result<CompiledModules, ()>>, pub output_filenames: Arc<OutputFilenames>, + pub coordinator: Coordinator<B>, } impl<B: ExtraBackendMethods> OngoingCodegen<B> { @@ -1843,8 +1876,7 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> { let _timer = sess.timer("finish_ongoing_codegen"); self.shared_emitter_main.check(sess, true); - let future = self.future; - let compiled_modules = sess.time("join_worker_thread", || match future.join() { + let compiled_modules = sess.time("join_worker_thread", || match self.coordinator.join() { Ok(Ok(compiled_modules)) => compiled_modules, Ok(Err(())) => { sess.abort_if_errors(); @@ -1892,26 +1924,13 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> { // These are generally cheap and won't throw off scheduling. let cost = 0; - submit_codegened_module_to_llvm(&self.backend, &self.coordinator_send, module, cost); + submit_codegened_module_to_llvm(&self.backend, &self.coordinator.sender, module, cost); } pub fn codegen_finished(&self, tcx: TyCtxt<'_>) { self.wait_for_signal_to_codegen_item(); self.check_for_errors(tcx.sess); - drop(self.coordinator_send.send(Box::new(Message::CodegenComplete::<B>))); - } - - /// Consumes this context indicating that codegen was entirely aborted, and - /// we need to exit as quickly as possible. - /// - /// This method blocks the current thread until all worker threads have - /// finished, and all worker threads should have exited or be real close to - /// exiting at this point. - pub fn codegen_aborted(self) { - // Signal to the coordinator it should spawn no more work and start - // shutdown. - drop(self.coordinator_send.send(Box::new(Message::CodegenAborted::<B>))); - drop(self.future.join()); + drop(self.coordinator.sender.send(Box::new(Message::CodegenComplete::<B>))); } pub fn check_for_errors(&self, sess: &Session) { diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 7def30af2b3..a840b270974 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -39,7 +39,6 @@ use rustc_target::abi::{Align, VariantIdx}; use std::collections::BTreeSet; use std::convert::TryFrom; -use std::ops::{Deref, DerefMut}; use std::time::{Duration, Instant}; use itertools::Itertools; @@ -171,7 +170,7 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( ); let new_vptr = bx.load(ptr_ty, gep, ptr_align); bx.nonnull_metadata(new_vptr); - // Vtable loads are invariant. + // VTable loads are invariant. bx.set_invariant_load(new_vptr); new_vptr } else { @@ -583,7 +582,6 @@ pub fn codegen_crate<B: ExtraBackendMethods>( metadata_module, codegen_units.len(), ); - let ongoing_codegen = AbortCodegenOnDrop::<B>(Some(ongoing_codegen)); // Codegen an allocator shim, if necessary. // @@ -704,7 +702,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>( submit_codegened_module_to_llvm( &backend, - &ongoing_codegen.coordinator_send, + &ongoing_codegen.coordinator.sender, module, cost, ); @@ -714,7 +712,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>( submit_pre_lto_module_to_llvm( &backend, tcx, - &ongoing_codegen.coordinator_send, + &ongoing_codegen.coordinator.sender, CachedModuleCodegen { name: cgu.name().to_string(), source: cgu.previous_work_product(tcx), @@ -725,7 +723,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>( CguReuse::PostLto => { submit_post_lto_module_to_llvm( &backend, - &ongoing_codegen.coordinator_send, + &ongoing_codegen.coordinator.sender, CachedModuleCodegen { name: cgu.name().to_string(), source: cgu.previous_work_product(tcx), @@ -752,55 +750,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>( } ongoing_codegen.check_for_errors(tcx.sess); - - ongoing_codegen.into_inner() -} - -/// A curious wrapper structure whose only purpose is to call `codegen_aborted` -/// when it's dropped abnormally. -/// -/// In the process of working on rust-lang/rust#55238 a mysterious segfault was -/// stumbled upon. The segfault was never reproduced locally, but it was -/// suspected to be related to the fact that codegen worker threads were -/// sticking around by the time the main thread was exiting, causing issues. -/// -/// This structure is an attempt to fix that issue where the `codegen_aborted` -/// message will block until all workers have finished. This should ensure that -/// even if the main codegen thread panics we'll wait for pending work to -/// complete before returning from the main thread, hopefully avoiding -/// segfaults. -/// -/// If you see this comment in the code, then it means that this workaround -/// worked! We may yet one day track down the mysterious cause of that -/// segfault... -struct AbortCodegenOnDrop<B: ExtraBackendMethods>(Option<OngoingCodegen<B>>); - -impl<B: ExtraBackendMethods> AbortCodegenOnDrop<B> { - fn into_inner(mut self) -> OngoingCodegen<B> { - self.0.take().unwrap() - } -} - -impl<B: ExtraBackendMethods> Deref for AbortCodegenOnDrop<B> { - type Target = OngoingCodegen<B>; - - fn deref(&self) -> &OngoingCodegen<B> { - self.0.as_ref().unwrap() - } -} - -impl<B: ExtraBackendMethods> DerefMut for AbortCodegenOnDrop<B> { - fn deref_mut(&mut self) -> &mut OngoingCodegen<B> { - self.0.as_mut().unwrap() - } -} - -impl<B: ExtraBackendMethods> Drop for AbortCodegenOnDrop<B> { - fn drop(&mut self) { - if let Some(codegen) = self.0.take() { - codegen.codegen_aborted(); - } - } + ongoing_codegen } impl CrateInfo { diff --git a/compiler/rustc_codegen_ssa/src/meth.rs b/compiler/rustc_codegen_ssa/src/meth.rs index df42d804566..27d791d90a5 100644 --- a/compiler/rustc_codegen_ssa/src/meth.rs +++ b/compiler/rustc_codegen_ssa/src/meth.rs @@ -39,7 +39,7 @@ impl<'a, 'tcx> VirtualIndex { let gep = bx.inbounds_gep(llty, llvtable, &[bx.const_usize(self.0)]); let ptr = bx.load(llty, gep, ptr_align); bx.nonnull_metadata(ptr); - // Vtable loads are invariant. + // VTable loads are invariant. bx.set_invariant_load(ptr); ptr } @@ -58,7 +58,7 @@ impl<'a, 'tcx> VirtualIndex { let usize_align = bx.tcx().data_layout.pointer_align.abi; let gep = bx.inbounds_gep(llty, llvtable, &[bx.const_usize(self.0)]); let ptr = bx.load(llty, gep, usize_align); - // Vtable loads are invariant. + // VTable loads are invariant. bx.set_invariant_load(ptr); ptr } diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 773c55cf551..3eee58d9d1c 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -20,7 +20,7 @@ use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; use rustc_middle::ty::{self, Instance, Ty, TypeVisitable}; use rustc_span::source_map::Span; use rustc_span::{sym, Symbol}; -use rustc_symbol_mangling::typeid_for_fnabi; +use rustc_symbol_mangling::typeid::typeid_for_fnabi; use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode}; use rustc_target::abi::{self, HasDataLayout, WrappingRange}; use rustc_target::spec::abi::Abi; @@ -918,7 +918,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // FIXME(rcvalle): Add support for generalized identifiers. // FIXME(rcvalle): Create distinct unnamed MDNodes for internal identifiers. let typeid = typeid_for_fnabi(bx.tcx(), fn_abi); - let typeid_metadata = bx.typeid_metadata(typeid); + let typeid_metadata = self.cx.typeid_metadata(typeid); // Test whether the function pointer is associated with the type identifier. let cond = bx.type_test(fn_ptr, typeid_metadata); diff --git a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs index a283bf1de76..f1fe495282a 100644 --- a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs @@ -9,11 +9,8 @@ use super::FunctionCx; impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { pub fn codegen_coverage(&self, bx: &mut Bx, coverage: Coverage, scope: SourceScope) { // Determine the instance that coverage data was originally generated for. - let scope_data = &self.mir.source_scopes[scope]; - let instance = if let Some((inlined_instance, _)) = scope_data.inlined { - self.monomorphize(inlined_instance) - } else if let Some(inlined_scope) = scope_data.inlined_parent_scope { - self.monomorphize(self.mir.source_scopes[inlined_scope].inlined.unwrap().0) + let instance = if let Some(inlined) = scope.inlined_instance(&self.mir.source_scopes) { + self.monomorphize(inlined) } else { self.instance }; diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 645afae30d8..94ac71a4dd2 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -3,12 +3,16 @@ use super::place::PlaceRef; use super::FunctionCx; use crate::common::{span_invalid_monomorphization_error, IntPredicate}; use crate::glue; +use crate::meth; use crate::traits::*; use crate::MemFlags; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::{sym, Span}; -use rustc_target::abi::call::{FnAbi, PassMode}; +use rustc_target::abi::{ + call::{FnAbi, PassMode}, + WrappingRange, +}; fn copy_intrinsic<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, @@ -102,6 +106,20 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.const_usize(bx.layout_of(tp_ty).align.abi.bytes()) } } + sym::vtable_size | sym::vtable_align => { + let vtable = args[0].immediate(); + let idx = match name { + sym::vtable_size => ty::COMMON_VTABLE_ENTRIES_SIZE, + sym::vtable_align => ty::COMMON_VTABLE_ENTRIES_ALIGN, + _ => bug!(), + }; + let value = meth::VirtualIndex::from_index(idx).get_usize(bx, vtable); + if name == sym::vtable_align { + // Alignment is always nonzero. + bx.range_metadata(value, WrappingRange { start: 1, end: !0 }); + }; + value + } sym::pref_align_of | sym::needs_drop | sym::type_id diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index ec3f7a2156a..8ee375fa9e3 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -3,7 +3,6 @@ use rustc_middle::mir; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TypeFoldable, TypeVisitable}; -use rustc_symbol_mangling::typeid_for_fnabi; use rustc_target::abi::call::{FnAbi, PassMode}; use std::iter; @@ -247,13 +246,6 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( for (bb, _) in traversal::reverse_postorder(&mir) { fx.codegen_block(bb); } - - // For backends that support CFI using type membership (i.e., testing whether a given pointer - // is associated with a type identifier). - if cx.tcx().sess.is_sanitizer_cfi_enabled() { - let typeid = typeid_for_fnabi(cx.tcx(), fn_abi); - bx.type_metadata(llfn, typeid); - } } /// Produces, for each argument, a `Value` pointing at the diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index 421d6f807ae..268c4d76503 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -411,21 +411,6 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { downcast } - pub fn project_type<Bx: BuilderMethods<'a, 'tcx, Value = V>>( - &self, - bx: &mut Bx, - ty: Ty<'tcx>, - ) -> Self { - let mut downcast = *self; - downcast.layout = bx.cx().layout_of(ty); - - // Cast to the appropriate type. - let variant_ty = bx.cx().backend_type(downcast.layout); - downcast.llval = bx.pointercast(downcast.llval, bx.cx().type_ptr_to(variant_ty)); - - downcast - } - pub fn storage_live<Bx: BuilderMethods<'a, 'tcx, Value = V>>(&self, bx: &mut Bx) { bx.lifetime_start(self.llval, self.layout.size); } @@ -450,18 +435,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { LocalRef::Place(place) => place, LocalRef::UnsizedPlace(place) => bx.load_operand(place).deref(cx), LocalRef::Operand(..) => { - if let Some(elem) = place_ref - .projection - .iter() - .enumerate() - .find(|elem| matches!(elem.1, mir::ProjectionElem::Deref)) - { - base = elem.0 + 1; + if place_ref.has_deref() { + base = 1; let cg_base = self.codegen_consume( bx, - mir::PlaceRef { projection: &place_ref.projection[..elem.0], ..place_ref }, + mir::PlaceRef { projection: &place_ref.projection[..0], ..place_ref }, ); - cg_base.deref(bx.cx()) } else { bug!("using operand local {:?} as place", place_ref); @@ -474,7 +453,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::ProjectionElem::Field(ref field, _) => { cg_base.project_field(bx, field.index()) } - mir::ProjectionElem::OpaqueCast(ty) => cg_base.project_type(bx, ty), mir::ProjectionElem::Index(index) => { let index = &mir::Operand::Copy(mir::Place::from(index)); let index = self.codegen_operand(bx, index); diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index bfdef2dc0e8..ecad0518533 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -249,6 +249,7 @@ const WASM_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[ ("bulk-memory", Some(sym::wasm_target_feature)), ("mutable-globals", Some(sym::wasm_target_feature)), ("reference-types", Some(sym::wasm_target_feature)), + ("sign-ext", Some(sym::wasm_target_feature)), ]; const BPF_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[("alu32", Some(sym::bpf_target_feature))]; diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 1bbe10141fc..9f49749bb41 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -160,8 +160,6 @@ pub trait BuilderMethods<'a, 'tcx>: fn range_metadata(&mut self, load: Self::Value, range: WrappingRange); fn nonnull_metadata(&mut self, load: Self::Value); - fn type_metadata(&mut self, function: Self::Function, typeid: String); - fn typeid_metadata(&mut self, typeid: String) -> Self::Value; fn store(&mut self, val: Self::Value, ptr: Self::Value, align: Align) -> Self::Value; fn store_with_flags( diff --git a/compiler/rustc_codegen_ssa/src/traits/mod.rs b/compiler/rustc_codegen_ssa/src/traits/mod.rs index 396768e0a42..782fdadbfb8 100644 --- a/compiler/rustc_codegen_ssa/src/traits/mod.rs +++ b/compiler/rustc_codegen_ssa/src/traits/mod.rs @@ -40,7 +40,8 @@ pub use self::intrinsic::IntrinsicCallMethods; pub use self::misc::MiscMethods; pub use self::statics::{StaticBuilderMethods, StaticMethods}; pub use self::type_::{ - ArgAbiMethods, BaseTypeMethods, DerivedTypeMethods, LayoutTypeMethods, TypeMethods, + ArgAbiMethods, BaseTypeMethods, DerivedTypeMethods, LayoutTypeMethods, TypeMembershipMethods, + TypeMethods, }; pub use self::write::{ModuleBufferMethods, ThinBufferMethods, WriteBackendMethods}; diff --git a/compiler/rustc_codegen_ssa/src/traits/statics.rs b/compiler/rustc_codegen_ssa/src/traits/statics.rs index a2a3cb56c78..413d31bb942 100644 --- a/compiler/rustc_codegen_ssa/src/traits/statics.rs +++ b/compiler/rustc_codegen_ssa/src/traits/statics.rs @@ -13,7 +13,9 @@ pub trait StaticMethods: BackendTypes { /// Same as add_used_global(), but only prevent the compiler from potentially removing an /// otherwise unused symbol. The linker is still permitted to drop it. /// - /// This corresponds to the semantics of the `#[used]` attribute. + /// This corresponds to the documented semantics of the `#[used]` attribute, although + /// on some targets (non-ELF), we may use `add_used_global` for `#[used]` statics + /// instead. fn add_compiler_used_global(&self, global: Self::Value); } diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs index 5d3f07317a3..8158e8dd011 100644 --- a/compiler/rustc_codegen_ssa/src/traits/type_.rs +++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs @@ -117,6 +117,13 @@ pub trait LayoutTypeMethods<'tcx>: Backend<'tcx> { ) -> Self::Type; } +// For backends that support CFI using type membership (i.e., testing whether a given pointer is +// associated with a type identifier). +pub trait TypeMembershipMethods<'tcx>: Backend<'tcx> { + fn set_type_metadata(&self, function: Self::Function, typeid: String); + fn typeid_metadata(&self, typeid: String) -> Self::Value; +} + pub trait ArgAbiMethods<'tcx>: HasCodegen<'tcx> { fn store_fn_arg( &mut self, @@ -133,6 +140,12 @@ pub trait ArgAbiMethods<'tcx>: HasCodegen<'tcx> { fn arg_memory_ty(&self, arg_abi: &ArgAbi<'tcx, Ty<'tcx>>) -> Self::Type; } -pub trait TypeMethods<'tcx>: DerivedTypeMethods<'tcx> + LayoutTypeMethods<'tcx> {} +pub trait TypeMethods<'tcx>: + DerivedTypeMethods<'tcx> + LayoutTypeMethods<'tcx> + TypeMembershipMethods<'tcx> +{ +} -impl<'tcx, T> TypeMethods<'tcx> for T where Self: DerivedTypeMethods<'tcx> + LayoutTypeMethods<'tcx> {} +impl<'tcx, T> TypeMethods<'tcx> for T where + Self: DerivedTypeMethods<'tcx> + LayoutTypeMethods<'tcx> + TypeMembershipMethods<'tcx> +{ +} 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 f03ceb54830..ba8222dc152 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -173,18 +173,17 @@ pub(super) fn op_to_const<'tcx>( Immediate::ScalarPair(a, b) => { debug!("ScalarPair(a: {:?}, b: {:?})", a, b); // We know `offset` is relative to the allocation, so we can use `into_parts`. - let (data, start) = - match ecx.scalar_to_ptr(a.check_init().unwrap()).unwrap().into_parts() { - (Some(alloc_id), offset) => { - (ecx.tcx.global_alloc(alloc_id).unwrap_memory(), offset.bytes()) - } - (None, _offset) => ( - ecx.tcx.intern_const_alloc( - Allocation::from_bytes_byte_aligned_immutable(b"" as &[u8]), - ), - 0, - ), - }; + let (data, start) = match a.to_pointer(ecx).unwrap().into_parts() { + (Some(alloc_id), offset) => { + (ecx.tcx.global_alloc(alloc_id).unwrap_memory(), offset.bytes()) + } + (None, _offset) => ( + ecx.tcx.intern_const_alloc(Allocation::from_bytes_byte_aligned_immutable( + b"" as &[u8], + )), + 0, + ), + }; let len = b.to_machine_usize(ecx).unwrap(); let start = start.try_into().unwrap(); let len: usize = len.try_into().unwrap(); diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index e00e667fb71..fc2e6652a3d 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -309,7 +309,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, ecx: &mut InterpCx<'mir, 'tcx, Self>, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx>], - dest: &PlaceTy<'tcx, Self::PointerTag>, + dest: &PlaceTy<'tcx, Self::Provenance>, target: Option<mir::BasicBlock>, _unwind: StackPopUnwind, ) -> InterpResult<'tcx> { @@ -369,7 +369,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, // we don't deallocate it. let (alloc_id, _, _) = ecx.ptr_get_alloc_id(ptr)?; let is_allocated_in_another_const = matches!( - ecx.tcx.get_global_alloc(alloc_id), + ecx.tcx.try_get_global_alloc(alloc_id), Some(interpret::GlobalAlloc::Memory(_)) ); @@ -470,14 +470,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, #[inline(always)] fn stack<'a>( ecx: &'a InterpCx<'mir, 'tcx, Self>, - ) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>] { + ) -> &'a [Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>] { &ecx.machine.stack } #[inline(always)] fn stack_mut<'a>( ecx: &'a mut InterpCx<'mir, 'tcx, Self>, - ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>> { + ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>> { &mut ecx.machine.stack } diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index edc4c13b6e8..948c3349498 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -138,7 +138,7 @@ pub(crate) fn deref_mir_constant<'tcx>( 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, + tcx.global_alloc(alloc_id).unwrap_memory().0.0.mutability, Mutability::Not, "deref_mir_constant cannot be used with mutable allocations as \ that could allow pattern matching to observe mutable statics", diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 5d598b65c72..c97c31eb9da 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -18,10 +18,10 @@ use super::{ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn cast( &mut self, - src: &OpTy<'tcx, M::PointerTag>, + src: &OpTy<'tcx, M::Provenance>, cast_kind: CastKind, cast_ty: Ty<'tcx>, - dest: &PlaceTy<'tcx, M::PointerTag>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { use rustc_middle::mir::CastKind::*; // FIXME: In which cases should we trigger UB when the source is uninit? @@ -114,9 +114,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn misc_cast( &mut self, - src: &ImmTy<'tcx, M::PointerTag>, + src: &ImmTy<'tcx, M::Provenance>, cast_ty: Ty<'tcx>, - ) -> InterpResult<'tcx, Immediate<M::PointerTag>> { + ) -> InterpResult<'tcx, Immediate<M::Provenance>> { use rustc_type_ir::sty::TyKind::*; trace!("Casting {:?}: {:?} to {:?}", *src, src.layout.ty, cast_ty); @@ -173,14 +173,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn pointer_expose_address_cast( &mut self, - src: &ImmTy<'tcx, M::PointerTag>, + src: &ImmTy<'tcx, M::Provenance>, cast_ty: Ty<'tcx>, - ) -> InterpResult<'tcx, Immediate<M::PointerTag>> { + ) -> InterpResult<'tcx, Immediate<M::Provenance>> { assert_matches!(src.layout.ty.kind(), ty::RawPtr(_) | ty::FnPtr(_)); assert!(cast_ty.is_integral()); let scalar = src.to_scalar()?; - let ptr = self.scalar_to_ptr(scalar)?; + let ptr = scalar.to_pointer(self)?; match ptr.into_pointer_or_addr() { Ok(ptr) => M::expose_ptr(self, ptr)?, Err(_) => {} // Do nothing, exposing an invalid pointer (`None` provenance) is a NOP. @@ -190,9 +190,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn pointer_from_exposed_address_cast( &mut self, - src: &ImmTy<'tcx, M::PointerTag>, + src: &ImmTy<'tcx, M::Provenance>, cast_ty: Ty<'tcx>, - ) -> InterpResult<'tcx, Immediate<M::PointerTag>> { + ) -> InterpResult<'tcx, Immediate<M::Provenance>> { assert!(src.layout.ty.is_integral()); assert_matches!(cast_ty.kind(), ty::RawPtr(_)); @@ -208,10 +208,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn cast_from_int_like( &self, - scalar: Scalar<M::PointerTag>, // input value (there is no ScalarTy so we separate data+layout) + scalar: Scalar<M::Provenance>, // input value (there is no ScalarTy so we separate data+layout) src_layout: TyAndLayout<'tcx>, cast_ty: Ty<'tcx>, - ) -> InterpResult<'tcx, Scalar<M::PointerTag>> { + ) -> InterpResult<'tcx, Scalar<M::Provenance>> { // 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`. @@ -245,9 +245,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }) } - fn cast_from_float<F>(&self, f: F, dest_ty: Ty<'tcx>) -> Scalar<M::PointerTag> + fn cast_from_float<F>(&self, f: F, dest_ty: Ty<'tcx>) -> Scalar<M::Provenance> where - F: Float + Into<Scalar<M::PointerTag>> + FloatConvert<Single> + FloatConvert<Double>, + F: Float + Into<Scalar<M::Provenance>> + FloatConvert<Single> + FloatConvert<Double>, { use rustc_type_ir::sty::TyKind::*; match *dest_ty.kind() { @@ -279,8 +279,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { fn unsize_into_ptr( &mut self, - src: &OpTy<'tcx, M::PointerTag>, - dest: &PlaceTy<'tcx, M::PointerTag>, + src: &OpTy<'tcx, M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, // The pointee types source_ty: Ty<'tcx>, cast_ty: Ty<'tcx>, @@ -298,30 +298,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.write_immediate(val, dest) } (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => { - let val = self.read_immediate(src)?; - if data_a.principal_def_id() == data_b.principal_def_id() { - return self.write_immediate(*val, dest); - } - // trait upcasting coercion - let vptr_entry_idx = self.tcx.vtable_trait_upcasting_coercion_new_vptr_slot(( - src_pointee_ty, - dest_pointee_ty, - )); - - if let Some(entry_idx) = vptr_entry_idx { - let entry_idx = u64::try_from(entry_idx).unwrap(); - let (old_data, old_vptr) = val.to_scalar_pair()?; - let old_vptr = self.scalar_to_ptr(old_vptr)?; - let new_vptr = self - .read_new_vtable_after_trait_upcasting_from_vtable(old_vptr, entry_idx)?; - self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest) - } else { - self.write_immediate(*val, dest) + let (old_data, old_vptr) = self.read_immediate(src)?.to_scalar_pair()?; + let old_vptr = old_vptr.to_pointer(self)?; + let (ty, old_trait) = self.get_ptr_vtable(old_vptr)?; + if old_trait != data_a.principal() { + throw_ub_format!("upcast on a pointer whose vtable does not match its type"); } + let new_vptr = self.get_vtable_ptr(ty, data_b.principal())?; + self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest) } (_, &ty::Dynamic(ref data, _)) => { // Initial cast from sized to dyn trait - let vtable = self.get_vtable(src_pointee_ty, data.principal())?; + let vtable = self.get_vtable_ptr(src_pointee_ty, data.principal())?; let ptr = self.read_immediate(src)?.to_scalar()?; let val = Immediate::new_dyn_trait(ptr, vtable, &*self.tcx); self.write_immediate(val, dest) @@ -335,9 +323,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { fn unsize_into( &mut self, - src: &OpTy<'tcx, M::PointerTag>, + src: &OpTy<'tcx, M::Provenance>, cast_ty: TyAndLayout<'tcx>, - dest: &PlaceTy<'tcx, M::PointerTag>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { trace!("Unsizing {:?} of type {} into {:?}", *src, src.layout.ty, cast_ty.ty); match (&src.layout.ty.kind(), &cast_ty.ty.kind()) { diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 6feb5219ab1..150d6589b08 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -81,7 +81,7 @@ impl Drop for SpanGuard { } /// A stack frame. -pub struct Frame<'mir, 'tcx, Tag: Provenance = AllocId, Extra = ()> { +pub struct Frame<'mir, 'tcx, Prov: Provenance = AllocId, Extra = ()> { //////////////////////////////////////////////////////////////////////////////// // Function and callsite information //////////////////////////////////////////////////////////////////////////////// @@ -102,7 +102,7 @@ pub struct Frame<'mir, 'tcx, Tag: Provenance = AllocId, Extra = ()> { /// The location where the result of the current stack frame should be written to, /// and its layout in the caller. - pub return_place: PlaceTy<'tcx, Tag>, + pub return_place: PlaceTy<'tcx, Prov>, /// The list of locals for this stack frame, stored in order as /// `[return_ptr, arguments..., variables..., temporaries...]`. @@ -111,7 +111,7 @@ pub struct Frame<'mir, 'tcx, Tag: Provenance = AllocId, Extra = ()> { /// can either directly contain `Scalar` or refer to some part of an `Allocation`. /// /// Do *not* access this directly; always go through the machine hook! - pub locals: IndexVec<mir::Local, LocalState<'tcx, Tag>>, + pub locals: IndexVec<mir::Local, LocalState<'tcx, Prov>>, /// The span of the `tracing` crate is stored here. /// When the guard is dropped, the span is exited. This gives us @@ -166,15 +166,15 @@ pub enum StackPopCleanup { /// State of a local variable including a memoized layout #[derive(Clone, Debug)] -pub struct LocalState<'tcx, Tag: Provenance = AllocId> { - pub value: LocalValue<Tag>, +pub struct LocalState<'tcx, Prov: Provenance = AllocId> { + pub value: LocalValue<Prov>, /// Don't modify if `Some`, this is only used to prevent computing the layout twice pub layout: Cell<Option<TyAndLayout<'tcx>>>, } /// Current value of a local variable #[derive(Copy, Clone, Debug)] // Miri debug-prints these -pub enum LocalValue<Tag: Provenance = AllocId> { +pub enum LocalValue<Prov: Provenance = AllocId> { /// This local is not currently alive, and cannot be used at all. Dead, /// A normal, live local. @@ -182,16 +182,16 @@ pub enum LocalValue<Tag: Provenance = AllocId> { /// This is an optimization over just always having a pointer here; /// we can thus avoid doing an allocation when the local just stores /// immediate values *and* never has its address taken. - Live(Operand<Tag>), + Live(Operand<Prov>), } -impl<'tcx, Tag: Provenance + 'static> LocalState<'tcx, Tag> { +impl<'tcx, Prov: Provenance + 'static> LocalState<'tcx, Prov> { /// Read the local's value or error if the local is not yet live or not live anymore. /// /// Note: This may only be invoked from the `Machine::access_local` hook and not from /// anywhere else. You may be invalidating machine invariants if you do! #[inline] - pub fn access(&self) -> InterpResult<'tcx, &Operand<Tag>> { + pub fn access(&self) -> InterpResult<'tcx, &Operand<Prov>> { match &self.value { LocalValue::Dead => throw_ub!(DeadLocal), // could even be "invalid program"? LocalValue::Live(val) => Ok(val), @@ -204,7 +204,7 @@ impl<'tcx, Tag: Provenance + 'static> LocalState<'tcx, Tag> { /// Note: This may only be invoked from the `Machine::access_local_mut` hook and not from /// anywhere else. You may be invalidating machine invariants if you do! #[inline] - pub fn access_mut(&mut self) -> InterpResult<'tcx, &mut Operand<Tag>> { + pub fn access_mut(&mut self) -> InterpResult<'tcx, &mut Operand<Prov>> { match &mut self.value { LocalValue::Dead => throw_ub!(DeadLocal), // could even be "invalid program"? LocalValue::Live(val) => Ok(val), @@ -212,8 +212,8 @@ impl<'tcx, Tag: Provenance + 'static> LocalState<'tcx, Tag> { } } -impl<'mir, 'tcx, Tag: Provenance> Frame<'mir, 'tcx, Tag> { - pub fn with_extra<Extra>(self, extra: Extra) -> Frame<'mir, 'tcx, Tag, Extra> { +impl<'mir, 'tcx, Prov: Provenance> Frame<'mir, 'tcx, Prov> { + pub fn with_extra<Extra>(self, extra: Extra) -> Frame<'mir, 'tcx, Prov, Extra> { Frame { body: self.body, instance: self.instance, @@ -227,7 +227,7 @@ impl<'mir, 'tcx, Tag: Provenance> Frame<'mir, 'tcx, Tag> { } } -impl<'mir, 'tcx, Tag: Provenance, Extra> Frame<'mir, 'tcx, Tag, Extra> { +impl<'mir, 'tcx, Prov: Provenance, Extra> Frame<'mir, 'tcx, Prov, Extra> { /// Get the current location within the Frame. /// /// If this is `Err`, we are not currently executing any particular statement in @@ -422,14 +422,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } #[inline(always)] - pub(crate) fn stack(&self) -> &[Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>] { + pub(crate) fn stack(&self) -> &[Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>] { M::stack(self) } #[inline(always)] pub(crate) fn stack_mut( &mut self, - ) -> &mut Vec<Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>> { + ) -> &mut Vec<Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>> { M::stack_mut(self) } @@ -441,12 +441,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } #[inline(always)] - pub fn frame(&self) -> &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra> { + pub fn frame(&self) -> &Frame<'mir, 'tcx, M::Provenance, M::FrameExtra> { self.stack().last().expect("no call frames exist") } #[inline(always)] - pub fn frame_mut(&mut self) -> &mut Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra> { + pub fn frame_mut(&mut self) -> &mut Frame<'mir, 'tcx, M::Provenance, M::FrameExtra> { self.stack_mut().last_mut().expect("no call frames exist") } @@ -503,7 +503,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// stack frame), to bring it into the proper environment for this interpreter. pub(super) fn subst_from_frame_and_normalize_erasing_regions<T: TypeFoldable<'tcx>>( &self, - frame: &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>, + frame: &Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>, value: T, ) -> Result<T, InterpError<'tcx>> { frame @@ -540,7 +540,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { #[inline(always)] pub fn layout_of_local( &self, - frame: &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>, + frame: &Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>, local: mir::Local, layout: Option<TyAndLayout<'tcx>>, ) -> InterpResult<'tcx, TyAndLayout<'tcx>> { @@ -569,7 +569,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// This can fail to provide an answer for extern types. pub(super) fn size_and_align_of( &self, - metadata: &MemPlaceMeta<M::PointerTag>, + metadata: &MemPlaceMeta<M::Provenance>, layout: &TyAndLayout<'tcx>, ) -> InterpResult<'tcx, Option<(Size, Align)>> { if !layout.is_unsized() { @@ -629,9 +629,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(Some((size, align))) } ty::Dynamic(..) => { - let vtable = self.scalar_to_ptr(metadata.unwrap_meta())?; + let vtable = metadata.unwrap_meta().to_pointer(self)?; // Read size and align from vtable (already checks size). - Ok(Some(self.read_size_and_align_from_vtable(vtable)?)) + Ok(Some(self.get_vtable_size_and_align(vtable)?)) } ty::Slice(_) | ty::Str => { @@ -655,7 +655,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { #[inline] pub fn size_and_align_of_mplace( &self, - mplace: &MPlaceTy<'tcx, M::PointerTag>, + mplace: &MPlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, Option<(Size, Align)>> { self.size_and_align_of(&mplace.meta, &mplace.layout) } @@ -665,7 +665,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { &mut self, instance: ty::Instance<'tcx>, body: &'mir mir::Body<'tcx>, - return_place: &PlaceTy<'tcx, M::PointerTag>, + return_place: &PlaceTy<'tcx, M::Provenance>, return_to_block: StackPopCleanup, ) -> InterpResult<'tcx> { trace!("body: {:#?}", body); @@ -891,7 +891,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } #[instrument(skip(self), level = "debug")] - fn deallocate_local(&mut self, local: LocalValue<M::PointerTag>) -> InterpResult<'tcx> { + fn deallocate_local(&mut self, local: LocalValue<M::Provenance>) -> InterpResult<'tcx> { if let LocalValue::Live(Operand::Indirect(MemPlace { ptr, .. })) = local { // All locals have a backing allocation, even if the allocation is empty // due to the local having ZST type. Hence we can `unwrap`. @@ -909,7 +909,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn eval_to_allocation( &self, gid: GlobalId<'tcx>, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { // For statics we pick `ParamEnv::reveal_all`, because statics don't have generics // and thus don't care about the parameter environment. While we could just use // `self.param_env`, that would mean we invoke the query to evaluate the static @@ -927,7 +927,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } #[must_use] - pub fn dump_place(&self, place: Place<M::PointerTag>) -> PlacePrinter<'_, 'mir, 'tcx, M> { + pub fn dump_place(&self, place: Place<M::Provenance>) -> PlacePrinter<'_, 'mir, 'tcx, M> { PlacePrinter { ecx: self, place } } @@ -956,7 +956,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Helper struct for the `dump_place` function. pub struct PlacePrinter<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> { ecx: &'a InterpCx<'mir, 'tcx, M>, - place: Place<M::PointerTag>, + place: Place<M::Provenance>, } impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index 7616e7a63d1..376b8872c90 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -33,7 +33,7 @@ pub trait CompileTimeMachine<'mir, 'tcx, T> = Machine< 'mir, 'tcx, MemoryKind = T, - PointerTag = AllocId, + Provenance = AllocId, ExtraFnVal = !, FrameExtra = (), AllocExtra = (), @@ -94,7 +94,7 @@ fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx, const_eval: // to validation to error -- it has the much better error messages, pointing out where // in the value the dangling reference lies. // The `delay_span_bug` ensures that we don't forget such a check in validation. - if tcx.get_global_alloc(alloc_id).is_none() { + if tcx.try_get_global_alloc(alloc_id).is_none() { tcx.sess.delay_span_bug(ecx.tcx.span, "tried to intern dangling pointer"); } // treat dangling pointers like other statics @@ -245,7 +245,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory if let ty::Dynamic(..) = tcx.struct_tail_erasing_lifetimes(referenced_ty, self.ecx.param_env).kind() { - let ptr = self.ecx.scalar_to_ptr(mplace.meta.unwrap_meta())?; + let ptr = mplace.meta.unwrap_meta().to_pointer(&tcx)?; if let Some(alloc_id) = ptr.provenance { // Explicitly choose const mode here, since vtables are immutable, even // if the reference of the fat pointer is mutable. @@ -454,7 +454,7 @@ pub fn intern_const_alloc_recursive< .sess .span_err(ecx.tcx.span, "encountered dangling pointer in final constant"); return Err(reported); - } else if ecx.tcx.get_global_alloc(alloc_id).is_none() { + } else if ecx.tcx.try_get_global_alloc(alloc_id).is_none() { // We have hit an `AllocId` that is neither in local or global memory and isn't // marked as dangling by local memory. That should be impossible. span_bug!(ecx.tcx.span, "encountered unknown alloc id {:?}", alloc_id); @@ -474,7 +474,7 @@ impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx, !>> layout: TyAndLayout<'tcx>, f: impl FnOnce( &mut InterpCx<'mir, 'tcx, M>, - &PlaceTy<'tcx, M::PointerTag>, + &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, ()>, ) -> InterpResult<'tcx, ConstAllocation<'tcx>> { let dest = self.allocate(layout, MemoryKind::Stack)?; diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index c6030604aed..08209eb7932 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -7,7 +7,7 @@ use std::convert::TryFrom; use rustc_hir::def_id::DefId; use rustc_middle::mir::{ self, - interpret::{ConstValue, GlobalId, InterpResult, Scalar}, + interpret::{ConstValue, GlobalId, InterpResult, PointerArithmetic, Scalar}, BinOp, }; use rustc_middle::ty; @@ -25,7 +25,7 @@ use super::{ mod caller_location; mod type_name; -fn numeric_intrinsic<Tag>(name: Symbol, bits: u128, kind: Primitive) -> Scalar<Tag> { +fn numeric_intrinsic<Prov>(name: Symbol, bits: u128, kind: Primitive) -> Scalar<Prov> { let size = match kind { Primitive::Int(integer, _) => integer.size(), _ => bug!("invalid `{}` argument: {:?}", name, bits), @@ -114,8 +114,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn emulate_intrinsic( &mut self, instance: ty::Instance<'tcx>, - args: &[OpTy<'tcx, M::PointerTag>], - dest: &PlaceTy<'tcx, M::PointerTag>, + args: &[OpTy<'tcx, M::Provenance>], + dest: &PlaceTy<'tcx, M::Provenance>, ret: Option<mir::BasicBlock>, ) -> InterpResult<'tcx, bool> { let substs = instance.substs; @@ -328,7 +328,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // We managed to find a valid allocation for one pointer, but not the other. // That means they are definitely not pointing to the same allocation. throw_ub_format!( - "{} called on pointers into different allocations", + "`{}` called on pointers into different allocations", intrinsic_name ); } @@ -336,7 +336,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Found allocation for both. They must be into the same allocation. if a_alloc_id != b_alloc_id { throw_ub_format!( - "{} called on pointers into different allocations", + "`{}` called on pointers into different allocations", intrinsic_name ); } @@ -346,47 +346,71 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; // Compute distance. - let distance = { - // 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, isize_layout); - let b_offset = ImmTy::from_uint(b_offset, isize_layout); - let (val, overflowed, _ty) = - self.overflowing_binary_op(BinOp::Sub, &a_offset, &b_offset)?; + let dist = { + // Addresses are unsigned, so this is a `usize` computation. We have to do the + // overflow check separately anyway. + let (val, overflowed, _ty) = { + let a_offset = ImmTy::from_uint(a_offset, usize_layout); + let b_offset = ImmTy::from_uint(b_offset, usize_layout); + self.overflowing_binary_op(BinOp::Sub, &a_offset, &b_offset)? + }; if overflowed { - throw_ub_format!("pointers were too far apart for {}", intrinsic_name); + // a < b + if intrinsic_name == sym::ptr_offset_from_unsigned { + throw_ub_format!( + "`{}` called when first pointer has smaller offset than second: {} < {}", + intrinsic_name, + a_offset, + b_offset, + ); + } + // The signed form of the intrinsic allows this. If we interpret the + // difference as isize, we'll get the proper signed difference. If that + // seems *positive*, they were more than isize::MAX apart. + let dist = val.to_machine_isize(self)?; + if dist >= 0 { + throw_ub_format!( + "`{}` called when first pointer is too far before second", + intrinsic_name + ); + } + dist + } else { + // b >= a + let dist = val.to_machine_isize(self)?; + // If converting to isize produced a *negative* result, we had an overflow + // because they were more than isize::MAX apart. + if dist < 0 { + throw_ub_format!( + "`{}` called when first pointer is too far ahead of second", + intrinsic_name + ); + } + dist } - val.to_machine_isize(self)? }; // Check that the range between them is dereferenceable ("in-bounds or one past the // end of the same allocation"). This is like the check in ptr_offset_inbounds. - let min_ptr = if distance >= 0 { b } else { a }; + let min_ptr = if dist >= 0 { b } else { a }; self.check_ptr_access_align( min_ptr, - Size::from_bytes(distance.unsigned_abs()), + Size::from_bytes(dist.unsigned_abs()), Align::ONE, CheckInAllocMsg::OffsetFromTest, )?; - if intrinsic_name == sym::ptr_offset_from_unsigned && distance < 0 { - throw_ub_format!( - "{} called when first pointer has smaller offset than second: {} < {}", - intrinsic_name, - a_offset, - b_offset, - ); - } - // Perform division by size to compute return value. let ret_layout = if intrinsic_name == sym::ptr_offset_from_unsigned { + assert!(0 <= dist && dist <= self.machine_isize_max()); usize_layout } else { + assert!(self.machine_isize_min() <= dist && dist <= self.machine_isize_max()); isize_layout }; let pointee_layout = self.layout_of(substs.type_at(0))?; // If ret_layout is unsigned, we checked that so is the distance, so we are good. - let val = ImmTy::from_int(distance, ret_layout); + let val = ImmTy::from_int(dist, ret_layout); let size = ImmTy::from_int(pointee_layout.size.bytes(), ret_layout); self.exact_div(&val, &size, dest)?; } @@ -492,6 +516,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let result = self.raw_eq_intrinsic(&args[0], &args[1])?; self.write_scalar(result, dest)?; } + + sym::vtable_size => { + let ptr = self.read_pointer(&args[0])?; + let (size, _align) = self.get_vtable_size_and_align(ptr)?; + self.write_scalar(Scalar::from_machine_usize(size.bytes(), self), dest)?; + } + sym::vtable_align => { + let ptr = self.read_pointer(&args[0])?; + let (_size, align) = self.get_vtable_size_and_align(ptr)?; + self.write_scalar(Scalar::from_machine_usize(align.bytes(), self), dest)?; + } + _ => return Ok(false), } @@ -502,9 +538,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn exact_div( &mut self, - a: &ImmTy<'tcx, M::PointerTag>, - b: &ImmTy<'tcx, M::PointerTag>, - dest: &PlaceTy<'tcx, M::PointerTag>, + a: &ImmTy<'tcx, M::Provenance>, + b: &ImmTy<'tcx, M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { // Performs an exact division, resulting in undefined behavior where // `x % y != 0` or `y == 0` or `x == T::MIN && y == -1`. @@ -521,9 +557,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn saturating_arith( &self, mir_op: BinOp, - l: &ImmTy<'tcx, M::PointerTag>, - r: &ImmTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, Scalar<M::PointerTag>> { + l: &ImmTy<'tcx, M::Provenance>, + r: &ImmTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, Scalar<M::Provenance>> { assert!(matches!(mir_op, BinOp::Add | BinOp::Sub)); let (val, overflowed, _ty) = self.overflowing_binary_op(mir_op, l, r)?; Ok(if overflowed { @@ -566,10 +602,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// 0, so offset-by-0 (and only 0) is okay -- except that null cannot be offset by _any_ value. pub fn ptr_offset_inbounds( &self, - ptr: Pointer<Option<M::PointerTag>>, + ptr: Pointer<Option<M::Provenance>>, pointee_ty: Ty<'tcx>, offset_count: i64, - ) -> InterpResult<'tcx, Pointer<Option<M::PointerTag>>> { + ) -> InterpResult<'tcx, Pointer<Option<M::Provenance>>> { // We cannot overflow i64 as a type's size must be <= isize::MAX. let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap(); // The computed offset, in bytes, must not overflow an isize. @@ -597,9 +633,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Copy `count*size_of::<T>()` many bytes from `*src` to `*dst`. pub(crate) fn copy_intrinsic( &mut self, - src: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>, - dst: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>, - count: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>, + src: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>, + dst: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>, + count: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>, nonoverlapping: bool, ) -> InterpResult<'tcx> { let count = self.read_scalar(&count)?.to_machine_usize(self)?; @@ -622,9 +658,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub(crate) fn write_bytes_intrinsic( &mut self, - dst: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>, - byte: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>, - count: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>, + dst: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>, + byte: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>, + count: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>, ) -> InterpResult<'tcx> { let layout = self.layout_of(dst.layout.ty.builtin_deref(true).unwrap().ty)?; @@ -645,9 +681,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub(crate) fn raw_eq_intrinsic( &mut self, - lhs: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>, - rhs: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>, - ) -> InterpResult<'tcx, Scalar<M::PointerTag>> { + lhs: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>, + rhs: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>, + ) -> InterpResult<'tcx, Scalar<M::Provenance>> { let layout = self.layout_of(lhs.layout.ty.builtin_deref(true).unwrap().ty)?; assert!(!layout.is_unsized()); 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 14fde2c305e..5864b921552 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs @@ -79,7 +79,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { filename: Symbol, line: u32, col: u32, - ) -> MPlaceTy<'tcx, M::PointerTag> { + ) -> MPlaceTy<'tcx, M::Provenance> { let loc_details = &self.tcx.sess.opts.unstable_opts.location_detail; let file = if loc_details.file { self.allocate_str(filename.as_str(), MemoryKind::CallerLocation, Mutability::Not) @@ -123,7 +123,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) } - pub fn alloc_caller_location_for_span(&mut self, span: Span) -> MPlaceTy<'tcx, M::PointerTag> { + pub fn alloc_caller_location_for_span(&mut self, span: Span) -> MPlaceTy<'tcx, M::Provenance> { let (file, line, column) = self.location_triple_for_span(span); self.alloc_caller_location(file, line, column) } diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 7f8eea94aee..71ccd1799fa 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -85,11 +85,11 @@ pub trait Machine<'mir, 'tcx>: Sized { type MemoryKind: Debug + std::fmt::Display + MayLeak + Eq + 'static; /// Pointers are "tagged" with provenance information; typically the `AllocId` they belong to. - type PointerTag: Provenance + Eq + Hash + 'static; + type Provenance: Provenance + Eq + Hash + 'static; - /// When getting the AllocId of a pointer, some extra data is also obtained from the tag + /// When getting the AllocId of a pointer, some extra data is also obtained from the provenance /// that is passed to memory access hooks so they can do things with it. - type TagExtra: Copy + 'static; + type ProvenanceExtra: Copy + 'static; /// Machines can define extra (non-instance) things that represent values of function pointers. /// For example, Miri uses this to return a function pointer from `dlsym` @@ -105,7 +105,7 @@ pub trait Machine<'mir, 'tcx>: Sized { /// Memory's allocation map type MemoryMap: AllocMap< AllocId, - (MemoryKind<Self::MemoryKind>, Allocation<Self::PointerTag, Self::AllocExtra>), + (MemoryKind<Self::MemoryKind>, Allocation<Self::Provenance, Self::AllocExtra>), > + Default + Clone; @@ -113,7 +113,7 @@ pub trait Machine<'mir, 'tcx>: Sized { /// or None if such memory should not be mutated and thus any such attempt will cause /// a `ModifiedStatic` error to be raised. /// Statics are copied under two circumstances: When they are mutated, and when - /// `tag_allocation` (see below) returns an owned allocation + /// `adjust_allocation` (see below) returns an owned allocation /// that is added to the memory so that the work is not done twice. const GLOBAL_KIND: Option<Self::MemoryKind>; @@ -126,7 +126,7 @@ pub trait Machine<'mir, 'tcx>: Sized { /// Whether, when checking alignment, we should `force_int` and thus support /// custom alignment logic based on whatever the integer address happens to be. /// - /// Requires PointerTag::OFFSET_IS_ADDR to be true. + /// Requires Provenance::OFFSET_IS_ADDR to be true. fn force_int_for_alignment_check(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool; /// Whether to enforce the validity invariant @@ -135,9 +135,6 @@ pub trait Machine<'mir, 'tcx>: Sized { /// 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](CallAbi)-checked. fn enforce_abi(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { true @@ -170,8 +167,8 @@ pub trait Machine<'mir, 'tcx>: Sized { ecx: &mut InterpCx<'mir, 'tcx, Self>, instance: ty::Instance<'tcx>, abi: CallAbi, - args: &[OpTy<'tcx, Self::PointerTag>], - destination: &PlaceTy<'tcx, Self::PointerTag>, + args: &[OpTy<'tcx, Self::Provenance>], + destination: &PlaceTy<'tcx, Self::Provenance>, target: Option<mir::BasicBlock>, unwind: StackPopUnwind, ) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>>; @@ -182,8 +179,8 @@ pub trait Machine<'mir, 'tcx>: Sized { ecx: &mut InterpCx<'mir, 'tcx, Self>, fn_val: Self::ExtraFnVal, abi: CallAbi, - args: &[OpTy<'tcx, Self::PointerTag>], - destination: &PlaceTy<'tcx, Self::PointerTag>, + args: &[OpTy<'tcx, Self::Provenance>], + destination: &PlaceTy<'tcx, Self::Provenance>, target: Option<mir::BasicBlock>, unwind: StackPopUnwind, ) -> InterpResult<'tcx>; @@ -193,8 +190,8 @@ pub trait Machine<'mir, 'tcx>: Sized { fn call_intrinsic( ecx: &mut InterpCx<'mir, 'tcx, Self>, instance: ty::Instance<'tcx>, - args: &[OpTy<'tcx, Self::PointerTag>], - destination: &PlaceTy<'tcx, Self::PointerTag>, + args: &[OpTy<'tcx, Self::Provenance>], + destination: &PlaceTy<'tcx, Self::Provenance>, target: Option<mir::BasicBlock>, unwind: StackPopUnwind, ) -> InterpResult<'tcx>; @@ -217,18 +214,18 @@ pub trait Machine<'mir, 'tcx>: Sized { fn binary_ptr_op( ecx: &InterpCx<'mir, 'tcx, Self>, bin_op: mir::BinOp, - left: &ImmTy<'tcx, Self::PointerTag>, - right: &ImmTy<'tcx, Self::PointerTag>, - ) -> InterpResult<'tcx, (Scalar<Self::PointerTag>, bool, Ty<'tcx>)>; + left: &ImmTy<'tcx, Self::Provenance>, + right: &ImmTy<'tcx, Self::Provenance>, + ) -> InterpResult<'tcx, (Scalar<Self::Provenance>, bool, Ty<'tcx>)>; /// Called to read the specified `local` from the `frame`. /// Since reading a ZST is not actually accessing memory or locals, this is never invoked /// for ZST reads. #[inline] fn access_local<'a>( - frame: &'a Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>, + frame: &'a Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>, local: mir::Local, - ) -> InterpResult<'tcx, &'a Operand<Self::PointerTag>> + ) -> InterpResult<'tcx, &'a Operand<Self::Provenance>> where 'tcx: 'mir, { @@ -243,7 +240,7 @@ pub trait Machine<'mir, 'tcx>: Sized { ecx: &'a mut InterpCx<'mir, 'tcx, Self>, frame: usize, local: mir::Local, - ) -> InterpResult<'tcx, &'a mut Operand<Self::PointerTag>> + ) -> InterpResult<'tcx, &'a mut Operand<Self::Provenance>> where 'tcx: 'mir, { @@ -275,7 +272,7 @@ pub trait Machine<'mir, 'tcx>: Sized { fn thread_local_static_base_pointer( _ecx: &mut InterpCx<'mir, 'tcx, Self>, def_id: DefId, - ) -> InterpResult<'tcx, Pointer<Self::PointerTag>> { + ) -> InterpResult<'tcx, Pointer<Self::Provenance>> { throw_unsup!(ThreadLocalStatic(def_id)) } @@ -283,35 +280,28 @@ pub trait Machine<'mir, 'tcx>: Sized { fn extern_static_base_pointer( ecx: &InterpCx<'mir, 'tcx, Self>, def_id: DefId, - ) -> InterpResult<'tcx, Pointer<Self::PointerTag>>; + ) -> InterpResult<'tcx, Pointer<Self::Provenance>>; /// Return a "base" pointer for the given allocation: the one that is used for direct /// accesses to this static/const/fn allocation, or the one returned from the heap allocator. /// /// Not called on `extern` or thread-local statics (those use the methods above). - fn tag_alloc_base_pointer( + fn adjust_alloc_base_pointer( ecx: &InterpCx<'mir, 'tcx, Self>, ptr: Pointer, - ) -> Pointer<Self::PointerTag>; + ) -> Pointer<Self::Provenance>; /// "Int-to-pointer cast" fn ptr_from_addr_cast( ecx: &InterpCx<'mir, 'tcx, Self>, addr: u64, - ) -> InterpResult<'tcx, Pointer<Option<Self::PointerTag>>>; - - /// Hook for returning a pointer from a transmute-like operation on an addr. - /// This is only needed to support Miri's (unsound) "allow-ptr-int-transmute" flag. - fn ptr_from_addr_transmute( - ecx: &InterpCx<'mir, 'tcx, Self>, - addr: u64, - ) -> Pointer<Option<Self::PointerTag>>; + ) -> InterpResult<'tcx, Pointer<Option<Self::Provenance>>>; /// 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>, + ptr: Pointer<Self::Provenance>, ) -> InterpResult<'tcx>; /// Convert a pointer with provenance into an allocation-offset pair @@ -322,30 +312,30 @@ pub trait Machine<'mir, 'tcx>: Sized { /// 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>, - ) -> 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 - /// to always first construct it without extra and then add the extra. - /// This keeps uniform code paths for handling both allocations created by CTFE - /// for globals, and allocations created by Miri during evaluation. + ptr: Pointer<Self::Provenance>, + ) -> Option<(AllocId, Size, Self::ProvenanceExtra)>; + + /// Called to adjust allocations to the Provenance and AllocExtra of this machine. + /// + /// The way we construct allocations is to always first construct it without extra and then add + /// the extra. This keeps uniform code paths for handling both allocations created by CTFE for + /// globals, and allocations created by Miri during evaluation. /// - /// `kind` is the kind of the allocation being tagged; it can be `None` when + /// `kind` is the kind of the allocation being adjusted; it can be `None` when /// it's a global and `GLOBAL_KIND` is `None`. /// /// This should avoid copying if no work has to be done! If this returns an owned - /// allocation (because a copy had to be done to add tags or metadata), machine memory will + /// allocation (because a copy had to be done to adjust things), machine memory will /// cache the result. (This relies on `AllocMap::get_or` being able to add the /// owned allocation to the map even when the map is shared.) /// /// This must only fail if `alloc` contains relocations. - fn init_allocation_extra<'b>( + fn adjust_allocation<'b>( ecx: &InterpCx<'mir, 'tcx, Self>, id: AllocId, alloc: Cow<'b, Allocation>, kind: Option<MemoryKind<Self::MemoryKind>>, - ) -> InterpResult<'tcx, Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>>; + ) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra>>>; /// Hook for performing extra checks on a memory read access. /// @@ -357,7 +347,7 @@ pub trait Machine<'mir, 'tcx>: Sized { _tcx: TyCtxt<'tcx>, _machine: &Self, _alloc_extra: &Self::AllocExtra, - _tag: (AllocId, Self::TagExtra), + _prov: (AllocId, Self::ProvenanceExtra), _range: AllocRange, ) -> InterpResult<'tcx> { Ok(()) @@ -369,7 +359,7 @@ pub trait Machine<'mir, 'tcx>: Sized { _tcx: TyCtxt<'tcx>, _machine: &mut Self, _alloc_extra: &mut Self::AllocExtra, - _tag: (AllocId, Self::TagExtra), + _prov: (AllocId, Self::ProvenanceExtra), _range: AllocRange, ) -> InterpResult<'tcx> { Ok(()) @@ -381,7 +371,7 @@ pub trait Machine<'mir, 'tcx>: Sized { _tcx: TyCtxt<'tcx>, _machine: &mut Self, _alloc_extra: &mut Self::AllocExtra, - _tag: (AllocId, Self::TagExtra), + _prov: (AllocId, Self::ProvenanceExtra), _range: AllocRange, ) -> InterpResult<'tcx> { Ok(()) @@ -392,7 +382,7 @@ pub trait Machine<'mir, 'tcx>: Sized { fn retag( _ecx: &mut InterpCx<'mir, 'tcx, Self>, _kind: mir::RetagKind, - _place: &PlaceTy<'tcx, Self::PointerTag>, + _place: &PlaceTy<'tcx, Self::Provenance>, ) -> InterpResult<'tcx> { Ok(()) } @@ -400,18 +390,18 @@ pub trait Machine<'mir, 'tcx>: Sized { /// Called immediately before a new stack frame gets pushed. fn init_frame_extra( ecx: &mut InterpCx<'mir, 'tcx, Self>, - frame: Frame<'mir, 'tcx, Self::PointerTag>, - ) -> InterpResult<'tcx, Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>>; + frame: Frame<'mir, 'tcx, Self::Provenance>, + ) -> InterpResult<'tcx, Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>>; /// Borrow the current thread's stack. fn stack<'a>( ecx: &'a InterpCx<'mir, 'tcx, Self>, - ) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>]; + ) -> &'a [Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>]; /// Mutably borrow the current thread's stack. fn stack_mut<'a>( ecx: &'a mut InterpCx<'mir, 'tcx, Self>, - ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>>; + ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>>; /// Called immediately after a stack frame got pushed and its locals got initialized. fn after_stack_push(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { @@ -422,7 +412,7 @@ pub trait Machine<'mir, 'tcx>: Sized { /// The `locals` have already been destroyed! fn after_stack_pop( _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _frame: Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>, + _frame: Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>, unwinding: bool, ) -> InterpResult<'tcx, StackPopJump> { // By default, we do not support unwinding from panics @@ -434,8 +424,8 @@ pub trait Machine<'mir, 'tcx>: Sized { // A lot of the flexibility above is just needed for `Miri`, but all "compile-time" machines // (CTFE and ConstProp) use the same instance. Here, we share that code. pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { - type PointerTag = AllocId; - type TagExtra = (); + type Provenance = AllocId; + type ProvenanceExtra = (); type ExtraFnVal = !; @@ -470,11 +460,6 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { } #[inline(always)] - fn enforce_number_no_provenance(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool { - true - } - - #[inline(always)] fn checked_binop_checks_overflow(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool { true } @@ -485,7 +470,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { fn_val: !, _abi: CallAbi, _args: &[OpTy<$tcx>], - _destination: &PlaceTy<$tcx, Self::PointerTag>, + _destination: &PlaceTy<$tcx, Self::Provenance>, _target: Option<mir::BasicBlock>, _unwind: StackPopUnwind, ) -> InterpResult<$tcx> { @@ -493,13 +478,12 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { } #[inline(always)] - fn init_allocation_extra<'b>( + fn adjust_allocation<'b>( _ecx: &InterpCx<$mir, $tcx, Self>, _id: AllocId, alloc: Cow<'b, Allocation>, _kind: Option<MemoryKind<Self::MemoryKind>>, - ) -> InterpResult<$tcx, Cow<'b, Allocation<Self::PointerTag>>> { - // We do not use a tag so we can just cheaply forward the allocation + ) -> InterpResult<$tcx, Cow<'b, Allocation<Self::Provenance>>> { Ok(alloc) } @@ -512,7 +496,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { } #[inline(always)] - fn tag_alloc_base_pointer( + fn adjust_alloc_base_pointer( _ecx: &InterpCx<$mir, $tcx, Self>, ptr: Pointer<AllocId>, ) -> Pointer<AllocId> { @@ -520,14 +504,6 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { } #[inline(always)] - fn ptr_from_addr_transmute( - _ecx: &InterpCx<$mir, $tcx, Self>, - addr: u64, - ) -> Pointer<Option<AllocId>> { - Pointer::from_addr(addr) - } - - #[inline(always)] fn ptr_from_addr_cast( _ecx: &InterpCx<$mir, $tcx, Self>, addr: u64, @@ -541,7 +517,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { fn ptr_get_alloc( _ecx: &InterpCx<$mir, $tcx, Self>, ptr: Pointer<AllocId>, - ) -> Option<(AllocId, Size, Self::TagExtra)> { + ) -> Option<(AllocId, Size, Self::ProvenanceExtra)> { // We know `offset` is relative to the allocation, so we can use `into_parts`. let (alloc_id, offset) = ptr.into_parts(); 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 509fe576893..ed2c4edf9dd 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -9,14 +9,13 @@ use std::assert_matches::assert_matches; use std::borrow::Cow; use std::collections::VecDeque; -use std::convert::TryFrom; use std::fmt; use std::ptr; use rustc_ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_middle::mir::display_allocation; -use rustc_middle::ty::{Instance, ParamEnv, TyCtxt}; +use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TyCtxt}; use rustc_target::abi::{Align, HasDataLayout, Size}; use super::{ @@ -62,6 +61,8 @@ pub enum AllocKind { LiveData, /// A function allocation (that fn ptrs point to). Function, + /// A (symbolic) vtable allocation. + VTable, /// A dead allocation. Dead, } @@ -112,16 +113,16 @@ pub struct Memory<'mir, 'tcx, M: Machine<'mir, 'tcx>> { /// A reference to some allocation that was already bounds-checked for the given region /// and had the on-access machine hooks run. #[derive(Copy, Clone)] -pub struct AllocRef<'a, 'tcx, Tag, Extra> { - alloc: &'a Allocation<Tag, Extra>, +pub struct AllocRef<'a, 'tcx, Prov, Extra> { + alloc: &'a Allocation<Prov, Extra>, range: AllocRange, tcx: TyCtxt<'tcx>, alloc_id: AllocId, } /// A reference to some allocation that was already bounds-checked for the given region /// and had the on-access machine hooks run. -pub struct AllocRefMut<'a, 'tcx, Tag, Extra> { - alloc: &'a mut Allocation<Tag, Extra>, +pub struct AllocRefMut<'a, 'tcx, Prov, Extra> { + alloc: &'a mut Allocation<Prov, Extra>, range: AllocRange, tcx: TyCtxt<'tcx>, alloc_id: AllocId, @@ -156,10 +157,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn global_base_pointer( &self, ptr: Pointer<AllocId>, - ) -> InterpResult<'tcx, Pointer<M::PointerTag>> { + ) -> InterpResult<'tcx, Pointer<M::Provenance>> { let alloc_id = ptr.provenance; // We need to handle `extern static`. - match self.tcx.get_global_alloc(alloc_id) { + match self.tcx.try_get_global_alloc(alloc_id) { Some(GlobalAlloc::Static(def_id)) if self.tcx.is_thread_local_static(def_id) => { bug!("global memory cannot point to thread-local static") } @@ -168,14 +169,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } _ => {} } - // And we need to get the tag. - Ok(M::tag_alloc_base_pointer(self, ptr)) + // And we need to get the provenance. + Ok(M::adjust_alloc_base_pointer(self, ptr)) } pub fn create_fn_alloc_ptr( &mut self, fn_val: FnVal<'tcx, M::ExtraFnVal>, - ) -> Pointer<M::PointerTag> { + ) -> Pointer<M::Provenance> { let id = match fn_val { FnVal::Instance(instance) => self.tcx.create_fn_alloc(instance), FnVal::Other(extra) => { @@ -196,7 +197,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { size: Size, align: Align, kind: MemoryKind<M::MemoryKind>, - ) -> InterpResult<'tcx, Pointer<M::PointerTag>> { + ) -> InterpResult<'tcx, Pointer<M::Provenance>> { let alloc = Allocation::uninit(size, align, M::PANIC_ON_ALLOC_FAIL)?; // We can `unwrap` since `alloc` contains no pointers. Ok(self.allocate_raw_ptr(alloc, kind).unwrap()) @@ -208,7 +209,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { align: Align, kind: MemoryKind<M::MemoryKind>, mutability: Mutability, - ) -> Pointer<M::PointerTag> { + ) -> Pointer<M::Provenance> { let alloc = Allocation::from_bytes(bytes, align, mutability); // We can `unwrap` since `alloc` contains no pointers. self.allocate_raw_ptr(alloc, kind).unwrap() @@ -219,27 +220,27 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { &mut self, alloc: Allocation, kind: MemoryKind<M::MemoryKind>, - ) -> InterpResult<'tcx, Pointer<M::PointerTag>> { + ) -> InterpResult<'tcx, Pointer<M::Provenance>> { let id = self.tcx.reserve_alloc_id(); debug_assert_ne!( Some(kind), M::GLOBAL_KIND.map(MemoryKind::Machine), "dynamically allocating global memory" ); - let alloc = M::init_allocation_extra(self, id, Cow::Owned(alloc), Some(kind))?; + let alloc = M::adjust_allocation(self, id, Cow::Owned(alloc), Some(kind))?; self.memory.alloc_map.insert(id, (kind, alloc.into_owned())); - Ok(M::tag_alloc_base_pointer(self, Pointer::from(id))) + Ok(M::adjust_alloc_base_pointer(self, Pointer::from(id))) } pub fn reallocate_ptr( &mut self, - ptr: Pointer<Option<M::PointerTag>>, + ptr: Pointer<Option<M::Provenance>>, old_size_and_align: Option<(Size, Align)>, new_size: Size, new_align: Align, kind: MemoryKind<M::MemoryKind>, - ) -> InterpResult<'tcx, Pointer<M::PointerTag>> { - let (alloc_id, offset, _tag) = self.ptr_get_alloc_id(ptr)?; + ) -> InterpResult<'tcx, Pointer<M::Provenance>> { + let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr)?; if offset.bytes() != 0 { throw_ub_format!( "reallocating {:?} which does not point to the beginning of an object", @@ -271,11 +272,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { #[instrument(skip(self), level = "debug")] pub fn deallocate_ptr( &mut self, - ptr: Pointer<Option<M::PointerTag>>, + ptr: Pointer<Option<M::Provenance>>, old_size_and_align: Option<(Size, Align)>, kind: MemoryKind<M::MemoryKind>, ) -> InterpResult<'tcx> { - let (alloc_id, offset, tag) = self.ptr_get_alloc_id(ptr)?; + let (alloc_id, offset, prov) = self.ptr_get_alloc_id(ptr)?; trace!("deallocating: {alloc_id:?}"); if offset.bytes() != 0 { @@ -287,10 +288,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let Some((alloc_kind, mut alloc)) = self.memory.alloc_map.remove(&alloc_id) else { // Deallocating global memory -- always an error - return Err(match self.tcx.get_global_alloc(alloc_id) { + return Err(match self.tcx.try_get_global_alloc(alloc_id) { Some(GlobalAlloc::Function(..)) => { err_ub_format!("deallocating {alloc_id:?}, which is a function") } + Some(GlobalAlloc::VTable(..)) => { + err_ub_format!("deallocating {alloc_id:?}, which is a vtable") + } Some(GlobalAlloc::Static(..) | GlobalAlloc::Memory(..)) => { err_ub_format!("deallocating {alloc_id:?}, which is static memory") } @@ -327,7 +331,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { *self.tcx, &mut self.machine, &mut alloc.extra, - (alloc_id, tag), + (alloc_id, prov), alloc_range(Size::ZERO, size), )?; @@ -344,19 +348,19 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { #[inline(always)] fn get_ptr_access( &self, - ptr: Pointer<Option<M::PointerTag>>, + ptr: Pointer<Option<M::Provenance>>, size: Size, align: Align, - ) -> InterpResult<'tcx, Option<(AllocId, Size, M::TagExtra)>> { + ) -> InterpResult<'tcx, Option<(AllocId, Size, M::ProvenanceExtra)>> { let align = M::enforce_alignment(&self).then_some(align); self.check_and_deref_ptr( ptr, size, align, CheckInAllocMsg::MemoryAccessTest, - |alloc_id, offset, tag| { + |alloc_id, offset, prov| { let (size, align) = self.get_live_alloc_size_and_align(alloc_id)?; - Ok((size, align, (alloc_id, offset, tag))) + Ok((size, align, (alloc_id, offset, prov))) }, ) } @@ -367,7 +371,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { #[inline(always)] pub fn check_ptr_access_align( &self, - ptr: Pointer<Option<M::PointerTag>>, + ptr: Pointer<Option<M::Provenance>>, size: Size, align: Align, msg: CheckInAllocMsg, @@ -385,11 +389,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// is done. Returns `None` for size 0, and otherwise `Some` of what `alloc_size` returned. fn check_and_deref_ptr<T>( &self, - ptr: Pointer<Option<M::PointerTag>>, + ptr: Pointer<Option<M::Provenance>>, size: Size, align: Option<Align>, msg: CheckInAllocMsg, - alloc_size: impl FnOnce(AllocId, Size, M::TagExtra) -> InterpResult<'tcx, (Size, Align, T)>, + alloc_size: impl FnOnce( + AllocId, + Size, + M::ProvenanceExtra, + ) -> InterpResult<'tcx, (Size, Align, T)>, ) -> InterpResult<'tcx, Option<T>> { fn check_offset_align<'tcx>(offset: u64, align: Align) -> InterpResult<'tcx> { if offset % align.bytes() == 0 { @@ -417,8 +425,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } None } - Ok((alloc_id, offset, tag)) => { - let (alloc_size, alloc_align, ret_val) = alloc_size(alloc_id, offset, tag)?; + Ok((alloc_id, offset, prov)) => { + let (alloc_size, alloc_align, ret_val) = alloc_size(alloc_id, offset, prov)?; // Test bounds. This also ensures non-null. // It is sufficient to check this for the end pointer. Also check for overflow! if offset.checked_add(size, &self.tcx).map_or(true, |end| end > alloc_size) { @@ -431,7 +439,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }) } // Ensure we never consider the null pointer dereferencable. - if M::PointerTag::OFFSET_IS_ADDR { + if M::Provenance::OFFSET_IS_ADDR { assert_ne!(ptr.addr(), Size::ZERO); } // Test align. Check this last; if both bounds and alignment are violated @@ -462,19 +470,20 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Helper function to obtain a global (tcx) allocation. /// This attempts to return a reference to an existing allocation if /// one can be found in `tcx`. That, however, is only possible if `tcx` and - /// this machine use the same pointer tag, so it is indirected through - /// `M::tag_allocation`. + /// this machine use the same pointer provenance, so it is indirected through + /// `M::adjust_allocation`. fn get_global_alloc( &self, id: AllocId, is_write: bool, - ) -> InterpResult<'tcx, Cow<'tcx, Allocation<M::PointerTag, M::AllocExtra>>> { - let (alloc, def_id) = match self.tcx.get_global_alloc(id) { + ) -> InterpResult<'tcx, Cow<'tcx, Allocation<M::Provenance, M::AllocExtra>>> { + let (alloc, def_id) = match self.tcx.try_get_global_alloc(id) { Some(GlobalAlloc::Memory(mem)) => { // Memory of a constant or promoted or anonymous memory referenced by a static. (mem, None) } Some(GlobalAlloc::Function(..)) => throw_ub!(DerefFunctionPointer(id)), + Some(GlobalAlloc::VTable(..)) => throw_ub!(DerefVTablePointer(id)), None => throw_ub!(PointerUseAfterFree(id)), Some(GlobalAlloc::Static(def_id)) => { assert!(self.tcx.is_static(def_id)); @@ -490,6 +499,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // contains a reference to memory that was created during its evaluation (i.e., not // to another static), those inner references only exist in "resolved" form. if self.tcx.is_foreign_item(def_id) { + // This is unreachable in Miri, but can happen in CTFE where we actually *do* support + // referencing arbitrary (declared) extern statics. throw_unsup!(ReadExternStatic(def_id)); } @@ -499,7 +510,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; M::before_access_global(*self.tcx, &self.machine, id, alloc, def_id, is_write)?; // We got tcx memory. Let the machine initialize its "extra" stuff. - M::init_allocation_extra( + M::adjust_allocation( self, id, // always use the ID we got as input, not the "hidden" one. Cow::Borrowed(alloc.inner()), @@ -512,11 +523,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { fn get_alloc_raw( &self, id: AllocId, - ) -> InterpResult<'tcx, &Allocation<M::PointerTag, M::AllocExtra>> { + ) -> InterpResult<'tcx, &Allocation<M::Provenance, M::AllocExtra>> { // The error type of the inner closure here is somewhat funny. We have two // ways of "erroring": An actual error, or because we got a reference from // `get_global_alloc` that we can actually use directly without inserting anything anywhere. - // So the error type is `InterpResult<'tcx, &Allocation<M::PointerTag>>`. + // So the error type is `InterpResult<'tcx, &Allocation<M::Provenance>>`. let a = self.memory.alloc_map.get_or(id, || { let alloc = self.get_global_alloc(id, /*is_write*/ false).map_err(Err)?; match alloc { @@ -545,24 +556,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// "Safe" (bounds and align-checked) allocation access. pub fn get_ptr_alloc<'a>( &'a self, - ptr: Pointer<Option<M::PointerTag>>, + ptr: Pointer<Option<M::Provenance>>, size: Size, align: Align, - ) -> InterpResult<'tcx, Option<AllocRef<'a, 'tcx, M::PointerTag, M::AllocExtra>>> { + ) -> InterpResult<'tcx, Option<AllocRef<'a, 'tcx, M::Provenance, M::AllocExtra>>> { let align = M::enforce_alignment(self).then_some(align); let ptr_and_alloc = self.check_and_deref_ptr( ptr, size, align, CheckInAllocMsg::MemoryAccessTest, - |alloc_id, offset, tag| { + |alloc_id, offset, prov| { let alloc = self.get_alloc_raw(alloc_id)?; - Ok((alloc.size(), alloc.align, (alloc_id, offset, tag, alloc))) + Ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc))) }, )?; - if let Some((alloc_id, offset, tag, alloc)) = ptr_and_alloc { + if let Some((alloc_id, offset, prov, alloc)) = ptr_and_alloc { let range = alloc_range(offset, size); - M::memory_read(*self.tcx, &self.machine, &alloc.extra, (alloc_id, tag), range)?; + M::memory_read(*self.tcx, &self.machine, &alloc.extra, (alloc_id, prov), range)?; Ok(Some(AllocRef { alloc, range, tcx: *self.tcx, alloc_id })) } else { // Even in this branch we have to be sure that we actually access the allocation, in @@ -586,7 +597,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { fn get_alloc_raw_mut( &mut self, id: AllocId, - ) -> InterpResult<'tcx, (&mut Allocation<M::PointerTag, M::AllocExtra>, &mut M)> { + ) -> InterpResult<'tcx, (&mut Allocation<M::Provenance, M::AllocExtra>, &mut M)> { // We have "NLL problem case #3" here, which cannot be worked around without loss of // efficiency even for the common case where the key is in the map. // <https://rust-lang.github.io/rfcs/2094-nll.html#problem-case-3-conditional-control-flow-across-functions> @@ -612,18 +623,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// "Safe" (bounds and align-checked) allocation access. pub fn get_ptr_alloc_mut<'a>( &'a mut self, - ptr: Pointer<Option<M::PointerTag>>, + ptr: Pointer<Option<M::Provenance>>, size: Size, align: Align, - ) -> InterpResult<'tcx, Option<AllocRefMut<'a, 'tcx, M::PointerTag, M::AllocExtra>>> { + ) -> InterpResult<'tcx, Option<AllocRefMut<'a, 'tcx, M::Provenance, M::AllocExtra>>> { let parts = self.get_ptr_access(ptr, size, align)?; - if let Some((alloc_id, offset, tag)) = parts { + if let Some((alloc_id, offset, prov)) = parts { let tcx = *self.tcx; // FIXME: can we somehow avoid looking up the allocation twice here? // We cannot call `get_raw_mut` inside `check_and_deref_ptr` as that would duplicate `&mut self`. let (alloc, machine) = self.get_alloc_raw_mut(alloc_id)?; let range = alloc_range(offset, size); - M::memory_written(tcx, machine, &mut alloc.extra, (alloc_id, tag), range)?; + M::memory_written(tcx, machine, &mut alloc.extra, (alloc_id, prov), range)?; Ok(Some(AllocRefMut { alloc, range, tcx, alloc_id })) } else { Ok(None) @@ -659,12 +670,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // # Statics // Can't do this in the match argument, we may get cycle errors since the lock would // be held throughout the match. - match self.tcx.get_global_alloc(id) { - Some(GlobalAlloc::Static(did)) => { - assert!(!self.tcx.is_thread_local_static(did)); + match self.tcx.try_get_global_alloc(id) { + Some(GlobalAlloc::Static(def_id)) => { + assert!(self.tcx.is_static(def_id)); + assert!(!self.tcx.is_thread_local_static(def_id)); // Use size and align of the type. - let ty = self.tcx.type_of(did); + let ty = self.tcx.type_of(def_id); let layout = self.tcx.layout_of(ParamEnv::empty().and(ty)).unwrap(); + assert!(!layout.is_unsized()); (layout.size, layout.align.abi, AllocKind::LiveData) } Some(GlobalAlloc::Memory(alloc)) => { @@ -674,6 +687,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { (alloc.size(), alloc.align, AllocKind::LiveData) } Some(GlobalAlloc::Function(_)) => bug!("We already checked function pointers above"), + Some(GlobalAlloc::VTable(..)) => { + // No data to be accessed here. But vtables are pointer-aligned. + return (Size::ZERO, self.tcx.data_layout.pointer_align.abi, AllocKind::VTable); + } // The rest must be dead. None => { // Deallocated pointers are allowed, we should be able to find @@ -701,7 +718,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if let Some(extra) = self.memory.extra_fn_ptr_map.get(&id) { Some(FnVal::Other(*extra)) } else { - match self.tcx.get_global_alloc(id) { + match self.tcx.try_get_global_alloc(id) { Some(GlobalAlloc::Function(instance)) => Some(FnVal::Instance(instance)), _ => None, } @@ -710,10 +727,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn get_ptr_fn( &self, - ptr: Pointer<Option<M::PointerTag>>, + ptr: Pointer<Option<M::Provenance>>, ) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> { - trace!("get_fn({:?})", ptr); - let (alloc_id, offset, _tag) = self.ptr_get_alloc_id(ptr)?; + trace!("get_ptr_fn({:?})", ptr); + let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr)?; if offset.bytes() != 0 { throw_ub!(InvalidFunctionPointer(Pointer::new(alloc_id, offset))) } @@ -721,6 +738,21 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { .ok_or_else(|| err_ub!(InvalidFunctionPointer(Pointer::new(alloc_id, offset))).into()) } + pub fn get_ptr_vtable( + &self, + ptr: Pointer<Option<M::Provenance>>, + ) -> InterpResult<'tcx, (Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>)> { + trace!("get_ptr_vtable({:?})", ptr); + let (alloc_id, offset, _tag) = self.ptr_get_alloc_id(ptr)?; + if offset.bytes() != 0 { + throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset))) + } + match self.tcx.try_get_global_alloc(alloc_id) { + Some(GlobalAlloc::VTable(ty, trait_ref)) => Ok((ty, trait_ref)), + _ => throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset))), + } + } + pub fn alloc_mark_immutable(&mut self, id: AllocId) -> InterpResult<'tcx> { self.get_alloc_raw_mut(id)?.0.mutability = Mutability::Not; Ok(()) @@ -759,7 +791,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // 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().filter_map(|tag| tag.get_alloc_id()), + alloc.relocations().values().filter_map(|prov| prov.get_alloc_id()), ); } } @@ -788,14 +820,14 @@ pub struct DumpAllocs<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> { impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a, 'mir, 'tcx, M> { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - // Cannot be a closure because it is generic in `Tag`, `Extra`. - fn write_allocation_track_relocs<'tcx, Tag: Provenance, Extra>( + // Cannot be a closure because it is generic in `Prov`, `Extra`. + fn write_allocation_track_relocs<'tcx, Prov: Provenance, Extra>( fmt: &mut std::fmt::Formatter<'_>, tcx: TyCtxt<'tcx>, allocs_to_print: &mut VecDeque<AllocId>, - alloc: &Allocation<Tag, Extra>, + alloc: &Allocation<Prov, Extra>, ) -> std::fmt::Result { - for alloc_id in alloc.relocations().values().filter_map(|tag| tag.get_alloc_id()) { + for alloc_id in alloc.relocations().values().filter_map(|prov| prov.get_alloc_id()) { allocs_to_print.push_back(alloc_id); } write!(fmt, "{}", display_allocation(tcx, alloc)) @@ -825,7 +857,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a, } None => { // global alloc - match self.ecx.tcx.get_global_alloc(id) { + match self.ecx.tcx.try_get_global_alloc(id) { Some(GlobalAlloc::Memory(alloc)) => { write!(fmt, " (unchanged global, ")?; write_allocation_track_relocs( @@ -836,7 +868,13 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a, )?; } Some(GlobalAlloc::Function(func)) => { - write!(fmt, " (fn: {})", func)?; + write!(fmt, " (fn: {func})")?; + } + Some(GlobalAlloc::VTable(ty, Some(trait_ref))) => { + write!(fmt, " (vtable: impl {trait_ref} for {ty})")?; + } + Some(GlobalAlloc::VTable(ty, None)) => { + write!(fmt, " (vtable: impl <auto trait> for {ty})")?; } Some(GlobalAlloc::Static(did)) => { write!(fmt, " (static: {})", self.ecx.tcx.def_path_str(did))?; @@ -854,12 +892,12 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a, } /// Reading and writing. -impl<'tcx, 'a, Tag: Provenance, Extra> AllocRefMut<'a, 'tcx, Tag, Extra> { +impl<'tcx, 'a, Prov: Provenance, Extra> AllocRefMut<'a, 'tcx, Prov, Extra> { /// `range` is relative to this allocation reference, not the base of the allocation. pub fn write_scalar( &mut self, range: AllocRange, - val: ScalarMaybeUninit<Tag>, + val: ScalarMaybeUninit<Prov>, ) -> InterpResult<'tcx> { let range = self.range.subrange(range); debug!("write_scalar at {:?}{range:?}: {val:?}", self.alloc_id); @@ -873,7 +911,7 @@ impl<'tcx, 'a, Tag: Provenance, Extra> AllocRefMut<'a, 'tcx, Tag, Extra> { pub fn write_ptr_sized( &mut self, offset: Size, - val: ScalarMaybeUninit<Tag>, + val: ScalarMaybeUninit<Prov>, ) -> InterpResult<'tcx> { self.write_scalar(alloc_range(offset, self.tcx.data_layout().pointer_size), val) } @@ -887,13 +925,13 @@ impl<'tcx, 'a, Tag: Provenance, Extra> AllocRefMut<'a, 'tcx, Tag, Extra> { } } -impl<'tcx, 'a, Tag: Provenance, Extra> AllocRef<'a, 'tcx, Tag, Extra> { +impl<'tcx, 'a, Prov: Provenance, Extra> AllocRef<'a, 'tcx, Prov, Extra> { /// `range` is relative to this allocation reference, not the base of the allocation. pub fn read_scalar( &self, range: AllocRange, read_provenance: bool, - ) -> InterpResult<'tcx, ScalarMaybeUninit<Tag>> { + ) -> InterpResult<'tcx, ScalarMaybeUninit<Prov>> { let range = self.range.subrange(range); let res = self .alloc @@ -904,12 +942,12 @@ impl<'tcx, 'a, Tag: Provenance, Extra> AllocRef<'a, 'tcx, Tag, Extra> { } /// `range` is relative to this allocation reference, not the base of the allocation. - pub fn read_integer(&self, range: AllocRange) -> InterpResult<'tcx, ScalarMaybeUninit<Tag>> { + pub fn read_integer(&self, range: AllocRange) -> InterpResult<'tcx, ScalarMaybeUninit<Prov>> { self.read_scalar(range, /*read_provenance*/ false) } /// `offset` is relative to this allocation reference, not the base of the allocation. - pub fn read_pointer(&self, offset: Size) -> InterpResult<'tcx, ScalarMaybeUninit<Tag>> { + pub fn read_pointer(&self, offset: Size) -> InterpResult<'tcx, ScalarMaybeUninit<Prov>> { self.read_scalar( alloc_range(offset, self.tcx.data_layout().pointer_size), /*read_provenance*/ true, @@ -941,7 +979,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Performs appropriate bounds checks. pub fn read_bytes_ptr( &self, - ptr: Pointer<Option<M::PointerTag>>, + ptr: Pointer<Option<M::Provenance>>, size: Size, ) -> InterpResult<'tcx, &[u8]> { let Some(alloc_ref) = self.get_ptr_alloc(ptr, size, Align::ONE)? else { @@ -961,7 +999,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Performs appropriate bounds checks. pub fn write_bytes_ptr( &mut self, - ptr: Pointer<Option<M::PointerTag>>, + ptr: Pointer<Option<M::Provenance>>, src: impl IntoIterator<Item = u8>, ) -> InterpResult<'tcx> { let mut src = src.into_iter(); @@ -998,9 +1036,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn mem_copy( &mut self, - src: Pointer<Option<M::PointerTag>>, + src: Pointer<Option<M::Provenance>>, src_align: Align, - dest: Pointer<Option<M::PointerTag>>, + dest: Pointer<Option<M::Provenance>>, dest_align: Align, size: Size, nonoverlapping: bool, @@ -1010,9 +1048,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn mem_copy_repeatedly( &mut self, - src: Pointer<Option<M::PointerTag>>, + src: Pointer<Option<M::Provenance>>, src_align: Align, - dest: Pointer<Option<M::PointerTag>>, + dest: Pointer<Option<M::Provenance>>, dest_align: Align, size: Size, num_copies: u64, @@ -1027,16 +1065,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // and once below to get the underlying `&[mut] Allocation`. // Source alloc preparations and access hooks. - let Some((src_alloc_id, src_offset, src_tag)) = src_parts else { + let Some((src_alloc_id, src_offset, src_prov)) = src_parts else { // Zero-sized *source*, that means dst is also zero-sized and we have nothing to do. return Ok(()); }; let src_alloc = self.get_alloc_raw(src_alloc_id)?; let src_range = alloc_range(src_offset, size); - M::memory_read(*tcx, &self.machine, &src_alloc.extra, (src_alloc_id, src_tag), src_range)?; + M::memory_read(*tcx, &self.machine, &src_alloc.extra, (src_alloc_id, src_prov), src_range)?; // We need the `dest` ptr for the next operation, so we get it now. // We already did the source checks and called the hooks so we are good to return early. - let Some((dest_alloc_id, dest_offset, dest_tag)) = dest_parts else { + let Some((dest_alloc_id, dest_offset, dest_prov)) = dest_parts else { // Zero-sized *destination*. return Ok(()); }; @@ -1062,7 +1100,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { *tcx, extra, &mut dest_alloc.extra, - (dest_alloc_id, dest_tag), + (dest_alloc_id, dest_prov), dest_range, )?; let dest_bytes = dest_alloc @@ -1133,34 +1171,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Machine pointer introspection. impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { - pub fn scalar_to_ptr( - &self, - scalar: Scalar<M::PointerTag>, - ) -> InterpResult<'tcx, Pointer<Option<M::PointerTag>>> { - // We use `to_bits_or_ptr_internal` since we are just implementing the method people need to - // call to force getting out a pointer. - Ok( - match scalar - .to_bits_or_ptr_internal(self.pointer_size()) - .map_err(|s| err_ub!(ScalarSizeMismatch(s)))? - { - Err(ptr) => ptr.into(), - Ok(bits) => { - let addr = u64::try_from(bits).unwrap(); - M::ptr_from_addr_transmute(&self, addr) - } - }, - ) - } - /// Test if this value might be null. /// If the machine does not support ptr-to-int casts, this is conservative. - pub fn scalar_may_be_null(&self, scalar: Scalar<M::PointerTag>) -> InterpResult<'tcx, bool> { + pub fn scalar_may_be_null(&self, scalar: Scalar<M::Provenance>) -> InterpResult<'tcx, bool> { Ok(match scalar.try_to_int() { Ok(int) => int.is_null(), Err(_) => { // Can only happen during CTFE. - let ptr = self.scalar_to_ptr(scalar)?; + let ptr = scalar.to_pointer(self)?; match self.ptr_try_get_alloc_id(ptr) { Ok((alloc_id, offset, _)) => { let (size, _align, _kind) = self.get_alloc_info(alloc_id); @@ -1178,13 +1196,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// about where it points), or an absolute address. pub fn ptr_try_get_alloc_id( &self, - ptr: Pointer<Option<M::PointerTag>>, - ) -> Result<(AllocId, Size, M::TagExtra), u64> { + ptr: Pointer<Option<M::Provenance>>, + ) -> Result<(AllocId, Size, M::ProvenanceExtra), u64> { match ptr.into_pointer_or_addr() { 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); + assert!(M::Provenance::OFFSET_IS_ADDR); let (_, addr) = ptr.into_parts(); Err(addr.bytes()) } @@ -1197,8 +1215,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { #[inline(always)] pub fn ptr_get_alloc_id( &self, - ptr: Pointer<Option<M::PointerTag>>, - ) -> InterpResult<'tcx, (AllocId, Size, M::TagExtra)> { + ptr: Pointer<Option<M::Provenance>>, + ) -> InterpResult<'tcx, (AllocId, Size, M::ProvenanceExtra)> { self.ptr_try_get_alloc_id(ptr).map_err(|offset| { err_ub!(DanglingIntPointer(offset, CheckInAllocMsg::InboundsTest)).into() }) diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index de6eb1c0336..de284bd3bae 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -14,7 +14,7 @@ use rustc_target::abi::{VariantIdx, Variants}; use super::{ alloc_range, from_known_layout, mir_assign_valid_types, AllocId, ConstValue, Frame, GlobalId, InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Place, PlaceTy, Pointer, - PointerArithmetic, Provenance, Scalar, ScalarMaybeUninit, + Provenance, Scalar, ScalarMaybeUninit, }; /// An `Immediate` represents a single immediate self-contained Rust value. @@ -25,14 +25,14 @@ use super::{ /// In particular, thanks to `ScalarPair`, arithmetic operations and casts can be entirely /// defined on `Immediate`, and do not have to work with a `Place`. #[derive(Copy, Clone, Debug)] -pub enum Immediate<Tag: Provenance = AllocId> { +pub enum Immediate<Prov: Provenance = AllocId> { /// A single scalar value (must have *initialized* `Scalar` ABI). /// FIXME: we also currently often use this for ZST. /// `ScalarMaybeUninit` should reject ZST, and we should use `Uninit` for them instead. - Scalar(ScalarMaybeUninit<Tag>), + Scalar(ScalarMaybeUninit<Prov>), /// A pair of two scalar value (must have `ScalarPair` ABI where both fields are /// `Scalar::Initialized`). - ScalarPair(ScalarMaybeUninit<Tag>, ScalarMaybeUninit<Tag>), + ScalarPair(ScalarMaybeUninit<Prov>, ScalarMaybeUninit<Prov>), /// A value of fully uninitialized memory. Can have and size and layout. Uninit, } @@ -40,36 +40,36 @@ pub enum Immediate<Tag: Provenance = AllocId> { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(Immediate, 56); -impl<Tag: Provenance> From<ScalarMaybeUninit<Tag>> for Immediate<Tag> { +impl<Prov: Provenance> From<ScalarMaybeUninit<Prov>> for Immediate<Prov> { #[inline(always)] - fn from(val: ScalarMaybeUninit<Tag>) -> Self { + fn from(val: ScalarMaybeUninit<Prov>) -> Self { Immediate::Scalar(val) } } -impl<Tag: Provenance> From<Scalar<Tag>> for Immediate<Tag> { +impl<Prov: Provenance> From<Scalar<Prov>> for Immediate<Prov> { #[inline(always)] - fn from(val: Scalar<Tag>) -> Self { + fn from(val: Scalar<Prov>) -> Self { Immediate::Scalar(val.into()) } } -impl<'tcx, Tag: Provenance> Immediate<Tag> { - pub fn from_pointer(p: Pointer<Tag>, cx: &impl HasDataLayout) -> Self { +impl<'tcx, Prov: Provenance> Immediate<Prov> { + pub fn from_pointer(p: Pointer<Prov>, cx: &impl HasDataLayout) -> Self { Immediate::Scalar(ScalarMaybeUninit::from_pointer(p, cx)) } - pub fn from_maybe_pointer(p: Pointer<Option<Tag>>, cx: &impl HasDataLayout) -> Self { + pub fn from_maybe_pointer(p: Pointer<Option<Prov>>, cx: &impl HasDataLayout) -> Self { Immediate::Scalar(ScalarMaybeUninit::from_maybe_pointer(p, cx)) } - pub fn new_slice(val: Scalar<Tag>, len: u64, cx: &impl HasDataLayout) -> Self { + pub fn new_slice(val: Scalar<Prov>, len: u64, cx: &impl HasDataLayout) -> Self { Immediate::ScalarPair(val.into(), Scalar::from_machine_usize(len, cx).into()) } pub fn new_dyn_trait( - val: Scalar<Tag>, - vtable: Pointer<Option<Tag>>, + val: Scalar<Prov>, + vtable: Pointer<Option<Prov>>, cx: &impl HasDataLayout, ) -> Self { Immediate::ScalarPair(val.into(), ScalarMaybeUninit::from_maybe_pointer(vtable, cx)) @@ -77,7 +77,7 @@ impl<'tcx, Tag: Provenance> Immediate<Tag> { #[inline] #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) - pub fn to_scalar_or_uninit(self) -> ScalarMaybeUninit<Tag> { + pub fn to_scalar_or_uninit(self) -> ScalarMaybeUninit<Prov> { match self { Immediate::Scalar(val) => val, Immediate::ScalarPair(..) => bug!("Got a scalar pair where a scalar was expected"), @@ -87,13 +87,13 @@ impl<'tcx, Tag: Provenance> Immediate<Tag> { #[inline] #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) - pub fn to_scalar(self) -> InterpResult<'tcx, Scalar<Tag>> { + pub fn to_scalar(self) -> InterpResult<'tcx, Scalar<Prov>> { self.to_scalar_or_uninit().check_init() } #[inline] #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) - pub fn to_scalar_or_uninit_pair(self) -> (ScalarMaybeUninit<Tag>, ScalarMaybeUninit<Tag>) { + pub fn to_scalar_or_uninit_pair(self) -> (ScalarMaybeUninit<Prov>, ScalarMaybeUninit<Prov>) { match self { Immediate::ScalarPair(val1, val2) => (val1, val2), Immediate::Scalar(..) => bug!("Got a scalar where a scalar pair was expected"), @@ -103,7 +103,7 @@ impl<'tcx, Tag: Provenance> Immediate<Tag> { #[inline] #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) - pub fn to_scalar_pair(self) -> InterpResult<'tcx, (Scalar<Tag>, Scalar<Tag>)> { + pub fn to_scalar_pair(self) -> InterpResult<'tcx, (Scalar<Prov>, Scalar<Prov>)> { let (val1, val2) = self.to_scalar_or_uninit_pair(); Ok((val1.check_init()?, val2.check_init()?)) } @@ -112,20 +112,20 @@ impl<'tcx, Tag: Provenance> Immediate<Tag> { // ScalarPair needs a type to interpret, so we often have an immediate and a type together // as input for binary and cast operations. #[derive(Clone, Debug)] -pub struct ImmTy<'tcx, Tag: Provenance = AllocId> { - imm: Immediate<Tag>, +pub struct ImmTy<'tcx, Prov: Provenance = AllocId> { + imm: Immediate<Prov>, pub layout: TyAndLayout<'tcx>, } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(ImmTy<'_>, 72); -impl<Tag: Provenance> std::fmt::Display for ImmTy<'_, Tag> { +impl<Prov: Provenance> std::fmt::Display for ImmTy<'_, Prov> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { /// Helper function for printing a scalar to a FmtPrinter - fn p<'a, 'tcx, Tag: Provenance>( + fn p<'a, 'tcx, Prov: Provenance>( cx: FmtPrinter<'a, 'tcx>, - s: ScalarMaybeUninit<Tag>, + s: ScalarMaybeUninit<Prov>, ty: Ty<'tcx>, ) -> Result<FmtPrinter<'a, 'tcx>, std::fmt::Error> { match s { @@ -170,10 +170,10 @@ impl<Tag: Provenance> std::fmt::Display for ImmTy<'_, Tag> { } } -impl<'tcx, Tag: Provenance> std::ops::Deref for ImmTy<'tcx, Tag> { - type Target = Immediate<Tag>; +impl<'tcx, Prov: Provenance> std::ops::Deref for ImmTy<'tcx, Prov> { + type Target = Immediate<Prov>; #[inline(always)] - fn deref(&self) -> &Immediate<Tag> { + fn deref(&self) -> &Immediate<Prov> { &self.imm } } @@ -182,17 +182,17 @@ impl<'tcx, Tag: Provenance> std::ops::Deref for ImmTy<'tcx, Tag> { /// or still in memory. The latter is an optimization, to delay reading that chunk of /// memory and to avoid having to store arbitrary-sized data here. #[derive(Copy, Clone, Debug)] -pub enum Operand<Tag: Provenance = AllocId> { - Immediate(Immediate<Tag>), - Indirect(MemPlace<Tag>), +pub enum Operand<Prov: Provenance = AllocId> { + Immediate(Immediate<Prov>), + Indirect(MemPlace<Prov>), } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(Operand, 64); #[derive(Clone, Debug)] -pub struct OpTy<'tcx, Tag: Provenance = AllocId> { - op: Operand<Tag>, // Keep this private; it helps enforce invariants. +pub struct OpTy<'tcx, Prov: Provenance = AllocId> { + op: Operand<Prov>, // Keep this private; it helps enforce invariants. pub layout: TyAndLayout<'tcx>, /// rustc does not have a proper way to represent the type of a field of a `repr(packed)` struct: /// it needs to have a different alignment than the field type would usually have. @@ -207,50 +207,50 @@ pub struct OpTy<'tcx, Tag: Provenance = AllocId> { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(OpTy<'_>, 88); -impl<'tcx, Tag: Provenance> std::ops::Deref for OpTy<'tcx, Tag> { - type Target = Operand<Tag>; +impl<'tcx, Prov: Provenance> std::ops::Deref for OpTy<'tcx, Prov> { + type Target = Operand<Prov>; #[inline(always)] - fn deref(&self) -> &Operand<Tag> { + fn deref(&self) -> &Operand<Prov> { &self.op } } -impl<'tcx, Tag: Provenance> From<MPlaceTy<'tcx, Tag>> for OpTy<'tcx, Tag> { +impl<'tcx, Prov: Provenance> From<MPlaceTy<'tcx, Prov>> for OpTy<'tcx, Prov> { #[inline(always)] - fn from(mplace: MPlaceTy<'tcx, Tag>) -> Self { + fn from(mplace: MPlaceTy<'tcx, Prov>) -> Self { OpTy { op: Operand::Indirect(*mplace), layout: mplace.layout, align: Some(mplace.align) } } } -impl<'tcx, Tag: Provenance> From<&'_ MPlaceTy<'tcx, Tag>> for OpTy<'tcx, Tag> { +impl<'tcx, Prov: Provenance> From<&'_ MPlaceTy<'tcx, Prov>> for OpTy<'tcx, Prov> { #[inline(always)] - fn from(mplace: &MPlaceTy<'tcx, Tag>) -> Self { + fn from(mplace: &MPlaceTy<'tcx, Prov>) -> Self { OpTy { op: Operand::Indirect(**mplace), layout: mplace.layout, align: Some(mplace.align) } } } -impl<'tcx, Tag: Provenance> From<&'_ mut MPlaceTy<'tcx, Tag>> for OpTy<'tcx, Tag> { +impl<'tcx, Prov: Provenance> From<&'_ mut MPlaceTy<'tcx, Prov>> for OpTy<'tcx, Prov> { #[inline(always)] - fn from(mplace: &mut MPlaceTy<'tcx, Tag>) -> Self { + fn from(mplace: &mut MPlaceTy<'tcx, Prov>) -> Self { OpTy { op: Operand::Indirect(**mplace), layout: mplace.layout, align: Some(mplace.align) } } } -impl<'tcx, Tag: Provenance> From<ImmTy<'tcx, Tag>> for OpTy<'tcx, Tag> { +impl<'tcx, Prov: Provenance> From<ImmTy<'tcx, Prov>> for OpTy<'tcx, Prov> { #[inline(always)] - fn from(val: ImmTy<'tcx, Tag>) -> Self { + fn from(val: ImmTy<'tcx, Prov>) -> Self { OpTy { op: Operand::Immediate(val.imm), layout: val.layout, align: None } } } -impl<'tcx, Tag: Provenance> ImmTy<'tcx, Tag> { +impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { #[inline] - pub fn from_scalar(val: Scalar<Tag>, layout: TyAndLayout<'tcx>) -> Self { + pub fn from_scalar(val: Scalar<Prov>, layout: TyAndLayout<'tcx>) -> Self { ImmTy { imm: val.into(), layout } } #[inline] - pub fn from_immediate(imm: Immediate<Tag>, layout: TyAndLayout<'tcx>) -> Self { + pub fn from_immediate(imm: Immediate<Prov>, layout: TyAndLayout<'tcx>) -> Self { ImmTy { imm, layout } } @@ -286,7 +286,7 @@ impl<'tcx, Tag: Provenance> ImmTy<'tcx, Tag> { } } -impl<'tcx, Tag: Provenance> OpTy<'tcx, Tag> { +impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> { pub fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> { if self.layout.is_unsized() { // There are no unsized immediates. @@ -302,7 +302,7 @@ impl<'tcx, Tag: Provenance> OpTy<'tcx, Tag> { pub fn offset_with_meta( &self, offset: Size, - meta: MemPlaceMeta<Tag>, + meta: MemPlaceMeta<Prov>, layout: TyAndLayout<'tcx>, cx: &impl HasDataLayout, ) -> InterpResult<'tcx, Self> { @@ -338,9 +338,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// This is an internal function; call `read_immediate` instead. fn read_immediate_from_mplace_raw( &self, - mplace: &MPlaceTy<'tcx, M::PointerTag>, + mplace: &MPlaceTy<'tcx, M::Provenance>, force: bool, - ) -> InterpResult<'tcx, Option<ImmTy<'tcx, M::PointerTag>>> { + ) -> InterpResult<'tcx, Option<ImmTy<'tcx, M::Provenance>>> { if mplace.layout.is_unsized() { // Don't touch unsized return Ok(None); @@ -363,17 +363,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Abi::Scalar(s) if force => Some(s.primitive()), _ => None, }; - let read_provenance = |s: abi::Primitive, size| { - // Should be just `s.is_ptr()`, but we support a Miri flag that accepts more - // questionable ptr-int transmutes. - let number_may_have_provenance = !M::enforce_number_no_provenance(self); - s.is_ptr() || (number_may_have_provenance && size == self.pointer_size()) - }; if let Some(s) = scalar_layout { let size = s.size(self); assert_eq!(size, mplace.layout.size, "abi::Scalar size does not match layout size"); - let scalar = - alloc.read_scalar(alloc_range(Size::ZERO, size), read_provenance(s, size))?; + let scalar = alloc + .read_scalar(alloc_range(Size::ZERO, size), /*read_provenance*/ s.is_ptr())?; return Ok(Some(ImmTy { imm: scalar.into(), layout: mplace.layout })); } let scalar_pair_layout = match mplace.layout.abi { @@ -391,10 +385,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { 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); // in `operand_field` we use the offset to tell apart the fields - let a_val = - alloc.read_scalar(alloc_range(Size::ZERO, a_size), read_provenance(a, a_size))?; - let b_val = - alloc.read_scalar(alloc_range(b_offset, b_size), read_provenance(b, b_size))?; + let a_val = alloc.read_scalar( + alloc_range(Size::ZERO, a_size), + /*read_provenance*/ a.is_ptr(), + )?; + let b_val = alloc + .read_scalar(alloc_range(b_offset, b_size), /*read_provenance*/ b.is_ptr())?; return Ok(Some(ImmTy { imm: Immediate::ScalarPair(a_val, b_val), layout: mplace.layout, @@ -418,9 +414,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// ConstProp needs it, though. pub fn read_immediate_raw( &self, - src: &OpTy<'tcx, M::PointerTag>, + src: &OpTy<'tcx, M::Provenance>, force: bool, - ) -> InterpResult<'tcx, Result<ImmTy<'tcx, M::PointerTag>, MPlaceTy<'tcx, M::PointerTag>>> { + ) -> InterpResult<'tcx, Result<ImmTy<'tcx, M::Provenance>, MPlaceTy<'tcx, M::Provenance>>> { Ok(match src.try_as_mplace() { Ok(ref mplace) => { if let Some(val) = self.read_immediate_from_mplace_raw(mplace, force)? { @@ -437,8 +433,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { #[inline(always)] pub fn read_immediate( &self, - op: &OpTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> { + op: &OpTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { if let Ok(imm) = self.read_immediate_raw(op, /*force*/ false)? { Ok(imm) } else { @@ -449,21 +445,21 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Read a scalar from a place pub fn read_scalar( &self, - op: &OpTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, ScalarMaybeUninit<M::PointerTag>> { + op: &OpTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, ScalarMaybeUninit<M::Provenance>> { Ok(self.read_immediate(op)?.to_scalar_or_uninit()) } /// Read a pointer from a place. pub fn read_pointer( &self, - op: &OpTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, Pointer<Option<M::PointerTag>>> { - self.scalar_to_ptr(self.read_scalar(op)?.check_init()?) + op: &OpTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, Pointer<Option<M::Provenance>>> { + self.read_scalar(op)?.to_pointer(self) } /// Turn the wide MPlace into a string (must already be dereferenced!) - pub fn read_str(&self, mplace: &MPlaceTy<'tcx, M::PointerTag>) -> InterpResult<'tcx, &str> { + pub fn read_str(&self, mplace: &MPlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx, &str> { let len = mplace.len(self)?; let bytes = self.read_bytes_ptr(mplace.ptr, Size::from_bytes(len))?; let str = std::str::from_utf8(bytes).map_err(|err| err_ub!(InvalidStr(err)))?; @@ -476,8 +472,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Can (but does not always) trigger UB if `op` is uninitialized. pub fn operand_to_simd( &self, - op: &OpTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, u64)> { + op: &OpTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::Provenance>, u64)> { // Basically we just transmute this place into an array following simd_size_and_type. // This only works in memory, but repr(simd) types should never be immediates anyway. assert!(op.layout.ty.is_simd()); @@ -501,10 +497,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// OpTy from a local. pub fn local_to_op( &self, - frame: &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>, + frame: &Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>, local: mir::Local, layout: Option<TyAndLayout<'tcx>>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { let layout = self.layout_of_local(frame, local, layout)?; let op = if layout.is_zst() { // Bypass `access_local` (helps in ConstProp) @@ -521,8 +517,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { #[inline(always)] pub fn place_to_op( &self, - place: &PlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + place: &PlaceTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { let op = match **place { Place::Ptr(mplace) => Operand::Indirect(mplace), Place::Local { frame, local } => { @@ -538,7 +534,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { &self, mir_place: mir::Place<'tcx>, layout: Option<TyAndLayout<'tcx>>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { // Do not use the layout passed in as argument if the base we are looking at // here is not the entire place. let layout = if mir_place.projection.is_empty() { layout } else { None }; @@ -575,7 +571,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { &self, mir_op: &mir::Operand<'tcx>, layout: Option<TyAndLayout<'tcx>>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { use rustc_middle::mir::Operand::*; let op = match *mir_op { // FIXME: do some more logic on `move` to invalidate the old location @@ -600,7 +596,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub(super) fn eval_operands( &self, ops: &[mir::Operand<'tcx>], - ) -> InterpResult<'tcx, Vec<OpTy<'tcx, M::PointerTag>>> { + ) -> InterpResult<'tcx, Vec<OpTy<'tcx, M::Provenance>>> { ops.iter().map(|op| self.eval_operand(op, None)).collect() } @@ -612,7 +608,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { &self, c: ty::Const<'tcx>, layout: Option<TyAndLayout<'tcx>>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { match c.kind() { ty::ConstKind::Param(_) | ty::ConstKind::Bound(..) => throw_inval!(TooGeneric), ty::ConstKind::Error(DelaySpanBugEmitted { reported, .. }) => { @@ -637,7 +633,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { &self, val: &mir::ConstantKind<'tcx>, layout: Option<TyAndLayout<'tcx>>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { match val { mir::ConstantKind::Ty(ct) => self.const_to_op(*ct, layout), mir::ConstantKind::Val(val, ty) => self.const_val_to_op(*val, *ty, layout), @@ -649,9 +645,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { val_val: ConstValue<'tcx>, ty: Ty<'tcx>, layout: Option<TyAndLayout<'tcx>>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { // Other cases need layout. - let tag_scalar = |scalar| -> InterpResult<'tcx, _> { + let adjust_scalar = |scalar| -> InterpResult<'tcx, _> { Ok(match scalar { Scalar::Ptr(ptr, size) => Scalar::Ptr(self.global_base_pointer(ptr)?, size), Scalar::Int(int) => Scalar::Int(int), @@ -666,7 +662,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let ptr = self.global_base_pointer(Pointer::new(id, offset))?; Operand::Indirect(MemPlace::from_ptr(ptr.into())) } - ConstValue::Scalar(x) => Operand::Immediate(tag_scalar(x)?.into()), + ConstValue::Scalar(x) => Operand::Immediate(adjust_scalar(x)?.into()), ConstValue::ZeroSized => Operand::Immediate(Immediate::Uninit), ConstValue::Slice { data, start, end } => { // We rely on mutability being set correctly in `data` to prevent writes @@ -689,8 +685,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Can also legally be called on non-enums (e.g. through the discriminant_value intrinsic)! pub fn read_discriminant( &self, - op: &OpTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, (Scalar<M::PointerTag>, VariantIdx)> { + op: &OpTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, (Scalar<M::Provenance>, VariantIdx)> { trace!("read_discriminant_value {:#?}", op.layout); // Get type and layout of the discriminant. let discr_layout = self.layout_of(op.layout.ty.discriminant_ty(*self.tcx))?; diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index 88999e3b47b..f9912d706fb 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -19,9 +19,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { &mut self, op: mir::BinOp, force_overflow_checks: bool, - left: &ImmTy<'tcx, M::PointerTag>, - right: &ImmTy<'tcx, M::PointerTag>, - dest: &PlaceTy<'tcx, M::PointerTag>, + left: &ImmTy<'tcx, M::Provenance>, + right: &ImmTy<'tcx, M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { let (val, overflowed, ty) = self.overflowing_binary_op(op, &left, &right)?; debug_assert_eq!( @@ -58,9 +58,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn binop_ignore_overflow( &mut self, op: mir::BinOp, - left: &ImmTy<'tcx, M::PointerTag>, - right: &ImmTy<'tcx, M::PointerTag>, - dest: &PlaceTy<'tcx, M::PointerTag>, + left: &ImmTy<'tcx, M::Provenance>, + right: &ImmTy<'tcx, M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { let (val, _overflowed, ty) = self.overflowing_binary_op(op, left, right)?; assert_eq!(ty, dest.layout.ty, "type mismatch for result of {:?}", op); @@ -74,7 +74,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { bin_op: mir::BinOp, l: char, r: char, - ) -> (Scalar<M::PointerTag>, bool, Ty<'tcx>) { + ) -> (Scalar<M::Provenance>, bool, Ty<'tcx>) { use rustc_middle::mir::BinOp::*; let res = match bin_op { @@ -94,7 +94,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { bin_op: mir::BinOp, l: bool, r: bool, - ) -> (Scalar<M::PointerTag>, bool, Ty<'tcx>) { + ) -> (Scalar<M::Provenance>, bool, Ty<'tcx>) { use rustc_middle::mir::BinOp::*; let res = match bin_op { @@ -112,13 +112,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { (Scalar::from_bool(res), false, self.tcx.types.bool) } - fn binary_float_op<F: Float + Into<Scalar<M::PointerTag>>>( + fn binary_float_op<F: Float + Into<Scalar<M::Provenance>>>( &self, bin_op: mir::BinOp, ty: Ty<'tcx>, l: F, r: F, - ) -> (Scalar<M::PointerTag>, bool, Ty<'tcx>) { + ) -> (Scalar<M::Provenance>, bool, Ty<'tcx>) { use rustc_middle::mir::BinOp::*; let (val, ty) = match bin_op { @@ -146,7 +146,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { left_layout: TyAndLayout<'tcx>, r: u128, right_layout: TyAndLayout<'tcx>, - ) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool, Ty<'tcx>)> { + ) -> InterpResult<'tcx, (Scalar<M::Provenance>, bool, Ty<'tcx>)> { use rustc_middle::mir::BinOp::*; // Shift ops can have an RHS with a different numeric type. @@ -314,9 +314,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn overflowing_binary_op( &self, bin_op: mir::BinOp, - left: &ImmTy<'tcx, M::PointerTag>, - right: &ImmTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool, Ty<'tcx>)> { + left: &ImmTy<'tcx, M::Provenance>, + right: &ImmTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, (Scalar<M::Provenance>, bool, Ty<'tcx>)> { trace!( "Running binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, @@ -393,9 +393,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn binary_op( &self, bin_op: mir::BinOp, - left: &ImmTy<'tcx, M::PointerTag>, - right: &ImmTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> { + left: &ImmTy<'tcx, M::Provenance>, + right: &ImmTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { let (val, _overflow, ty) = self.overflowing_binary_op(bin_op, left, right)?; Ok(ImmTy::from_scalar(val, self.layout_of(ty)?)) } @@ -405,8 +405,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn overflowing_unary_op( &self, un_op: mir::UnOp, - val: &ImmTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool, Ty<'tcx>)> { + val: &ImmTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, (Scalar<M::Provenance>, bool, Ty<'tcx>)> { use rustc_middle::mir::UnOp::*; let layout = val.layout; @@ -455,8 +455,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn unary_op( &self, un_op: mir::UnOp, - val: &ImmTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> { + val: &ImmTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { let (val, _overflow, ty) = self.overflowing_unary_op(un_op, val)?; Ok(ImmTy::from_scalar(val, self.layout_of(ty)?)) } diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index bc71bfe4327..473da71a0ab 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -18,9 +18,9 @@ use super::{ #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)] /// Information required for the sound usage of a `MemPlace`. -pub enum MemPlaceMeta<Tag: Provenance = AllocId> { +pub enum MemPlaceMeta<Prov: Provenance = AllocId> { /// The unsized payload (e.g. length for slices or vtable pointer for trait objects). - Meta(Scalar<Tag>), + Meta(Scalar<Prov>), /// `Sized` types or unsized `extern type` None, } @@ -28,8 +28,8 @@ pub enum MemPlaceMeta<Tag: Provenance = AllocId> { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(MemPlaceMeta, 24); -impl<Tag: Provenance> MemPlaceMeta<Tag> { - pub fn unwrap_meta(self) -> Scalar<Tag> { +impl<Prov: Provenance> MemPlaceMeta<Prov> { + pub fn unwrap_meta(self) -> Scalar<Prov> { match self { Self::Meta(s) => s, Self::None => { @@ -47,13 +47,13 @@ impl<Tag: Provenance> MemPlaceMeta<Tag> { } #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)] -pub struct MemPlace<Tag: Provenance = AllocId> { - /// The pointer can be a pure integer, with the `None` tag. - pub ptr: Pointer<Option<Tag>>, +pub struct MemPlace<Prov: Provenance = AllocId> { + /// The pointer can be a pure integer, with the `None` provenance. + pub ptr: Pointer<Option<Prov>>, /// Metadata for unsized places. Interpretation is up to the type. /// Must not be present for sized types, but can be missing for unsized types /// (e.g., `extern type`). - pub meta: MemPlaceMeta<Tag>, + pub meta: MemPlaceMeta<Prov>, } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] @@ -61,8 +61,8 @@ rustc_data_structures::static_assert_size!(MemPlace, 40); /// A MemPlace with its layout. Constructing it is only possible in this module. #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] -pub struct MPlaceTy<'tcx, Tag: Provenance = AllocId> { - mplace: MemPlace<Tag>, +pub struct MPlaceTy<'tcx, Prov: Provenance = AllocId> { + mplace: MemPlace<Prov>, pub layout: TyAndLayout<'tcx>, /// rustc does not have a proper way to represent the type of a field of a `repr(packed)` struct: /// it needs to have a different alignment than the field type would usually have. @@ -75,9 +75,9 @@ pub struct MPlaceTy<'tcx, Tag: Provenance = AllocId> { rustc_data_structures::static_assert_size!(MPlaceTy<'_>, 64); #[derive(Copy, Clone, Debug)] -pub enum Place<Tag: Provenance = AllocId> { +pub enum Place<Prov: Provenance = AllocId> { /// A place referring to a value allocated in the `Memory` system. - Ptr(MemPlace<Tag>), + Ptr(MemPlace<Prov>), /// To support alloc-free locals, we are able to write directly to a local. /// (Without that optimization, we'd just always be a `MemPlace`.) @@ -88,8 +88,8 @@ pub enum Place<Tag: Provenance = AllocId> { rustc_data_structures::static_assert_size!(Place, 48); #[derive(Clone, Debug)] -pub struct PlaceTy<'tcx, Tag: Provenance = AllocId> { - place: Place<Tag>, // Keep this private; it helps enforce invariants. +pub struct PlaceTy<'tcx, Prov: Provenance = AllocId> { + place: Place<Prov>, // Keep this private; it helps enforce invariants. pub layout: TyAndLayout<'tcx>, /// rustc does not have a proper way to represent the type of a field of a `repr(packed)` struct: /// it needs to have a different alignment than the field type would usually have. @@ -101,58 +101,58 @@ pub struct PlaceTy<'tcx, Tag: Provenance = AllocId> { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(PlaceTy<'_>, 72); -impl<'tcx, Tag: Provenance> std::ops::Deref for PlaceTy<'tcx, Tag> { - type Target = Place<Tag>; +impl<'tcx, Prov: Provenance> std::ops::Deref for PlaceTy<'tcx, Prov> { + type Target = Place<Prov>; #[inline(always)] - fn deref(&self) -> &Place<Tag> { + fn deref(&self) -> &Place<Prov> { &self.place } } -impl<'tcx, Tag: Provenance> std::ops::Deref for MPlaceTy<'tcx, Tag> { - type Target = MemPlace<Tag>; +impl<'tcx, Prov: Provenance> std::ops::Deref for MPlaceTy<'tcx, Prov> { + type Target = MemPlace<Prov>; #[inline(always)] - fn deref(&self) -> &MemPlace<Tag> { + fn deref(&self) -> &MemPlace<Prov> { &self.mplace } } -impl<'tcx, Tag: Provenance> From<MPlaceTy<'tcx, Tag>> for PlaceTy<'tcx, Tag> { +impl<'tcx, Prov: Provenance> From<MPlaceTy<'tcx, Prov>> for PlaceTy<'tcx, Prov> { #[inline(always)] - fn from(mplace: MPlaceTy<'tcx, Tag>) -> Self { + fn from(mplace: MPlaceTy<'tcx, Prov>) -> Self { PlaceTy { place: Place::Ptr(*mplace), layout: mplace.layout, align: mplace.align } } } -impl<'tcx, Tag: Provenance> From<&'_ MPlaceTy<'tcx, Tag>> for PlaceTy<'tcx, Tag> { +impl<'tcx, Prov: Provenance> From<&'_ MPlaceTy<'tcx, Prov>> for PlaceTy<'tcx, Prov> { #[inline(always)] - fn from(mplace: &MPlaceTy<'tcx, Tag>) -> Self { + fn from(mplace: &MPlaceTy<'tcx, Prov>) -> Self { PlaceTy { place: Place::Ptr(**mplace), layout: mplace.layout, align: mplace.align } } } -impl<'tcx, Tag: Provenance> From<&'_ mut MPlaceTy<'tcx, Tag>> for PlaceTy<'tcx, Tag> { +impl<'tcx, Prov: Provenance> From<&'_ mut MPlaceTy<'tcx, Prov>> for PlaceTy<'tcx, Prov> { #[inline(always)] - fn from(mplace: &mut MPlaceTy<'tcx, Tag>) -> Self { + fn from(mplace: &mut MPlaceTy<'tcx, Prov>) -> Self { PlaceTy { place: Place::Ptr(**mplace), layout: mplace.layout, align: mplace.align } } } -impl<Tag: Provenance> MemPlace<Tag> { +impl<Prov: Provenance> MemPlace<Prov> { #[inline(always)] - pub fn from_ptr(ptr: Pointer<Option<Tag>>) -> Self { + pub fn from_ptr(ptr: Pointer<Option<Prov>>) -> Self { MemPlace { ptr, meta: MemPlaceMeta::None } } /// Adjust the provenance of the main pointer (metadata is unaffected). - pub fn map_provenance(self, f: impl FnOnce(Option<Tag>) -> Option<Tag>) -> Self { + pub fn map_provenance(self, f: impl FnOnce(Option<Prov>) -> Option<Prov>) -> Self { MemPlace { ptr: self.ptr.map_provenance(f), ..self } } /// Turn a mplace into a (thin or wide) pointer, as a reference, pointing to the same space. /// This is the inverse of `ref_to_mplace`. #[inline(always)] - pub fn to_ref(self, cx: &impl HasDataLayout) -> Immediate<Tag> { + pub fn to_ref(self, cx: &impl HasDataLayout) -> Immediate<Prov> { match self.meta { MemPlaceMeta::None => Immediate::from(Scalar::from_maybe_pointer(self.ptr, cx)), MemPlaceMeta::Meta(meta) => { @@ -165,14 +165,14 @@ impl<Tag: Provenance> MemPlace<Tag> { pub fn offset_with_meta<'tcx>( self, offset: Size, - meta: MemPlaceMeta<Tag>, + meta: MemPlaceMeta<Prov>, cx: &impl HasDataLayout, ) -> InterpResult<'tcx, Self> { Ok(MemPlace { ptr: self.ptr.offset(offset, cx)?, meta }) } } -impl<Tag: Provenance> Place<Tag> { +impl<Prov: Provenance> Place<Prov> { /// Asserts that this points to some local variable. /// Returns the frame idx and the variable idx. #[inline] @@ -185,7 +185,7 @@ impl<Tag: Provenance> Place<Tag> { } } -impl<'tcx, Tag: Provenance> MPlaceTy<'tcx, Tag> { +impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> { /// Produces a MemPlace that works for ZST but nothing else. /// Conceptually this is a new allocation, but it doesn't actually create an allocation so you /// don't need to worry about memory leaks. @@ -201,7 +201,7 @@ impl<'tcx, Tag: Provenance> MPlaceTy<'tcx, Tag> { pub fn offset_with_meta( &self, offset: Size, - meta: MemPlaceMeta<Tag>, + meta: MemPlaceMeta<Prov>, layout: TyAndLayout<'tcx>, cx: &impl HasDataLayout, ) -> InterpResult<'tcx, Self> { @@ -223,15 +223,15 @@ impl<'tcx, Tag: Provenance> MPlaceTy<'tcx, Tag> { } #[inline] - pub fn from_aligned_ptr(ptr: Pointer<Option<Tag>>, layout: TyAndLayout<'tcx>) -> Self { + pub fn from_aligned_ptr(ptr: Pointer<Option<Prov>>, layout: TyAndLayout<'tcx>) -> Self { MPlaceTy { mplace: MemPlace::from_ptr(ptr), layout, align: layout.align.abi } } #[inline] pub fn from_aligned_ptr_with_meta( - ptr: Pointer<Option<Tag>>, + ptr: Pointer<Option<Prov>>, layout: TyAndLayout<'tcx>, - meta: MemPlaceMeta<Tag>, + meta: MemPlaceMeta<Prov>, ) -> Self { let mut mplace = MemPlace::from_ptr(ptr); mplace.meta = meta; @@ -258,7 +258,7 @@ impl<'tcx, Tag: Provenance> MPlaceTy<'tcx, Tag> { } #[inline] - pub(super) fn vtable(&self) -> Scalar<Tag> { + pub(super) fn vtable(&self) -> Scalar<Prov> { match self.layout.ty.kind() { ty::Dynamic(..) => self.mplace.meta.unwrap_meta(), _ => bug!("vtable not supported on type {:?}", self.layout.ty), @@ -267,11 +267,11 @@ impl<'tcx, Tag: Provenance> MPlaceTy<'tcx, Tag> { } // These are defined here because they produce a place. -impl<'tcx, Tag: Provenance> OpTy<'tcx, Tag> { +impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> { #[inline(always)] /// Note: do not call `as_ref` on the resulting place. This function should only be used to /// read from the resulting mplace, not to get its address back. - pub fn try_as_mplace(&self) -> Result<MPlaceTy<'tcx, Tag>, ImmTy<'tcx, Tag>> { + pub fn try_as_mplace(&self) -> Result<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>> { match **self { Operand::Indirect(mplace) => { Ok(MPlaceTy { mplace, layout: self.layout, align: self.align.unwrap() }) @@ -284,15 +284,15 @@ impl<'tcx, Tag: Provenance> OpTy<'tcx, Tag> { #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) /// Note: do not call `as_ref` on the resulting place. This function should only be used to /// read from the resulting mplace, not to get its address back. - pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Tag> { + pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Prov> { self.try_as_mplace().unwrap() } } -impl<'tcx, Tag: Provenance> PlaceTy<'tcx, Tag> { +impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> { /// A place is either an mplace or some local. #[inline] - pub fn try_as_mplace(&self) -> Result<MPlaceTy<'tcx, Tag>, (usize, mir::Local)> { + pub fn try_as_mplace(&self) -> Result<MPlaceTy<'tcx, Prov>, (usize, mir::Local)> { match **self { Place::Ptr(mplace) => Ok(MPlaceTy { mplace, layout: self.layout, align: self.align }), Place::Local { frame, local } => Err((frame, local)), @@ -301,16 +301,16 @@ impl<'tcx, Tag: Provenance> PlaceTy<'tcx, Tag> { #[inline(always)] #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) - pub fn assert_mem_place(self) -> MPlaceTy<'tcx, Tag> { + pub fn assert_mem_place(self) -> MPlaceTy<'tcx, Prov> { self.try_as_mplace().unwrap() } } // FIXME: Working around https://github.com/rust-lang/rust/issues/54385 -impl<'mir, 'tcx: 'mir, Tag, M> InterpCx<'mir, 'tcx, M> +impl<'mir, 'tcx: 'mir, Prov, M> InterpCx<'mir, 'tcx, M> where - Tag: Provenance + Eq + Hash + 'static, - M: Machine<'mir, 'tcx, PointerTag = Tag>, + Prov: Provenance + Eq + Hash + 'static, + M: Machine<'mir, 'tcx, Provenance = Prov>, { /// Take a value, which represents a (thin or wide) reference, and make it a place. /// Alignment is just based on the type. This is the inverse of `MemPlace::to_ref()`. @@ -320,8 +320,8 @@ where /// Generally prefer `deref_operand`. pub fn ref_to_mplace( &self, - val: &ImmTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + val: &ImmTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { let pointee_type = val.layout.ty.builtin_deref(true).expect("`ref_to_mplace` called on non-ptr type").ty; let layout = self.layout_of(pointee_type)?; @@ -331,7 +331,7 @@ where Immediate::Uninit => throw_ub!(InvalidUninitBytes(None)), }; - let mplace = MemPlace { ptr: self.scalar_to_ptr(ptr.check_init()?)?, meta }; + let mplace = MemPlace { ptr: ptr.to_pointer(self)?, meta }; // When deref'ing a pointer, the *static* alignment given by the type is what matters. let align = layout.align.abi; Ok(MPlaceTy { mplace, layout, align }) @@ -342,8 +342,8 @@ where #[instrument(skip(self), level = "debug")] pub fn deref_operand( &self, - src: &OpTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + src: &OpTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { let val = self.read_immediate(src)?; trace!("deref to {} on {:?}", val.layout.ty, *val); @@ -359,8 +359,8 @@ where #[inline] pub(super) fn get_place_alloc( &self, - place: &MPlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, Option<AllocRef<'_, 'tcx, M::PointerTag, M::AllocExtra>>> { + place: &MPlaceTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, Option<AllocRef<'_, 'tcx, M::Provenance, M::AllocExtra>>> { assert!(!place.layout.is_unsized()); assert!(!place.meta.has_meta()); let size = place.layout.size; @@ -370,8 +370,8 @@ where #[inline] pub(super) fn get_place_alloc_mut( &mut self, - place: &MPlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, Option<AllocRefMut<'_, 'tcx, M::PointerTag, M::AllocExtra>>> { + place: &MPlaceTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, Option<AllocRefMut<'_, 'tcx, M::Provenance, M::AllocExtra>>> { assert!(!place.layout.is_unsized()); assert!(!place.meta.has_meta()); let size = place.layout.size; @@ -381,7 +381,7 @@ where /// Check if this mplace is dereferenceable and sufficiently aligned. fn check_mplace_access( &self, - mplace: MPlaceTy<'tcx, M::PointerTag>, + mplace: MPlaceTy<'tcx, M::Provenance>, msg: CheckInAllocMsg, ) -> InterpResult<'tcx> { let (size, align) = self @@ -397,8 +397,8 @@ where /// Also returns the number of elements. pub fn mplace_to_simd( &self, - mplace: &MPlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, u64)> { + mplace: &MPlaceTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::Provenance>, u64)> { // Basically we just transmute this place into an array following simd_size_and_type. // (Transmuting is okay since this is an in-memory place. We also double-check the size // stays the same.) @@ -413,8 +413,8 @@ where /// Also returns the number of elements. pub fn place_to_simd( &mut self, - place: &PlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, u64)> { + place: &PlaceTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::Provenance>, u64)> { let mplace = self.force_allocation(place)?; self.mplace_to_simd(&mplace) } @@ -423,7 +423,7 @@ where &self, frame: usize, local: mir::Local, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { let layout = self.layout_of_local(&self.stack()[frame], local, None)?; let place = Place::Local { frame, local }; Ok(PlaceTy { place, layout, align: layout.align.abi }) @@ -435,7 +435,7 @@ where pub fn eval_place( &mut self, mir_place: mir::Place<'tcx>, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { let mut place = self.local_to_place(self.frame_idx(), mir_place.local)?; // Using `try_fold` turned out to be bad for performance, hence the loop. for elem in mir_place.projection.iter() { @@ -465,8 +465,8 @@ where #[instrument(skip(self), level = "debug")] pub fn write_immediate( &mut self, - src: Immediate<M::PointerTag>, - dest: &PlaceTy<'tcx, M::PointerTag>, + src: Immediate<M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { self.write_immediate_no_validate(src, dest)?; @@ -482,8 +482,8 @@ where #[inline(always)] pub fn write_scalar( &mut self, - val: impl Into<ScalarMaybeUninit<M::PointerTag>>, - dest: &PlaceTy<'tcx, M::PointerTag>, + val: impl Into<ScalarMaybeUninit<M::Provenance>>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { self.write_immediate(Immediate::Scalar(val.into()), dest) } @@ -492,8 +492,8 @@ where #[inline(always)] pub fn write_pointer( &mut self, - ptr: impl Into<Pointer<Option<M::PointerTag>>>, - dest: &PlaceTy<'tcx, M::PointerTag>, + ptr: impl Into<Pointer<Option<M::Provenance>>>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { self.write_scalar(Scalar::from_maybe_pointer(ptr.into(), self), dest) } @@ -503,8 +503,8 @@ where /// right type. fn write_immediate_no_validate( &mut self, - src: Immediate<M::PointerTag>, - dest: &PlaceTy<'tcx, M::PointerTag>, + src: Immediate<M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { assert!(!dest.layout.is_unsized(), "Cannot write unsized data"); trace!("write_immediate: {:?} <- {:?}: {}", *dest, src, dest.layout.ty); @@ -537,10 +537,10 @@ where /// right layout. fn write_immediate_to_mplace_no_validate( &mut self, - value: Immediate<M::PointerTag>, + value: Immediate<M::Provenance>, layout: TyAndLayout<'tcx>, align: Align, - dest: MemPlace<M::PointerTag>, + dest: MemPlace<M::Provenance>, ) -> 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 `value` is a `ScalarPair`, we don't do any magic here @@ -589,7 +589,7 @@ where } } - pub fn write_uninit(&mut self, dest: &PlaceTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> { + pub fn write_uninit(&mut self, dest: &PlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> { let mplace = match dest.try_as_mplace() { Ok(mplace) => mplace, Err((frame, local)) => { @@ -619,8 +619,8 @@ where #[instrument(skip(self), level = "debug")] pub fn copy_op( &mut self, - src: &OpTy<'tcx, M::PointerTag>, - dest: &PlaceTy<'tcx, M::PointerTag>, + src: &OpTy<'tcx, M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, allow_transmute: bool, ) -> InterpResult<'tcx> { self.copy_op_no_validate(src, dest, allow_transmute)?; @@ -640,8 +640,8 @@ where #[instrument(skip(self), level = "debug")] fn copy_op_no_validate( &mut self, - src: &OpTy<'tcx, M::PointerTag>, - dest: &PlaceTy<'tcx, M::PointerTag>, + src: &OpTy<'tcx, M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, allow_transmute: bool, ) -> InterpResult<'tcx> { // We do NOT compare the types for equality, because well-typed code can @@ -713,8 +713,8 @@ where #[instrument(skip(self), level = "debug")] pub fn force_allocation( &mut self, - place: &PlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + place: &PlaceTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { let mplace = match place.place { Place::Local { frame, local } => { match M::access_local_mut(self, frame, local)? { @@ -760,7 +760,7 @@ where &mut self, layout: TyAndLayout<'tcx>, kind: MemoryKind<M::MemoryKind>, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { assert!(!layout.is_unsized()); let ptr = self.allocate_ptr(layout.size, layout.align.abi, kind)?; Ok(MPlaceTy::from_aligned_ptr(ptr.into(), layout)) @@ -772,7 +772,7 @@ where str: &str, kind: MemoryKind<M::MemoryKind>, mutbl: Mutability, - ) -> MPlaceTy<'tcx, M::PointerTag> { + ) -> MPlaceTy<'tcx, M::Provenance> { let ptr = self.allocate_bytes_ptr(str.as_bytes(), Align::ONE, kind, mutbl); let meta = Scalar::from_machine_usize(u64::try_from(str.len()).unwrap(), self); let mplace = MemPlace { ptr: ptr.into(), meta: MemPlaceMeta::Meta(meta) }; @@ -790,7 +790,7 @@ where pub fn write_discriminant( &mut self, variant_index: VariantIdx, - dest: &PlaceTy<'tcx, M::PointerTag>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { // This must be an enum or generator. match dest.layout.ty.kind() { @@ -876,7 +876,7 @@ where pub fn raw_const_to_mplace( &self, raw: ConstAlloc<'tcx>, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { // This must be an allocation in `tcx` let _ = self.tcx.global_alloc(raw.alloc_id); let ptr = self.global_base_pointer(Pointer::from(raw.alloc_id))?; @@ -885,28 +885,19 @@ where } /// Turn a place with a `dyn Trait` type into a place with the actual dynamic type. - /// Also return some more information so drop doesn't have to run the same code twice. pub(super) fn unpack_dyn_trait( &self, - mplace: &MPlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, (ty::Instance<'tcx>, MPlaceTy<'tcx, M::PointerTag>)> { - let vtable = self.scalar_to_ptr(mplace.vtable())?; // also sanity checks the type - let (instance, ty) = self.read_drop_type_from_vtable(vtable)?; + mplace: &MPlaceTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { + let vtable = mplace.vtable().to_pointer(self)?; // also sanity checks the type + let (ty, _) = self.get_ptr_vtable(vtable)?; let layout = self.layout_of(ty)?; - // More sanity checks - if cfg!(debug_assertions) { - let (size, align) = self.read_size_and_align_from_vtable(vtable)?; - assert_eq!(size, layout.size); - // only ABI alignment is preserved - assert_eq!(align, layout.align.abi); - } - let mplace = MPlaceTy { mplace: MemPlace { meta: MemPlaceMeta::None, ..**mplace }, layout, align: layout.align.abi, }; - Ok((instance, mplace)) + Ok(mplace) } } diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index 4e69d71dc00..742339f2b0a 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -20,10 +20,10 @@ use super::{ }; // FIXME: Working around https://github.com/rust-lang/rust/issues/54385 -impl<'mir, 'tcx: 'mir, Tag, M> InterpCx<'mir, 'tcx, M> +impl<'mir, 'tcx: 'mir, Prov, M> InterpCx<'mir, 'tcx, M> where - Tag: Provenance + Eq + Hash + 'static, - M: Machine<'mir, 'tcx, PointerTag = Tag>, + Prov: Provenance + Eq + Hash + 'static, + M: Machine<'mir, 'tcx, Provenance = Prov>, { //# Field access @@ -35,9 +35,9 @@ where /// For indexing into arrays, use `mplace_index`. pub fn mplace_field( &self, - base: &MPlaceTy<'tcx, M::PointerTag>, + base: &MPlaceTy<'tcx, M::Provenance>, field: usize, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { let offset = base.layout.fields.offset(field); let field_layout = base.layout.field(self, field); @@ -72,9 +72,9 @@ where /// into the field of a local `ScalarPair`, we have to first allocate it. pub fn place_field( &mut self, - base: &PlaceTy<'tcx, M::PointerTag>, + base: &PlaceTy<'tcx, M::Provenance>, field: usize, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { // FIXME: We could try to be smarter and avoid allocation for fields that span the // entire place. let base = self.force_allocation(base)?; @@ -83,9 +83,9 @@ where pub fn operand_field( &self, - base: &OpTy<'tcx, M::PointerTag>, + base: &OpTy<'tcx, M::Provenance>, field: usize, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { let base = match base.try_as_mplace() { Ok(ref mplace) => { // We can reuse the mplace field computation logic for indirect operands. @@ -139,9 +139,9 @@ where pub fn mplace_downcast( &self, - base: &MPlaceTy<'tcx, M::PointerTag>, + base: &MPlaceTy<'tcx, M::Provenance>, variant: VariantIdx, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { // Downcasts only change the layout. // (In particular, no check about whether this is even the active variant -- that's by design, // see https://github.com/rust-lang/rust/issues/93688#issuecomment-1032929496.) @@ -153,9 +153,9 @@ where pub fn place_downcast( &self, - base: &PlaceTy<'tcx, M::PointerTag>, + base: &PlaceTy<'tcx, M::Provenance>, variant: VariantIdx, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { // Downcast just changes the layout let mut base = base.clone(); base.layout = base.layout.for_variant(self, variant); @@ -164,9 +164,9 @@ where pub fn operand_downcast( &self, - base: &OpTy<'tcx, M::PointerTag>, + base: &OpTy<'tcx, M::Provenance>, variant: VariantIdx, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { // Downcast just changes the layout let mut base = base.clone(); base.layout = base.layout.for_variant(self, variant); @@ -178,9 +178,9 @@ where #[inline(always)] pub fn operand_index( &self, - base: &OpTy<'tcx, M::PointerTag>, + base: &OpTy<'tcx, M::Provenance>, index: u64, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { // Not using the layout method because we want to compute on u64 match base.layout.fields { abi::FieldsShape::Array { stride, count: _ } => { @@ -207,8 +207,8 @@ where // same by repeatedly calling `operand_index`. pub fn operand_array_fields<'a>( &self, - base: &'a OpTy<'tcx, Tag>, - ) -> InterpResult<'tcx, impl Iterator<Item = InterpResult<'tcx, OpTy<'tcx, Tag>>> + 'a> { + base: &'a OpTy<'tcx, Prov>, + ) -> InterpResult<'tcx, impl Iterator<Item = InterpResult<'tcx, OpTy<'tcx, Prov>>> + 'a> { let len = base.len(self)?; // also asserts that we have a type where this makes sense let abi::FieldsShape::Array { stride, .. } = base.layout.fields else { span_bug!(self.cur_span(), "operand_array_fields: expected an array layout"); @@ -222,17 +222,17 @@ where /// Index into an array. pub fn mplace_index( &self, - base: &MPlaceTy<'tcx, M::PointerTag>, + base: &MPlaceTy<'tcx, M::Provenance>, index: u64, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { Ok(self.operand_index(&base.into(), index)?.assert_mem_place()) } pub fn place_index( &mut self, - base: &PlaceTy<'tcx, M::PointerTag>, + base: &PlaceTy<'tcx, M::Provenance>, index: u64, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { // There's not a lot we can do here, since we cannot have a place to a part of a local. If // we are accessing the only element of a 1-element array, it's still the entire local... // that doesn't seem worth it. @@ -244,11 +244,11 @@ where fn operand_constant_index( &self, - base: &OpTy<'tcx, M::PointerTag>, + base: &OpTy<'tcx, M::Provenance>, offset: u64, min_length: u64, from_end: bool, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { let n = base.len(self)?; if n < min_length { // This can only be reached in ConstProp and non-rustc-MIR. @@ -268,11 +268,11 @@ where fn place_constant_index( &mut self, - base: &PlaceTy<'tcx, M::PointerTag>, + base: &PlaceTy<'tcx, M::Provenance>, offset: u64, min_length: u64, from_end: bool, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { let base = self.force_allocation(base)?; Ok(self .operand_constant_index(&base.into(), offset, min_length, from_end)? @@ -284,11 +284,11 @@ where fn operand_subslice( &self, - base: &OpTy<'tcx, M::PointerTag>, + base: &OpTy<'tcx, M::Provenance>, from: u64, to: u64, from_end: bool, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { let len = base.len(self)?; // also asserts that we have a type where this makes sense let actual_to = if from_end { if from.checked_add(to).map_or(true, |to| to > len) { @@ -329,11 +329,11 @@ where pub fn place_subslice( &mut self, - base: &PlaceTy<'tcx, M::PointerTag>, + base: &PlaceTy<'tcx, M::Provenance>, from: u64, to: u64, from_end: bool, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { let base = self.force_allocation(base)?; Ok(self.operand_subslice(&base.into(), from, to, from_end)?.assert_mem_place().into()) } @@ -344,16 +344,11 @@ where #[instrument(skip(self), level = "trace")] pub fn place_projection( &mut self, - base: &PlaceTy<'tcx, M::PointerTag>, + base: &PlaceTy<'tcx, M::Provenance>, proj_elem: mir::PlaceElem<'tcx>, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { use rustc_middle::mir::ProjectionElem::*; Ok(match proj_elem { - OpaqueCast(ty) => { - let mut place = base.clone(); - place.layout = self.layout_of(ty)?; - place - } Field(field, _) => self.place_field(base, field.index())?, Downcast(_, variant) => self.place_downcast(base, variant)?, Deref => self.deref_operand(&self.place_to_op(base)?)?.into(), @@ -373,16 +368,11 @@ where #[instrument(skip(self), level = "trace")] pub fn operand_projection( &self, - base: &OpTy<'tcx, M::PointerTag>, + base: &OpTy<'tcx, M::Provenance>, proj_elem: mir::PlaceElem<'tcx>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { use rustc_middle::mir::ProjectionElem::*; Ok(match proj_elem { - OpaqueCast(ty) => { - let mut op = base.clone(); - op.layout = self.layout_of(ty)?; - op - } Field(field, _) => self.operand_field(base, field.index())?, Downcast(_, variant) => self.operand_downcast(base, variant)?, Deref => self.deref_operand(base)?.into(), diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index d0c9b5319dd..d563e35f910 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -1,5 +1,4 @@ use std::borrow::Cow; -use std::convert::TryFrom; use rustc_middle::ty::layout::{FnAbiOf, LayoutOf}; use rustc_middle::ty::Instance; @@ -267,10 +266,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { fn pass_argument<'x, 'y>( &mut self, caller_args: &mut impl Iterator< - Item = (&'x OpTy<'tcx, M::PointerTag>, &'y ArgAbi<'tcx, Ty<'tcx>>), + Item = (&'x OpTy<'tcx, M::Provenance>, &'y ArgAbi<'tcx, Ty<'tcx>>), >, callee_abi: &ArgAbi<'tcx, Ty<'tcx>>, - callee_arg: &PlaceTy<'tcx, M::PointerTag>, + callee_arg: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> where 'tcx: 'x, @@ -336,9 +335,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { &mut self, fn_val: FnVal<'tcx, M::ExtraFnVal>, (caller_abi, caller_fn_abi): (Abi, &FnAbi<'tcx, Ty<'tcx>>), - args: &[OpTy<'tcx, M::PointerTag>], + args: &[OpTy<'tcx, M::Provenance>], with_caller_location: bool, - destination: &PlaceTy<'tcx, M::PointerTag>, + destination: &PlaceTy<'tcx, M::Provenance>, target: Option<mir::BasicBlock>, mut unwind: StackPopUnwind, ) -> InterpResult<'tcx> { @@ -365,7 +364,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // caller_fn_abi is not relevant here, we interpret the arguments directly for each intrinsic. M::call_intrinsic(self, instance, args, destination, target, unwind) } - ty::InstanceDef::VtableShim(..) + ty::InstanceDef::VTableShim(..) | ty::InstanceDef::ReifyShim(..) | ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::FnPtrShim(..) @@ -437,7 +436,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // last incoming argument. These two iterators do not have the same type, // so to keep the code paths uniform we accept an allocation // (for RustCall ABI only). - let caller_args: Cow<'_, [OpTy<'tcx, M::PointerTag>]> = + let caller_args: Cow<'_, [OpTy<'tcx, M::Provenance>]> = if caller_abi == Abi::RustCall && !args.is_empty() { // Untuple let (untuple_arg, args) = args.split_last().unwrap(); @@ -449,7 +448,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { (0..untuple_arg.layout.fields.count()) .map(|i| self.operand_field(untuple_arg, i)), ) - .collect::<InterpResult<'_, Vec<OpTy<'tcx, M::PointerTag>>>>( + .collect::<InterpResult<'_, Vec<OpTy<'tcx, M::Provenance>>>>( )?, ) } else { @@ -520,7 +519,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } // cannot use the shim here, because that will only result in infinite recursion - ty::InstanceDef::Virtual(_, idx) => { + ty::InstanceDef::Virtual(def_id, idx) => { let mut args = args.to_vec(); // We have to implement all "object safe receivers". So we have to go search for a // pointer or `dyn Trait` type, but it could be wrapped in newtypes. So recursively @@ -553,17 +552,52 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } }; - // Find and consult vtable. The type now could be something like RcBox<dyn Trait>, - // i.e., it is still not necessarily `ty::Dynamic` (so we cannot use - // `place.vtable()`), but it should have a `dyn Trait` tail. - assert!(matches!( - self.tcx - .struct_tail_erasing_lifetimes(receiver_place.layout.ty, self.param_env) - .kind(), - ty::Dynamic(..) - )); - let vtable = self.scalar_to_ptr(receiver_place.meta.unwrap_meta())?; - let fn_val = self.get_vtable_slot(vtable, u64::try_from(idx).unwrap())?; + // Obtain the underlying trait we are working on. + let receiver_tail = self + .tcx + .struct_tail_erasing_lifetimes(receiver_place.layout.ty, self.param_env); + let ty::Dynamic(data, ..) = receiver_tail.kind() else { + span_bug!(self.cur_span(), "dyanmic call on non-`dyn` type {}", receiver_tail) + }; + + // Get the required information from the vtable. + let vptr = receiver_place.meta.unwrap_meta().to_pointer(self)?; + let (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?; + if dyn_trait != data.principal() { + throw_ub_format!( + "`dyn` call on a pointer whose vtable does not match its type" + ); + } + + // Now determine the actual method to call. We can do that in two different ways and + // compare them to ensure everything fits. + let Some(ty::VtblEntry::Method(fn_inst)) = self.get_vtable_entries(vptr)?.get(idx).copied() else { + throw_ub_format!("`dyn` call trying to call something that is not a method") + }; + if cfg!(debug_assertions) { + let tcx = *self.tcx; + + let trait_def_id = tcx.trait_of_item(def_id).unwrap(); + let virtual_trait_ref = + ty::TraitRef::from_method(tcx, trait_def_id, instance.substs); + assert_eq!( + receiver_tail, + virtual_trait_ref.self_ty(), + "mismatch in underlying dyn trait computation within Miri and MIR building", + ); + let existential_trait_ref = + ty::ExistentialTraitRef::erase_self_ty(tcx, virtual_trait_ref); + let concrete_trait_ref = existential_trait_ref.with_self_ty(tcx, dyn_ty); + + let concrete_method = Instance::resolve_for_vtable( + tcx, + self.param_env, + def_id, + instance.substs.rebase_onto(tcx, trait_def_id, concrete_trait_ref.substs), + ) + .unwrap(); + assert_eq!(fn_inst, concrete_method); + } // `*mut receiver_place.layout.ty` is almost the layout that we // want for args[0]: We have to project to field 0 because we want @@ -579,7 +613,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { trace!("Patched receiver operand to {:#?}", args[0]); // recurse with concrete function self.eval_fn_call( - fn_val, + FnVal::Instance(fn_inst), (caller_abi, caller_fn_abi), &args, with_caller_location, @@ -593,7 +627,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { fn drop_in_place( &mut self, - place: &PlaceTy<'tcx, M::PointerTag>, + place: &PlaceTy<'tcx, M::Provenance>, instance: ty::Instance<'tcx>, target: mir::BasicBlock, unwind: Option<mir::BasicBlock>, @@ -606,8 +640,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let (instance, place) = match place.layout.ty.kind() { ty::Dynamic(..) => { - // Dropping a trait object. - self.unpack_dyn_trait(&place)? + // Dropping a trait object. Need to find actual drop fn. + let place = self.unpack_dyn_trait(&place)?; + let instance = ty::Instance::resolve_drop_in_place(*self.tcx, place.layout.ty); + (instance, place) } _ => (instance, place), }; diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs index 22c23df7b1a..b3a511d5a49 100644 --- a/compiler/rustc_const_eval/src/interpret/traits.rs +++ b/compiler/rustc_const_eval/src/interpret/traits.rs @@ -1,27 +1,24 @@ -use std::convert::TryFrom; - -use rustc_middle::mir::interpret::{alloc_range, InterpResult, Pointer, PointerArithmetic}; -use rustc_middle::ty::{ - self, Ty, TyCtxt, COMMON_VTABLE_ENTRIES_ALIGN, COMMON_VTABLE_ENTRIES_DROPINPLACE, - COMMON_VTABLE_ENTRIES_SIZE, -}; +use rustc_middle::mir::interpret::{InterpResult, Pointer}; +use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_target::abi::{Align, Size}; use super::util::ensure_monomorphic_enough; -use super::{FnVal, InterpCx, Machine}; +use super::{InterpCx, Machine}; impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Creates a dynamic vtable for the given type and vtable origin. This is used only for /// objects. /// - /// The `trait_ref` encodes the erased self type. Hence, if we are - /// making an object `Foo<Trait>` from a value of type `Foo<T>`, then - /// `trait_ref` would map `T: Trait`. - pub fn get_vtable( - &mut self, + /// The `trait_ref` encodes the erased self type. Hence, if we are making an object `Foo<Trait>` + /// from a value of type `Foo<T>`, then `trait_ref` would map `T: Trait`. `None` here means that + /// this is an auto trait without any methods, so we only need the basic vtable (drop, size, + /// align). + pub fn get_vtable_ptr( + &self, ty: Ty<'tcx>, poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>, - ) -> InterpResult<'tcx, Pointer<Option<M::PointerTag>>> { + ) -> InterpResult<'tcx, Pointer<Option<M::Provenance>>> { trace!("get_vtable(trait_ref={:?})", poly_trait_ref); let (ty, poly_trait_ref) = self.tcx.erase_regions((ty, poly_trait_ref)); @@ -30,114 +27,33 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ensure_monomorphic_enough(*self.tcx, ty)?; ensure_monomorphic_enough(*self.tcx, poly_trait_ref)?; - let vtable_allocation = self.tcx.vtable_allocation((ty, poly_trait_ref)); - - let vtable_ptr = self.global_base_pointer(Pointer::from(vtable_allocation))?; - + let vtable_symbolic_allocation = self.tcx.create_vtable_alloc(ty, poly_trait_ref); + let vtable_ptr = self.global_base_pointer(Pointer::from(vtable_symbolic_allocation))?; Ok(vtable_ptr.into()) } - /// Resolves the function at the specified slot in the provided - /// vtable. Currently an index of '3' (`TyCtxt::COMMON_VTABLE_ENTRIES.len()`) - /// corresponds to the first method declared in the trait of the provided vtable. - pub fn get_vtable_slot( - &self, - vtable: Pointer<Option<M::PointerTag>>, - idx: u64, - ) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> { - let ptr_size = self.pointer_size(); - let vtable_slot = vtable.offset(ptr_size * idx, self)?; - let vtable_slot = self - .get_ptr_alloc(vtable_slot, ptr_size, self.tcx.data_layout.pointer_align.abi)? - .expect("cannot be a ZST"); - let fn_ptr = self.scalar_to_ptr(vtable_slot.read_pointer(Size::ZERO)?.check_init()?)?; - self.get_ptr_fn(fn_ptr) - } - - /// Returns the drop fn instance as well as the actual dynamic type. - pub fn read_drop_type_from_vtable( + /// Returns a high-level representation of the entires of the given vtable. + pub fn get_vtable_entries( &self, - vtable: Pointer<Option<M::PointerTag>>, - ) -> InterpResult<'tcx, (ty::Instance<'tcx>, Ty<'tcx>)> { - let pointer_size = self.pointer_size(); - // We don't care about the pointee type; we just want a pointer. - let vtable = self - .get_ptr_alloc( - vtable, - pointer_size * u64::try_from(TyCtxt::COMMON_VTABLE_ENTRIES.len()).unwrap(), - self.tcx.data_layout.pointer_align.abi, - )? - .expect("cannot be a ZST"); - let drop_fn = vtable - .read_pointer(pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES_DROPINPLACE).unwrap())? - .check_init()?; - // We *need* an instance here, no other kind of function value, to be able - // to determine the type. - let drop_instance = self.get_ptr_fn(self.scalar_to_ptr(drop_fn)?)?.as_instance()?; - trace!("Found drop fn: {:?}", drop_instance); - let fn_sig = drop_instance.ty(*self.tcx, self.param_env).fn_sig(*self.tcx); - let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, fn_sig); - // The drop function takes `*mut T` where `T` is the type being dropped, so get that. - let args = fn_sig.inputs(); - if args.len() != 1 { - throw_ub!(InvalidVtableDropFn(fn_sig)); - } - let ty = - args[0].builtin_deref(true).ok_or_else(|| err_ub!(InvalidVtableDropFn(fn_sig)))?.ty; - Ok((drop_instance, ty)) + vtable: Pointer<Option<M::Provenance>>, + ) -> InterpResult<'tcx, &'tcx [ty::VtblEntry<'tcx>]> { + let (ty, poly_trait_ref) = self.get_ptr_vtable(vtable)?; + Ok(if let Some(poly_trait_ref) = poly_trait_ref { + let trait_ref = poly_trait_ref.with_self_ty(*self.tcx, ty); + let trait_ref = self.tcx.erase_regions(trait_ref); + self.tcx.vtable_entries(trait_ref) + } else { + TyCtxt::COMMON_VTABLE_ENTRIES + }) } - pub fn read_size_and_align_from_vtable( + pub fn get_vtable_size_and_align( &self, - vtable: Pointer<Option<M::PointerTag>>, + vtable: Pointer<Option<M::Provenance>>, ) -> InterpResult<'tcx, (Size, Align)> { - let pointer_size = self.pointer_size(); - // We check for `size = 3 * ptr_size`, which covers the drop fn (unused here), - // the size, and the align (which we read below). - let vtable = self - .get_ptr_alloc( - vtable, - pointer_size * u64::try_from(TyCtxt::COMMON_VTABLE_ENTRIES.len()).unwrap(), - self.tcx.data_layout.pointer_align.abi, - )? - .expect("cannot be a ZST"); - let size = vtable - .read_integer(alloc_range( - pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES_SIZE).unwrap(), - pointer_size, - ))? - .check_init()?; - let size = size.to_machine_usize(self)?; - let size = Size::from_bytes(size); - let align = vtable - .read_integer(alloc_range( - pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES_ALIGN).unwrap(), - pointer_size, - ))? - .check_init()?; - let align = align.to_machine_usize(self)?; - let align = Align::from_bytes(align).map_err(|e| err_ub!(InvalidVtableAlignment(e)))?; - - if size > self.max_size_of_val() { - throw_ub!(InvalidVtableSize); - } - Ok((size, align)) - } - - pub fn read_new_vtable_after_trait_upcasting_from_vtable( - &self, - vtable: Pointer<Option<M::PointerTag>>, - idx: u64, - ) -> InterpResult<'tcx, Pointer<Option<M::PointerTag>>> { - let pointer_size = self.pointer_size(); - - let vtable_slot = vtable.offset(pointer_size * idx, self)?; - let new_vtable = self - .get_ptr_alloc(vtable_slot, pointer_size, self.tcx.data_layout.pointer_align.abi)? - .expect("cannot be a ZST"); - - let new_vtable = self.scalar_to_ptr(new_vtable.read_pointer(Size::ZERO)?.check_init()?)?; - - Ok(new_vtable) + let (ty, _trait_ref) = self.get_ptr_vtable(vtable)?; + let layout = self.layout_of(ty)?; + assert!(!layout.is_unsized(), "there are no vtables for unsized types"); + Ok((layout.size, layout.align.abi)) } } diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 2e5492ecf56..d20f16755c3 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -206,7 +206,7 @@ struct ValidityVisitor<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> { /// starts must not be changed! `visit_fields` and `visit_array` rely on /// this stack discipline. path: Vec<PathElem>, - ref_tracking: Option<&'rt mut RefTracking<MPlaceTy<'tcx, M::PointerTag>, Vec<PathElem>>>, + ref_tracking: Option<&'rt mut RefTracking<MPlaceTy<'tcx, M::Provenance>, Vec<PathElem>>>, /// `None` indicates this is not validating for CTFE (but for runtime). ctfe_mode: Option<CtfeValidationMode>, ecx: &'rt InterpCx<'mir, 'tcx, M>, @@ -306,57 +306,22 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' fn check_wide_ptr_meta( &mut self, - meta: MemPlaceMeta<M::PointerTag>, + meta: MemPlaceMeta<M::Provenance>, pointee: TyAndLayout<'tcx>, ) -> InterpResult<'tcx> { let tail = self.ecx.tcx.struct_tail_erasing_lifetimes(pointee.ty, self.ecx.param_env); match tail.kind() { ty::Dynamic(..) => { - let vtable = self.ecx.scalar_to_ptr(meta.unwrap_meta())?; - // Direct call to `check_ptr_access_align` checks alignment even on CTFE machines. - try_validation!( - self.ecx.check_ptr_access_align( - vtable, - 3 * self.ecx.tcx.data_layout.pointer_size, // drop, size, align - self.ecx.tcx.data_layout.pointer_align.abi, - CheckInAllocMsg::InboundsTest, // will anyway be replaced by validity message - ), + let vtable = meta.unwrap_meta().to_pointer(self.ecx)?; + // Make sure it is a genuine vtable pointer. + let (_ty, _trait) = try_validation!( + self.ecx.get_ptr_vtable(vtable), self.path, err_ub!(DanglingIntPointer(..)) | - err_ub!(PointerUseAfterFree(..)) => - { "dangling vtable pointer in wide pointer" }, - err_ub!(AlignmentCheckFailed { .. }) => - { "unaligned vtable pointer in wide pointer" }, - err_ub!(PointerOutOfBounds { .. }) => - { "too small vtable" }, + err_ub!(InvalidVTablePointer(..)) => + { "{vtable}" } expected { "a vtable pointer" }, ); - try_validation!( - self.ecx.read_drop_type_from_vtable(vtable), - self.path, - err_ub!(DanglingIntPointer(..)) | - err_ub!(InvalidFunctionPointer(..)) => - { "invalid drop function pointer in vtable (not pointing to a function)" }, - err_ub!(InvalidVtableDropFn(..)) => - { "invalid drop function pointer in vtable (function has incompatible signature)" }, - // Stacked Borrows errors can happen here, see https://github.com/rust-lang/miri/issues/2123. - // (We assume there are no other MachineStop errors possible here.) - InterpError::MachineStop(_) => - { "vtable pointer does not have permission to read drop function pointer" }, - ); - try_validation!( - self.ecx.read_size_and_align_from_vtable(vtable), - self.path, - err_ub!(InvalidVtableSize) => - { "invalid vtable: size is bigger than largest supported object" }, - err_ub!(InvalidVtableAlignment(msg)) => - { "invalid vtable: alignment {}", msg }, - err_unsup!(ReadPointerAsBytes) => { "invalid size or align in vtable" }, - // Stacked Borrows errors can happen here, see https://github.com/rust-lang/miri/issues/2123. - // (We assume there are no other MachineStop errors possible here.) - InterpError::MachineStop(_) => - { "vtable pointer does not have permission to read size and alignment" }, - ); - // FIXME: More checks for the vtable. + // FIXME: check if the type/trait match what ty::Dynamic says? } ty::Slice(..) | ty::Str => { let _len = try_validation!( @@ -380,7 +345,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' /// Check a reference or `Box`. fn check_safe_pointer( &mut self, - value: &OpTy<'tcx, M::PointerTag>, + value: &OpTy<'tcx, M::Provenance>, kind: &str, ) -> InterpResult<'tcx> { let value = try_validation!( @@ -445,9 +410,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' if let Some(ref mut ref_tracking) = self.ref_tracking { // Proceed recursively even for ZST, no reason to skip them! // `!` is a ZST and we want to validate it. - if let Ok((alloc_id, _offset, _tag)) = self.ecx.ptr_try_get_alloc_id(place.ptr) { + if let Ok((alloc_id, _offset, _prov)) = self.ecx.ptr_try_get_alloc_id(place.ptr) { // Special handling for pointers to statics (irrespective of their type). - let alloc_kind = self.ecx.tcx.get_global_alloc(alloc_id); + let alloc_kind = self.ecx.tcx.try_get_global_alloc(alloc_id); if let Some(GlobalAlloc::Static(did)) = alloc_kind { assert!(!self.ecx.tcx.is_thread_local_static(did)); assert!(self.ecx.tcx.is_static(did)); @@ -491,8 +456,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' fn read_scalar( &self, - op: &OpTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, ScalarMaybeUninit<M::PointerTag>> { + op: &OpTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, ScalarMaybeUninit<M::Provenance>> { Ok(try_validation!( self.ecx.read_scalar(op), self.path, @@ -502,8 +467,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' fn read_immediate_forced( &self, - op: &OpTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, Immediate<M::PointerTag>> { + op: &OpTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, Immediate<M::Provenance>> { Ok(*try_validation!( self.ecx.read_immediate_raw(op, /*force*/ true), self.path, @@ -515,7 +480,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' /// at that type. Return `true` if the type is indeed primitive. fn try_visit_primitive( &mut self, - value: &OpTy<'tcx, M::PointerTag>, + value: &OpTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, bool> { // Go over all the primitive types let ty = value.layout.ty; @@ -552,15 +517,13 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' { "{: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_ptr = value.check_init().map_or(false, |v| matches!(v, Scalar::Ptr(..))); - if is_ptr { - throw_validation_failure!(self.path, - { "{:x}", value } expected { "plain (non-pointer) bytes" } - ) - } + // 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_ptr = value.check_init().map_or(false, |v| matches!(v, Scalar::Ptr(..))); + if is_ptr { + throw_validation_failure!(self.path, + { "{:x}", value } expected { "plain (non-pointer) bytes" } + ) } Ok(true) } @@ -603,15 +566,13 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' // If we check references recursively, also check that this points to a function. if let Some(_) = self.ref_tracking { - let ptr = self.ecx.scalar_to_ptr(value)?; + let ptr = value.to_pointer(self.ecx)?; let _fn = try_validation!( self.ecx.get_ptr_fn(ptr), self.path, - err_ub!(DanglingIntPointer(0, _)) => - { "a null function pointer" }, err_ub!(DanglingIntPointer(..)) | err_ub!(InvalidFunctionPointer(..)) => - { "{:x}", value } expected { "a function pointer" }, + { "{ptr}" } expected { "a function pointer" }, ); // FIXME: Check if the signature matches } else { @@ -652,7 +613,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' fn visit_scalar( &mut self, - scalar: ScalarMaybeUninit<M::PointerTag>, + scalar: ScalarMaybeUninit<M::Provenance>, scalar_layout: ScalarAbi, ) -> InterpResult<'tcx> { // We check `is_full_range` in a slightly complicated way because *if* we are checking @@ -735,7 +696,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> for ValidityVisitor<'rt, 'mir, 'tcx, M> { - type V = OpTy<'tcx, M::PointerTag>; + type V = OpTy<'tcx, M::Provenance>; #[inline(always)] fn ecx(&self) -> &InterpCx<'mir, 'tcx, M> { @@ -744,7 +705,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> fn read_discriminant( &mut self, - op: &OpTy<'tcx, M::PointerTag>, + op: &OpTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, VariantIdx> { self.with_elem(PathElem::EnumTag, move |this| { Ok(try_validation!( @@ -764,9 +725,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> #[inline] fn visit_field( &mut self, - old_op: &OpTy<'tcx, M::PointerTag>, + old_op: &OpTy<'tcx, M::Provenance>, field: usize, - new_op: &OpTy<'tcx, M::PointerTag>, + new_op: &OpTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { let elem = self.aggregate_field_path_elem(old_op.layout, field); self.with_elem(elem, move |this| this.visit_value(new_op)) @@ -775,9 +736,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> #[inline] fn visit_variant( &mut self, - old_op: &OpTy<'tcx, M::PointerTag>, + old_op: &OpTy<'tcx, M::Provenance>, variant_id: VariantIdx, - new_op: &OpTy<'tcx, M::PointerTag>, + new_op: &OpTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { let name = match old_op.layout.ty.kind() { ty::Adt(adt, _) => PathElem::Variant(adt.variant(variant_id).name), @@ -791,7 +752,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> #[inline(always)] fn visit_union( &mut self, - op: &OpTy<'tcx, M::PointerTag>, + op: &OpTy<'tcx, M::Provenance>, _fields: NonZeroUsize, ) -> InterpResult<'tcx> { // Special check preventing `UnsafeCell` inside unions in the inner part of constants. @@ -804,13 +765,13 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> } #[inline] - fn visit_box(&mut self, op: &OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> { + fn visit_box(&mut self, op: &OpTy<'tcx, M::Provenance>) -> InterpResult<'tcx> { self.check_safe_pointer(op, "box")?; Ok(()) } #[inline] - fn visit_value(&mut self, op: &OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> { + fn visit_value(&mut self, op: &OpTy<'tcx, M::Provenance>) -> InterpResult<'tcx> { trace!("visit_value: {:?}, {:?}", *op, op.layout); // Check primitive types -- the leaves of our recursive descent. @@ -881,7 +842,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> fn visit_aggregate( &mut self, - op: &OpTy<'tcx, M::PointerTag>, + op: &OpTy<'tcx, M::Provenance>, fields: impl Iterator<Item = InterpResult<'tcx, Self::V>>, ) -> InterpResult<'tcx> { match op.layout.ty.kind() { @@ -943,7 +904,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> match alloc.check_bytes( alloc_range(Size::ZERO, size), /*allow_uninit*/ !M::enforce_number_init(self.ecx), - /*allow_ptr*/ !M::enforce_number_no_provenance(self.ecx), + /*allow_ptr*/ false, ) { // In the happy case, we needn't check anything else. Ok(()) => {} @@ -992,9 +953,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { fn validate_operand_internal( &self, - op: &OpTy<'tcx, M::PointerTag>, + op: &OpTy<'tcx, M::Provenance>, path: Vec<PathElem>, - ref_tracking: Option<&mut RefTracking<MPlaceTy<'tcx, M::PointerTag>, Vec<PathElem>>>, + ref_tracking: Option<&mut RefTracking<MPlaceTy<'tcx, M::Provenance>, Vec<PathElem>>>, ctfe_mode: Option<CtfeValidationMode>, ) -> InterpResult<'tcx> { trace!("validate_operand_internal: {:?}, {:?}", *op, op.layout.ty); @@ -1031,9 +992,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { #[inline(always)] pub fn const_validate_operand( &self, - op: &OpTy<'tcx, M::PointerTag>, + op: &OpTy<'tcx, M::Provenance>, path: Vec<PathElem>, - ref_tracking: &mut RefTracking<MPlaceTy<'tcx, M::PointerTag>, Vec<PathElem>>, + ref_tracking: &mut RefTracking<MPlaceTy<'tcx, M::Provenance>, Vec<PathElem>>, ctfe_mode: CtfeValidationMode, ) -> InterpResult<'tcx> { self.validate_operand_internal(op, path, Some(ref_tracking), Some(ctfe_mode)) @@ -1043,7 +1004,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// `op` is assumed to cover valid memory if it is an indirect operand. /// It will error if the bits at the destination do not match the ones described by the layout. #[inline(always)] - pub fn validate_operand(&self, op: &OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> { + pub fn validate_operand(&self, op: &OpTy<'tcx, M::Provenance>) -> InterpResult<'tcx> { self.validate_operand_internal(op, vec![], None, None) } } diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs index 5956b7e4cb9..aee1f93b1a3 100644 --- a/compiler/rustc_const_eval/src/interpret/visitor.rs +++ b/compiler/rustc_const_eval/src/interpret/visitor.rs @@ -21,20 +21,20 @@ pub trait Value<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Sized { fn to_op_for_read( &self, ecx: &InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>>; + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>; /// Makes this into an `OpTy`, in a potentially more expensive way that is good for projections. fn to_op_for_proj( &self, ecx: &InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { self.to_op_for_read(ecx) } /// Creates this from an `OpTy`. /// /// If `to_op_for_proj` only ever produces `Indirect` operands, then this one is definitely `Indirect`. - fn from_op(op: &OpTy<'tcx, M::PointerTag>) -> Self; + fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self; /// Projects to the given enum variant. fn project_downcast( @@ -62,18 +62,18 @@ pub trait ValueMut<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Sized { fn to_op_for_read( &self, ecx: &InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>>; + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>; /// Makes this into an `OpTy`, in a potentially more expensive way that is good for projections. fn to_op_for_proj( &self, ecx: &mut InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>>; + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>; /// Creates this from an `OpTy`. /// /// If `to_op_for_proj` only ever produces `Indirect` operands, then this one is definitely `Indirect`. - fn from_op(op: &OpTy<'tcx, M::PointerTag>) -> Self; + fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self; /// Projects to the given enum variant. fn project_downcast( @@ -95,7 +95,7 @@ pub trait ValueMut<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Sized { // So we have some copy-paste here. (We could have a macro but since we only have 2 types with this // double-impl, that would barely make the code shorter, if at all.) -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tcx, M::PointerTag> { +impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tcx, M::Provenance> { #[inline(always)] fn layout(&self) -> TyAndLayout<'tcx> { self.layout @@ -105,12 +105,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tc fn to_op_for_read( &self, _ecx: &InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { Ok(self.clone()) } #[inline(always)] - fn from_op(op: &OpTy<'tcx, M::PointerTag>) -> Self { + fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self { op.clone() } @@ -134,7 +134,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tc } impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M> - for OpTy<'tcx, M::PointerTag> + for OpTy<'tcx, M::Provenance> { #[inline(always)] fn layout(&self) -> TyAndLayout<'tcx> { @@ -145,7 +145,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M> fn to_op_for_read( &self, _ecx: &InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { Ok(self.clone()) } @@ -153,12 +153,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M> fn to_op_for_proj( &self, _ecx: &mut InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { Ok(self.clone()) } #[inline(always)] - fn from_op(op: &OpTy<'tcx, M::PointerTag>) -> Self { + fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self { op.clone() } @@ -182,7 +182,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M> } impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> - for MPlaceTy<'tcx, M::PointerTag> + for MPlaceTy<'tcx, M::Provenance> { #[inline(always)] fn layout(&self) -> TyAndLayout<'tcx> { @@ -193,12 +193,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> fn to_op_for_read( &self, _ecx: &InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { Ok(self.into()) } #[inline(always)] - fn from_op(op: &OpTy<'tcx, M::PointerTag>) -> Self { + fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self { // assert is justified because our `to_op_for_read` only ever produces `Indirect` operands. op.assert_mem_place() } @@ -223,7 +223,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> } impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M> - for MPlaceTy<'tcx, M::PointerTag> + for MPlaceTy<'tcx, M::Provenance> { #[inline(always)] fn layout(&self) -> TyAndLayout<'tcx> { @@ -234,7 +234,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M> fn to_op_for_read( &self, _ecx: &InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { Ok(self.into()) } @@ -242,12 +242,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M> fn to_op_for_proj( &self, _ecx: &mut InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { Ok(self.into()) } #[inline(always)] - fn from_op(op: &OpTy<'tcx, M::PointerTag>) -> Self { + fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self { // assert is justified because our `to_op_for_proj` only ever produces `Indirect` operands. op.assert_mem_place() } @@ -272,7 +272,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M> } impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M> - for PlaceTy<'tcx, M::PointerTag> + for PlaceTy<'tcx, M::Provenance> { #[inline(always)] fn layout(&self) -> TyAndLayout<'tcx> { @@ -283,7 +283,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M> fn to_op_for_read( &self, ecx: &InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { // We `force_allocation` here so that `from_op` below can work. ecx.place_to_op(self) } @@ -292,13 +292,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M> fn to_op_for_proj( &self, ecx: &mut InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { // We `force_allocation` here so that `from_op` below can work. Ok(ecx.force_allocation(self)?.into()) } #[inline(always)] - fn from_op(op: &OpTy<'tcx, M::PointerTag>) -> Self { + fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self { // assert is justified because our `to_op` only ever produces `Indirect` operands. op.assert_mem_place().into() } @@ -336,7 +336,7 @@ macro_rules! make_value_visitor { #[inline(always)] fn read_discriminant( &mut self, - op: &OpTy<'tcx, M::PointerTag>, + op: &OpTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, VariantIdx> { Ok(self.ecx().read_discriminant(op)?.1) } @@ -425,7 +425,7 @@ macro_rules! make_value_visitor { // unsized values are never immediate, so we can assert_mem_place let op = v.to_op_for_read(self.ecx())?; let dest = op.assert_mem_place(); - let inner_mplace = self.ecx().unpack_dyn_trait(&dest)?.1; + let inner_mplace = self.ecx().unpack_dyn_trait(&dest)?; trace!("walk_value: dyn object layout: {:#?}", inner_mplace.layout); // recurse with the inner type return self.visit_field(&v, 0, &$value_trait::from_op(&inner_mplace.into())); 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 0581f491978..628298df473 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -652,7 +652,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { ProjectionElem::ConstantIndex { .. } | ProjectionElem::Downcast(..) - | ProjectionElem::OpaqueCast(..) | ProjectionElem::Subslice { .. } | ProjectionElem::Field(..) | ProjectionElem::Index(_) => {} 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 1d083b0bf82..33802261644 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs @@ -23,8 +23,8 @@ use rustc_trait_selection::traits::SelectionContext; use super::ConstCx; use crate::errors::{ - MutDerefErr, NonConstOpErr, PanicNonStrErr, RawPtrComparisonErr, RawPtrToIntErr, - StaticAccessErr, TransientMutBorrowErr, TransientMutBorrowErrRaw, + MutDerefErr, NonConstOpErr, PanicNonStrErr, RawPtrToIntErr, StaticAccessErr, + TransientMutBorrowErr, TransientMutBorrowErrRaw, }; use crate::util::{call_kind, CallDesugaringKind, CallKind}; @@ -654,10 +654,10 @@ pub struct RawPtrComparison; impl<'tcx> NonConstOp<'tcx> for RawPtrComparison { fn build_error( &self, - ccx: &ConstCx<'_, 'tcx>, + _: &ConstCx<'_, 'tcx>, span: Span, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - ccx.tcx.sess.create_err(RawPtrComparisonErr { span }) + span_bug!(span, "raw ptr comparison should already be caught in the trait system"); } } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs index c2b4f6eca5c..e0994451172 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -226,7 +226,7 @@ impl Qualif for CustomEq { // because that component may be part of an enum variant (e.g., // `Option::<NonStructuralMatchTy>::Some`), in which case some values of this type may be // structural-match (`Option::None`). - traits::search_for_structural_match_violation(cx.body.span, cx.tcx, ty, true).is_some() + traits::search_for_structural_match_violation(cx.body.span, cx.tcx, ty).is_some() } fn in_adt_inherently<'tcx>( @@ -316,7 +316,6 @@ where ProjectionElem::Deref | ProjectionElem::Field(_, _) - | ProjectionElem::OpaqueCast(_) | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } | ProjectionElem::Downcast(_, _) diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs index daa154576ae..ed4d8c95d1e 100644 --- a/compiler/rustc_const_eval/src/transform/promote_consts.rs +++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs @@ -361,7 +361,7 @@ impl<'tcx> Validator<'_, 'tcx> { return Err(Unpromotable); } } - ProjectionElem::OpaqueCast(..) | ProjectionElem::Downcast(..) => { + ProjectionElem::Downcast(..) => { return Err(Unpromotable); } diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index 2a801d0e702..5c641f54f68 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -9,7 +9,7 @@ doctest = false [dependencies] arrayvec = { version = "0.7", default-features = false } ena = "0.14" -indexmap = { version = "1.8.2" } +indexmap = { version = "1.9.1" } tracing = "0.1" jobserver_crate = { version = "0.1.13", package = "jobserver" } rustc_serialize = { path = "../rustc_serialize" } diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 0a2d2b40709..265f45b72d1 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -61,12 +61,10 @@ pub mod sip128; pub mod small_c_str; pub mod small_str; pub mod snapshot_map; -pub mod stable_map; pub mod svh; pub use ena::snapshot_vec; pub mod memmap; pub mod sorted_map; -pub mod stable_set; #[macro_use] pub mod stable_hasher; mod atomic_ref; diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index a915a4daa95..ce859173418 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -3,6 +3,7 @@ use rustc_index::bit_set; use rustc_index::vec; use smallvec::SmallVec; use std::hash::{BuildHasher, Hash, Hasher}; +use std::marker::PhantomData; use std::mem; #[cfg(test)] @@ -261,6 +262,10 @@ impl<CTX> HashStable<CTX> for ! { } } +impl<CTX, T> HashStable<CTX> for PhantomData<T> { + fn hash_stable(&self, _ctx: &mut CTX, _hasher: &mut StableHasher) {} +} + impl<CTX> HashStable<CTX> for ::std::num::NonZeroU32 { #[inline] fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { diff --git a/compiler/rustc_data_structures/src/stable_map.rs b/compiler/rustc_data_structures/src/stable_map.rs deleted file mode 100644 index 670452d0d8c..00000000000 --- a/compiler/rustc_data_structures/src/stable_map.rs +++ /dev/null @@ -1,100 +0,0 @@ -pub use rustc_hash::FxHashMap; -use std::borrow::Borrow; -use std::collections::hash_map::Entry; -use std::fmt; -use std::hash::Hash; - -/// A deterministic wrapper around FxHashMap that does not provide iteration support. -/// -/// It supports insert, remove, get and get_mut functions from FxHashMap. -/// It also allows to convert hashmap to a sorted vector with the method `into_sorted_vector()`. -#[derive(Clone)] -pub struct StableMap<K, V> { - base: FxHashMap<K, V>, -} - -impl<K, V> Default for StableMap<K, V> -where - K: Eq + Hash, -{ - fn default() -> StableMap<K, V> { - StableMap::new() - } -} - -impl<K, V> fmt::Debug for StableMap<K, V> -where - K: Eq + Hash + fmt::Debug, - V: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self.base) - } -} - -impl<K, V> PartialEq for StableMap<K, V> -where - K: Eq + Hash, - V: PartialEq, -{ - fn eq(&self, other: &StableMap<K, V>) -> bool { - self.base == other.base - } -} - -impl<K, V> Eq for StableMap<K, V> -where - K: Eq + Hash, - V: Eq, -{ -} - -impl<K, V> StableMap<K, V> -where - K: Eq + Hash, -{ - pub fn new() -> StableMap<K, V> { - StableMap { base: FxHashMap::default() } - } - - pub fn into_sorted_vector(self) -> Vec<(K, V)> - where - K: Ord + Copy, - { - let mut vector = self.base.into_iter().collect::<Vec<_>>(); - vector.sort_unstable_by_key(|pair| pair.0); - vector - } - - pub fn entry(&mut self, k: K) -> Entry<'_, K, V> { - self.base.entry(k) - } - - pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V> - where - K: Borrow<Q>, - Q: Hash + Eq, - { - self.base.get(k) - } - - pub fn get_mut<Q: ?Sized>(&mut self, k: &Q) -> Option<&mut V> - where - K: Borrow<Q>, - Q: Hash + Eq, - { - self.base.get_mut(k) - } - - pub fn insert(&mut self, k: K, v: V) -> Option<V> { - self.base.insert(k, v) - } - - pub fn remove<Q: ?Sized>(&mut self, k: &Q) -> Option<V> - where - K: Borrow<Q>, - Q: Hash + Eq, - { - self.base.remove(k) - } -} diff --git a/compiler/rustc_data_structures/src/stable_set.rs b/compiler/rustc_data_structures/src/stable_set.rs deleted file mode 100644 index c7ca74f5fbd..00000000000 --- a/compiler/rustc_data_structures/src/stable_set.rs +++ /dev/null @@ -1,77 +0,0 @@ -pub use rustc_hash::FxHashSet; -use std::borrow::Borrow; -use std::fmt; -use std::hash::Hash; - -/// A deterministic wrapper around FxHashSet that does not provide iteration support. -/// -/// It supports insert, remove, get functions from FxHashSet. -/// It also allows to convert hashset to a sorted vector with the method `into_sorted_vector()`. -#[derive(Clone)] -pub struct StableSet<T> { - base: FxHashSet<T>, -} - -impl<T> Default for StableSet<T> -where - T: Eq + Hash, -{ - fn default() -> StableSet<T> { - StableSet::new() - } -} - -impl<T> fmt::Debug for StableSet<T> -where - T: Eq + Hash + fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self.base) - } -} - -impl<T> PartialEq<StableSet<T>> for StableSet<T> -where - T: Eq + Hash, -{ - fn eq(&self, other: &StableSet<T>) -> bool { - self.base == other.base - } -} - -impl<T> Eq for StableSet<T> where T: Eq + Hash {} - -impl<T: Hash + Eq> StableSet<T> { - pub fn new() -> StableSet<T> { - StableSet { base: FxHashSet::default() } - } - - pub fn into_sorted_vector(self) -> Vec<T> - where - T: Ord, - { - let mut vector = self.base.into_iter().collect::<Vec<_>>(); - vector.sort_unstable(); - vector - } - - pub fn get<Q: ?Sized>(&self, value: &Q) -> Option<&T> - where - T: Borrow<Q>, - Q: Hash + Eq, - { - self.base.get(value) - } - - pub fn insert(&mut self, value: T) -> bool { - self.base.insert(value) - } - - pub fn remove<Q: ?Sized>(&mut self, value: &Q) -> bool - where - T: Borrow<Q>, - Q: Hash + Eq, - { - self.base.remove(value) - } -} diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index f5b059793cf..53ae913f94f 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -123,8 +123,7 @@ impl Callbacks for TimePassesCallbacks { 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.unstable_opts.time_passes || config.opts.unstable_opts.time); + self.time_passes = config.opts.prints.is_empty() && config.opts.time_passes(); config.opts.trimmed_def_paths = TrimmedDefPaths::GoodPath; } } @@ -249,7 +248,7 @@ fn run_compiler( if sopts.describe_lints { let mut lint_store = rustc_lint::new_lint_store( sopts.unstable_opts.no_interleave_lints, - compiler.session().unstable_options(), + compiler.session().enable_internal_lints(), ); let registered_lints = if let Some(register_lints) = compiler.register_lints() { diff --git a/compiler/rustc_driver/src/pretty.rs b/compiler/rustc_driver/src/pretty.rs index 12bac956adb..f66b1a2976f 100644 --- a/compiler/rustc_driver/src/pretty.rs +++ b/compiler/rustc_driver/src/pretty.rs @@ -328,7 +328,7 @@ impl<'tcx> pprust_hir::PpAnn for TypedAnnotation<'tcx> { let typeck_results = self.maybe_typeck_results.get().or_else(|| { self.tcx .hir() - .maybe_body_owned_by(self.tcx.hir().local_def_id_to_hir_id(expr.hir_id.owner)) + .maybe_body_owned_by(expr.hir_id.owner) .map(|body_id| self.tcx.typeck_body(body_id)) }); diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs index 97766b8368a..854625579ee 100644 --- a/compiler/rustc_error_codes/src/error_codes.rs +++ b/compiler/rustc_error_codes/src/error_codes.rs @@ -559,7 +559,7 @@ E0790: include_str!("./error_codes/E0790.md"), // E0273, // on_unimplemented #1 // E0274, // on_unimplemented #2 // E0278, // requirement is not satisfied - E0279, // requirement is not satisfied +// E0279, E0280, // requirement is not satisfied // E0285, // overflow evaluation builtin bounds // E0296, // replaced with a generic attribute input check diff --git a/compiler/rustc_error_codes/src/error_codes/E0118.md b/compiler/rustc_error_codes/src/error_codes/E0118.md index 8033aa8384c..cfabae1a634 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0118.md +++ b/compiler/rustc_error_codes/src/error_codes/E0118.md @@ -4,7 +4,7 @@ enum, union, or trait object. Erroneous code example: ```compile_fail,E0118 -impl fn(u8) { // error: no nominal type found for inherent implementation +impl<T> T { // error: no nominal type found for inherent implementation fn get_state(&self) -> String { // ... } @@ -20,8 +20,8 @@ trait LiveLongAndProsper { fn get_state(&self) -> String; } -// and now you can implement it on fn(u8) -impl LiveLongAndProsper for fn(u8) { +// and now you can implement it on T +impl<T> LiveLongAndProsper for T { fn get_state(&self) -> String { "He's dead, Jim!".to_owned() } @@ -33,9 +33,9 @@ For example, `NewType` is a newtype over `Foo` in `struct NewType(Foo)`. Example: ``` -struct TypeWrapper(fn(u8)); +struct TypeWrapper<T>(T); -impl TypeWrapper { +impl<T> TypeWrapper<T> { fn get_state(&self) -> String { "Fascinating!".to_owned() } diff --git a/compiler/rustc_error_messages/locales/en-US/passes.ftl b/compiler/rustc_error_messages/locales/en-US/passes.ftl index e4c9a4dad7b..b17eb9c2d26 100644 --- a/compiler/rustc_error_messages/locales/en-US/passes.ftl +++ b/compiler/rustc_error_messages/locales/en-US/passes.ftl @@ -81,8 +81,8 @@ passes-doc-keyword-not-mod = `#[doc(keyword = "...")]` should be used on modules passes-doc-keyword-invalid-ident = `{$doc_keyword}` is not a valid identifier -passes-doc-tuple-variadic-not-first = - `#[doc(tuple_variadic)]` must be used on the first of a set of tuple trait impls with varying arity +passes-doc-fake-variadic-not-valid = + `#[doc(fake_variadic)]` must be used on the first of a set of tuple or fn pointer trait impls with varying arity passes-doc-keyword-only-impl = `#[doc(keyword = "...")]` should be used on impl blocks @@ -149,3 +149,116 @@ passes-cold = {passes-should-be-applied-to-fn} passes-link = attribute should be applied to an `extern` block with non-Rust ABI .warn = {-passes-previously-accepted} .label = not an `extern` block + +passes-link-name = attribute should be applied to a foreign function or static + .warn = {-passes-previously-accepted} + .label = not a foreign function or static + .help = try `#[link(name = "{$value}")]` instead + +passes-no-link = attribute should be applied to an `extern crate` item + .label = not an `extern crate` item + +passes-export-name = attribute should be applied to a free function, impl method or static + .label = not a free function, impl method or static + +passes-rustc-layout-scalar-valid-range-not-struct = attribute should be applied to a struct + .label = not a struct + +passes-rustc-layout-scalar-valid-range-arg = expected exactly one integer literal argument + +passes-rustc-legacy-const-generics-only = #[rustc_legacy_const_generics] functions must only have const generics + .label = non-const generic parameter + +passes-rustc-legacy-const-generics-index = #[rustc_legacy_const_generics] must have one index for each generic parameter + .label = generic parameters + +passes-rustc-legacy-const-generics-index-exceed = index exceeds number of arguments + .label = there {$arg_count -> + [one] is + *[other] are + } only {$arg_count} {$arg_count -> + [one] argument + *[other] arguments + } + +passes-rustc-legacy-const-generics-index-negative = arguments should be non-negative integers + +passes-rustc-dirty-clean = attribute requires -Z query-dep-graph to be enabled + +passes-link-section = attribute should be applied to a function or static + .warn = {-passes-previously-accepted} + .label = not a function or static + +passes-no-mangle-foreign = `#[no_mangle]` has no effect on a foreign {$foreign_item_kind} + .warn = {-passes-previously-accepted} + .label = foreign {$foreign_item_kind} + .note = symbol names in extern blocks are not mangled + .suggestion = remove this attribute + +passes-no-mangle = attribute should be applied to a free function, impl method or static + .warn = {-passes-previously-accepted} + .label = not a free function, impl method or static + +passes-repr-ident = meta item in `repr` must be an identifier + +passes-repr-conflicting = conflicting representation hints + +passes-used-static = attribute must be applied to a `static` variable + +passes-used-compiler-linker = `used(compiler)` and `used(linker)` can't be used together + +passes-allow-internal-unstable = attribute should be applied to a macro + .label = not a macro + +passes-debug-visualizer-placement = attribute should be applied to a module + +passes-debug-visualizer-invalid = invalid argument + .note-1 = expected: `natvis_file = "..."` + .note-2 = OR + .note-3 = expected: `gdb_script_file = "..."` + +passes-rustc-allow-const-fn-unstable = attribute should be applied to `const fn` + .label = not a `const fn` + +passes-rustc-std-internal-symbol = attribute should be applied to functions or statics + .label = not a function or static + +passes-const-trait = attribute should be applied to a trait + +passes-stability-promotable = attribute cannot be applied to an expression + +passes-deprecated = attribute is ignored here + +passes-macro-use = `#[{$name}]` only has an effect on `extern crate` and modules + +passes-macro-export = `#[macro_export]` only has an effect on macro definitions + +passes-plugin-registrar = `#[plugin_registrar]` only has an effect on functions + +passes-unused-empty-lints-note = attribute `{$name}` with an empty list has no effect + +passes-unused-no-lints-note = attribute `{$name}` without any lints has no effect + +passes-unused-default-method-body-const-note = + `default_method_body_is_const` has been replaced with `#[const_trait]` on traits + +passes-unused = unused attribute + .suggestion = remove this attribute + +passes-non-exported-macro-invalid-attrs = attribute should be applied to function or closure + .label = not a function or closure + +passes-unused-duplicate = unused attribute + .suggestion = remove this attribute + .note = attribute also specified here + .warn = {-passes-previously-accepted} + +passes-unused-multiple = multiple `{$name}` attributes + .suggestion = remove this attribute + .note = attribute also specified here + +passes-rustc-lint-opt-ty = `#[rustc_lint_opt_ty]` should be applied to a struct + .label = not a struct + +passes-rustc-lint-opt-deny-field-access = `#[rustc_lint_opt_deny_field_access]` should be applied to a field + .label = not a field diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index 6b961eaeb42..2ac5c1960cd 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -299,7 +299,7 @@ impl DiagnosticMessage { /// - If `self` is non-translatable then return `self`'s message. pub fn with_subdiagnostic_message(&self, sub: SubdiagnosticMessage) -> Self { let attr = match sub { - SubdiagnosticMessage::Str(s) => return DiagnosticMessage::Str(s.clone()), + SubdiagnosticMessage::Str(s) => return DiagnosticMessage::Str(s), SubdiagnosticMessage::FluentIdentifier(id) => { return DiagnosticMessage::FluentIdentifier(id, None); } diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 7d7f3e18335..2a4f609a2d8 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -3,7 +3,7 @@ use crate::{ CodeSuggestion, DiagnosticMessage, EmissionGuarantee, Level, LintDiagnosticBuilder, MultiSpan, SubdiagnosticMessage, Substitution, SubstitutionPart, SuggestionStyle, }; -use rustc_data_structures::stable_map::FxHashMap; +use rustc_data_structures::fx::FxHashMap; use rustc_error_messages::FluentValue; use rustc_hir as hir; use rustc_lint_defs::{Applicability, LintExpectationId}; @@ -390,18 +390,17 @@ impl Diagnostic { expected: DiagnosticStyledString, found: DiagnosticStyledString, ) -> &mut Self { - let mut msg: Vec<_> = - vec![("required when trying to coerce from type `".to_string(), Style::NoStyle)]; + let mut msg: Vec<_> = vec![("required when trying to coerce from type `", Style::NoStyle)]; msg.extend(expected.0.iter().map(|x| match *x { - StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle), - StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight), + StringPart::Normal(ref s) => (s.as_str(), Style::NoStyle), + StringPart::Highlighted(ref s) => (s.as_str(), Style::Highlight), })); - msg.push(("` to type '".to_string(), Style::NoStyle)); + msg.push(("` to type '", Style::NoStyle)); msg.extend(found.0.iter().map(|x| match *x { - StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle), - StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight), + StringPart::Normal(ref s) => (s.as_str(), Style::NoStyle), + StringPart::Highlighted(ref s) => (s.as_str(), Style::Highlight), })); - msg.push(("`".to_string(), Style::NoStyle)); + msg.push(("`", Style::NoStyle)); // For now, just attach these as notes self.highlighted_note(msg); diff --git a/compiler/rustc_expand/Cargo.toml b/compiler/rustc_expand/Cargo.toml index 05e5913baa0..4ee7b6c42bb 100644 --- a/compiler/rustc_expand/Cargo.toml +++ b/compiler/rustc_expand/Cargo.toml @@ -24,3 +24,4 @@ rustc_parse = { path = "../rustc_parse" } rustc_session = { path = "../rustc_session" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } rustc_ast = { path = "../rustc_ast" } +crossbeam-channel = "0.5.0" diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index e1f19064d52..6e093811fcf 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -8,7 +8,7 @@ use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{AssocCtxt, Visitor}; 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::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::sync::{self, Lrc}; use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, PResult}; use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT; @@ -985,7 +985,7 @@ pub struct ExtCtxt<'a> { /// Error recovery mode entered when expansion is stuck /// (or during eager expansion, but that's a hack). pub force_mode: bool, - pub expansions: FxHashMap<Span, Vec<String>>, + pub expansions: FxIndexMap<Span, Vec<String>>, /// Used for running pre-expansion lints on freshly loaded modules. pub(super) lint_store: LintStoreExpandDyn<'a>, /// Used for storing lints generated during expansion, like `NAMED_ARGUMENTS_USED_POSITIONALLY` @@ -1020,7 +1020,7 @@ impl<'a> ExtCtxt<'a> { is_trailing_mac: false, }, force_mode: false, - expansions: FxHashMap::default(), + expansions: FxIndexMap::default(), expanded_inert_attrs: MarkedAttrs::new(), buffered_early_lint: vec![], } diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 2b941ec6809..3e1acf4382d 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -129,7 +129,7 @@ fn get_features( .span_suggestion( mi.span(), "expected just one word", - format!("{}", ident.name), + ident.name, Applicability::MaybeIncorrect, ) .emit(); @@ -401,7 +401,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.into_trees(); - let TokenTree::Token(pound_token @ Token { kind: TokenKind::Pound, .. }) = orig_trees.next().unwrap() else { + let TokenTree::Token(pound_token @ Token { kind: TokenKind::Pound, .. }, _) = orig_trees.next().unwrap() else { panic!("Bad tokens for attribute {:?}", attr); }; let pound_span = pound_token.span; @@ -409,7 +409,7 @@ impl<'a> StripUnconfigured<'a> { let mut trees = vec![(AttrAnnotatedTokenTree::Token(pound_token), Spacing::Alone)]; if attr.style == AttrStyle::Inner { // For inner attributes, we do the same thing for the `!` in `#![some_attr]` - let TokenTree::Token(bang_token @ Token { kind: TokenKind::Not, .. }) = orig_trees.next().unwrap() else { + let TokenTree::Token(bang_token @ Token { kind: TokenKind::Not, .. }, _) = orig_trees.next().unwrap() else { panic!("Bad tokens for attribute {:?}", attr); }; trees.push((AttrAnnotatedTokenTree::Token(bang_token), Spacing::Alone)); diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index 03b75d809a0..20d01b6dc26 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -1,4 +1,3 @@ -#![allow(rustc::potential_query_instability)] #![feature(array_windows)] #![feature(associated_type_bounds)] #![feature(associated_type_defaults)] diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 3e9ddd6aec0..f7e1575afbf 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -13,7 +13,7 @@ use rustc_ast::tokenstream::{DelimSpan, TokenStream}; use rustc_ast::{NodeId, DUMMY_NODE_ID}; use rustc_ast_pretty::pprust; use rustc_attr::{self as attr, TransparencyError}; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; use rustc_feature::Features; use rustc_lint_defs::builtin::{ @@ -198,7 +198,7 @@ fn macro_rules_dummy_expander<'cx>( DummyResult::any(span) } -fn trace_macros_note(cx_expansions: &mut FxHashMap<Span, Vec<String>>, sp: Span, message: String) { +fn trace_macros_note(cx_expansions: &mut FxIndexMap<Span, Vec<String>>, sp: Span, message: String) { let sp = sp.macro_backtrace().last().map_or(sp, |trace| trace.call_site); cx_expansions.entry(sp).or_default().push(message); } @@ -481,7 +481,7 @@ pub fn compile_declarative_macro( .map(|m| { if let MatchedTokenTree(ref tt) = *m { let tt = mbe::quoted::parse( - tt.clone().into(), + TokenStream::new(vec![tt.clone()]), true, &sess.parse_sess, def.id, @@ -505,7 +505,7 @@ pub fn compile_declarative_macro( .map(|m| { if let MatchedTokenTree(ref tt) = *m { return mbe::quoted::parse( - tt.clone().into(), + TokenStream::new(vec![tt.clone()]), false, &sess.parse_sess, def.id, diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs index 45c462bc425..fc808401a5e 100644 --- a/compiler/rustc_expand/src/mbe/metavar_expr.rs +++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs @@ -106,7 +106,7 @@ fn parse_depth<'sess>( let Some(tt) = iter.next() else { return Ok(0) }; let TokenTree::Token(token::Token { kind: token::TokenKind::Literal(lit), .. - }) = tt else { + }, _) = tt else { return Err(sess.span_diagnostic.struct_span_err( span, "meta-variable expression depth must be a literal" @@ -130,7 +130,7 @@ fn parse_ident<'sess>( sess: &'sess ParseSess, span: Span, ) -> PResult<'sess, Ident> { - if let Some(tt) = iter.next() && let TokenTree::Token(token) = tt { + if let Some(tt) = iter.next() && let TokenTree::Token(token, _) = tt { if let Some((elem, false)) = token.ident() { return Ok(elem); } @@ -153,7 +153,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 CursorRef<'_>) -> bool { - if let Some(TokenTree::Token(token::Token { kind: token::Comma, .. })) = iter.look_ahead(0) { + 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 707cb73f097..ee17d54f629 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -56,9 +56,9 @@ pub(super) fn parse( match tree { TokenTree::MetaVar(start_sp, ident) if parsing_patterns => { let span = match trees.next() { - Some(tokenstream::TokenTree::Token(Token { kind: token::Colon, span })) => { + Some(tokenstream::TokenTree::Token(Token { kind: token::Colon, span }, _)) => { match trees.next() { - Some(tokenstream::TokenTree::Token(token)) => match token.ident() { + Some(tokenstream::TokenTree::Token(token, _)) => match token.ident() { Some((frag, _)) => { let span = token.span.with_lo(start_sp.lo()); @@ -146,7 +146,7 @@ fn parse_tree( // Depending on what `tree` is, we could be parsing different parts of a macro match tree { // `tree` is a `$` token. Look at the next token in `trees` - tokenstream::TokenTree::Token(Token { kind: token::Dollar, span }) => { + tokenstream::TokenTree::Token(Token { kind: token::Dollar, span }, _) => { // FIXME: Handle `Invisible`-delimited groups in a more systematic way // during parsing. let mut next = outer_trees.next(); @@ -217,7 +217,7 @@ fn parse_tree( // `tree` is followed by an `ident`. This could be `$meta_var` or the `$crate` // special metavariable that names the crate of the invocation. - Some(tokenstream::TokenTree::Token(token)) if token.is_ident() => { + Some(tokenstream::TokenTree::Token(token, _)) if token.is_ident() => { let (ident, is_raw) = token.ident().unwrap(); let span = ident.span.with_lo(span.lo()); if ident.name == kw::Crate && !is_raw { @@ -228,7 +228,7 @@ fn parse_tree( } // `tree` is followed by another `$`. This is an escaped `$`. - Some(tokenstream::TokenTree::Token(Token { kind: token::Dollar, span })) => { + Some(tokenstream::TokenTree::Token(Token { kind: token::Dollar, span }, _)) => { if parsing_patterns { span_dollar_dollar_or_metavar_in_the_lhs_err( sess, @@ -241,7 +241,7 @@ fn parse_tree( } // `tree` is followed by some other token. This is an error. - Some(tokenstream::TokenTree::Token(token)) => { + Some(tokenstream::TokenTree::Token(token, _)) => { let msg = format!( "expected identifier, found `{}`", pprust::token_to_string(&token), @@ -256,7 +256,7 @@ fn parse_tree( } // `tree` is an arbitrary token. Keep it. - tokenstream::TokenTree::Token(token) => TokenTree::Token(token), + tokenstream::TokenTree::Token(token, _) => TokenTree::Token(token), // `tree` is the beginning of a delimited set of tokens (e.g., `(` or `{`). We need to // descend into the delimited set and further parse it. @@ -291,7 +291,7 @@ fn parse_kleene_op( span: Span, ) -> Result<Result<(KleeneOp, Span), Token>, Span> { match input.next() { - Some(tokenstream::TokenTree::Token(token)) => match kleene_op(&token) { + Some(tokenstream::TokenTree::Token(token, _)) => match kleene_op(&token) { Some(op) => Ok(Ok((op, token.span))), None => Ok(Err(token)), }, diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index fdd8dc93fc1..e47ea83ac38 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -3,7 +3,7 @@ use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq, MatchedTokenTree, use crate::mbe::{self, MetaVarExpr}; use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; -use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndSpacing}; +use rustc_ast::tokenstream::{DelimSpan, Spacing, TokenStream, TokenTree}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{pluralize, PResult}; use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed}; @@ -105,7 +105,7 @@ pub(super) fn transcribe<'a>( // // Thus, if we try to pop the `result_stack` and it is empty, we have reached the top-level // again, and we are done transcribing. - let mut result: Vec<TreeAndSpacing> = Vec::new(); + let mut result: Vec<TokenTree> = Vec::new(); let mut result_stack = Vec::new(); let mut marker = Marker(cx.current_expansion.id, transparency); @@ -123,7 +123,7 @@ pub(super) fn transcribe<'a>( if repeat_idx < repeat_len { *idx = 0; if let Some(sep) = sep { - result.push(TokenTree::Token(sep.clone()).into()); + result.push(TokenTree::Token(sep.clone(), Spacing::Alone)); } continue; } @@ -150,7 +150,7 @@ pub(super) fn transcribe<'a>( // Step back into the parent Delimited. let tree = TokenTree::Delimited(span, delim, TokenStream::new(result)); result = result_stack.pop().unwrap(); - result.push(tree.into()); + result.push(tree); } } continue; @@ -227,15 +227,15 @@ pub(super) fn transcribe<'a>( // `tt`s are emitted into the output stream directly as "raw tokens", // without wrapping them into groups. let token = tt.clone(); - result.push(token.into()); + result.push(token); } MatchedNonterminal(ref nt) => { // Other variables are emitted into the output stream as groups with // `Delimiter::Invisible` to maintain parsing priorities. // `Interpolated` is currently used for such groups in rustc parser. marker.visit_span(&mut sp); - let token = TokenTree::token(token::Interpolated(nt.clone()), sp); - result.push(token.into()); + let token = TokenTree::token_alone(token::Interpolated(nt.clone()), sp); + result.push(token); } MatchedSeq(..) => { // We were unable to descend far enough. This is an error. @@ -250,8 +250,11 @@ pub(super) fn transcribe<'a>( // with modified syntax context. (I believe this supports nested macros). marker.visit_span(&mut sp); marker.visit_ident(&mut original_ident); - result.push(TokenTree::token(token::Dollar, sp).into()); - result.push(TokenTree::Token(Token::from_ast_ident(original_ident)).into()); + result.push(TokenTree::token_alone(token::Dollar, sp)); + result.push(TokenTree::Token( + Token::from_ast_ident(original_ident), + Spacing::Alone, + )); } } @@ -281,8 +284,8 @@ pub(super) fn transcribe<'a>( mbe::TokenTree::Token(token) => { let mut token = token.clone(); mut_visit::visit_token(&mut token, &mut marker); - let tt = TokenTree::Token(token); - result.push(tt.into()); + let tt = TokenTree::Token(token, Spacing::Alone); + result.push(tt); } // There should be no meta-var declarations in the invocation of a macro. @@ -512,7 +515,18 @@ fn out_of_bounds_err<'a>( span: Span, ty: &str, ) -> DiagnosticBuilder<'a, ErrorGuaranteed> { - cx.struct_span_err(span, &format!("{ty} depth must be less than {max}")) + let msg = if max == 0 { + format!( + "meta-variable expression `{ty}` with depth parameter \ + must be called inside of a macro repetition" + ) + } else { + format!( + "depth parameter on meta-variable expression `{ty}` \ + must be less than {max}" + ) + }; + cx.struct_span_err(span, &msg) } fn transcribe_metavar_expr<'a>( @@ -521,7 +535,7 @@ fn transcribe_metavar_expr<'a>( interp: &FxHashMap<MacroRulesNormalizedIdent, NamedMatch>, marker: &mut Marker, repeats: &[(usize, usize)], - result: &mut Vec<TreeAndSpacing>, + result: &mut Vec<TokenTree>, sp: &DelimSpan, ) -> PResult<'a, ()> { let mut visited_span = || { @@ -533,11 +547,11 @@ fn transcribe_metavar_expr<'a>( MetaVarExpr::Count(original_ident, depth_opt) => { let matched = matched_from_ident(cx, original_ident, interp)?; let count = count_repetitions(cx, depth_opt, matched, &repeats, sp)?; - let tt = TokenTree::token( + let tt = TokenTree::token_alone( TokenKind::lit(token::Integer, sym::integer(count), None), visited_span(), ); - result.push(tt.into()); + result.push(tt); } MetaVarExpr::Ignore(original_ident) => { // Used to ensure that `original_ident` is present in the LHS @@ -545,25 +559,19 @@ fn transcribe_metavar_expr<'a>( } MetaVarExpr::Index(depth) => match repeats.iter().nth_back(depth) { Some((index, _)) => { - result.push( - TokenTree::token( - TokenKind::lit(token::Integer, sym::integer(*index), None), - visited_span(), - ) - .into(), - ); + result.push(TokenTree::token_alone( + TokenKind::lit(token::Integer, sym::integer(*index), None), + visited_span(), + )); } None => return Err(out_of_bounds_err(cx, repeats.len(), sp.entire(), "index")), }, MetaVarExpr::Length(depth) => match repeats.iter().nth_back(depth) { Some((_, length)) => { - result.push( - TokenTree::token( - TokenKind::lit(token::Integer, sym::integer(*length), None), - visited_span(), - ) - .into(), - ); + result.push(TokenTree::token_alone( + TokenKind::lit(token::Integer, sym::integer(*length), None), + visited_span(), + )); } None => return Err(out_of_bounds_err(cx, repeats.len(), sp.entire(), "length")), }, diff --git a/compiler/rustc_expand/src/parse/tests.rs b/compiler/rustc_expand/src/parse/tests.rs index 8da78792758..a3c631d3318 100644 --- a/compiler/rustc_expand/src/parse/tests.rs +++ b/compiler/rustc_expand/src/parse/tests.rs @@ -66,23 +66,23 @@ fn string_to_tts_macro() { match tts { [ - TokenTree::Token(Token { kind: token::Ident(name_macro_rules, false), .. }), - TokenTree::Token(Token { kind: token::Not, .. }), - TokenTree::Token(Token { kind: token::Ident(name_zip, false), .. }), + TokenTree::Token(Token { kind: token::Ident(name_macro_rules, false), .. }, _), + TokenTree::Token(Token { kind: token::Not, .. }, _), + TokenTree::Token(Token { kind: token::Ident(name_zip, false), .. }, _), TokenTree::Delimited(_, macro_delim, macro_tts), ] if name_macro_rules == &kw::MacroRules && name_zip.as_str() == "zip" => { let tts = ¯o_tts.trees().collect::<Vec<_>>(); match &tts[..] { [ TokenTree::Delimited(_, first_delim, first_tts), - TokenTree::Token(Token { kind: token::FatArrow, .. }), + TokenTree::Token(Token { kind: token::FatArrow, .. }, _), TokenTree::Delimited(_, second_delim, second_tts), ] if macro_delim == &Delimiter::Parenthesis => { let tts = &first_tts.trees().collect::<Vec<_>>(); match &tts[..] { [ - TokenTree::Token(Token { kind: token::Dollar, .. }), - TokenTree::Token(Token { kind: token::Ident(name, false), .. }), + TokenTree::Token(Token { kind: token::Dollar, .. }, _), + TokenTree::Token(Token { kind: token::Ident(name, false), .. }, _), ] if first_delim == &Delimiter::Parenthesis && name.as_str() == "a" => { } _ => panic!("value 3: {:?} {:?}", first_delim, first_tts), @@ -90,8 +90,8 @@ fn string_to_tts_macro() { let tts = &second_tts.trees().collect::<Vec<_>>(); match &tts[..] { [ - TokenTree::Token(Token { kind: token::Dollar, .. }), - TokenTree::Token(Token { kind: token::Ident(name, false), .. }), + TokenTree::Token(Token { kind: token::Dollar, .. }, _), + TokenTree::Token(Token { kind: token::Ident(name, false), .. }, _), ] if second_delim == &Delimiter::Parenthesis && name.as_str() == "a" => {} _ => panic!("value 4: {:?} {:?}", second_delim, second_tts), @@ -111,29 +111,27 @@ fn string_to_tts_1() { let tts = string_to_stream("fn a (b : i32) { b; }".to_string()); let expected = TokenStream::new(vec![ - TokenTree::token(token::Ident(kw::Fn, false), sp(0, 2)).into(), - TokenTree::token(token::Ident(Symbol::intern("a"), false), sp(3, 4)).into(), + TokenTree::token_alone(token::Ident(kw::Fn, false), sp(0, 2)), + TokenTree::token_alone(token::Ident(Symbol::intern("a"), false), sp(3, 4)), TokenTree::Delimited( DelimSpan::from_pair(sp(5, 6), sp(13, 14)), Delimiter::Parenthesis, TokenStream::new(vec![ - TokenTree::token(token::Ident(Symbol::intern("b"), false), sp(6, 7)).into(), - TokenTree::token(token::Colon, sp(8, 9)).into(), - TokenTree::token(token::Ident(sym::i32, false), sp(10, 13)).into(), + TokenTree::token_alone(token::Ident(Symbol::intern("b"), false), sp(6, 7)), + TokenTree::token_alone(token::Colon, sp(8, 9)), + TokenTree::token_alone(token::Ident(sym::i32, false), sp(10, 13)), ]) .into(), - ) - .into(), + ), TokenTree::Delimited( DelimSpan::from_pair(sp(15, 16), sp(20, 21)), Delimiter::Brace, TokenStream::new(vec![ - TokenTree::token(token::Ident(Symbol::intern("b"), false), sp(17, 18)).into(), - TokenTree::token(token::Semi, sp(18, 19)).into(), + TokenTree::token_joint(token::Ident(Symbol::intern("b"), false), sp(17, 18)), + TokenTree::token_alone(token::Semi, sp(18, 19)), ]) .into(), - ) - .into(), + ), ]); assert_eq!(tts, expected); diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index 9e1cd299fd6..1a2ab9d190e 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -4,14 +4,41 @@ use crate::proc_macro_server; use rustc_ast as ast; use rustc_ast::ptr::P; use rustc_ast::token; -use rustc_ast::tokenstream::{TokenStream, TokenTree}; +use rustc_ast::tokenstream::TokenStream; use rustc_data_structures::sync::Lrc; use rustc_errors::ErrorGuaranteed; use rustc_parse::parser::ForceCollect; +use rustc_session::config::ProcMacroExecutionStrategy; use rustc_span::profiling::SpannedEventArgRecorder; use rustc_span::{Span, DUMMY_SP}; -const EXEC_STRATEGY: pm::bridge::server::SameThread = pm::bridge::server::SameThread; +struct CrossbeamMessagePipe<T> { + tx: crossbeam_channel::Sender<T>, + rx: crossbeam_channel::Receiver<T>, +} + +impl<T> pm::bridge::server::MessagePipe<T> for CrossbeamMessagePipe<T> { + fn new() -> (Self, Self) { + let (tx1, rx1) = crossbeam_channel::bounded(1); + let (tx2, rx2) = crossbeam_channel::bounded(1); + (CrossbeamMessagePipe { tx: tx1, rx: rx2 }, CrossbeamMessagePipe { tx: tx2, rx: rx1 }) + } + + fn send(&mut self, value: T) { + self.tx.send(value).unwrap(); + } + + fn recv(&mut self) -> Option<T> { + self.rx.recv().ok() + } +} + +fn exec_strategy(ecx: &ExtCtxt<'_>) -> impl pm::bridge::server::ExecutionStrategy { + pm::bridge::server::MaybeCrossThread::<CrossbeamMessagePipe<_>>::new( + ecx.sess.opts.unstable_opts.proc_macro_execution_strategy + == ProcMacroExecutionStrategy::CrossThread, + ) +} pub struct BangProcMacro { pub client: pm::bridge::client::Client<pm::TokenStream, pm::TokenStream>, @@ -30,8 +57,9 @@ impl base::BangProcMacro for BangProcMacro { }); let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace; + let strategy = exec_strategy(ecx); let server = proc_macro_server::Rustc::new(ecx); - self.client.run(&EXEC_STRATEGY, server, input, proc_macro_backtrace).map_err(|e| { + self.client.run(&strategy, server, input, proc_macro_backtrace).map_err(|e| { let mut err = ecx.struct_span_err(span, "proc macro panicked"); if let Some(s) = e.as_str() { err.help(&format!("message: {}", s)); @@ -59,16 +87,17 @@ impl base::AttrProcMacro for AttrProcMacro { }); let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace; + let strategy = exec_strategy(ecx); let server = proc_macro_server::Rustc::new(ecx); - self.client - .run(&EXEC_STRATEGY, server, annotation, annotated, proc_macro_backtrace) - .map_err(|e| { + self.client.run(&strategy, server, annotation, annotated, proc_macro_backtrace).map_err( + |e| { let mut err = ecx.struct_span_err(span, "custom attribute panicked"); if let Some(s) = e.as_str() { err.help(&format!("message: {}", s)); } err.emit() - }) + }, + ) } } @@ -94,7 +123,7 @@ impl MultiItemModifier for DeriveProcMacro { Annotatable::Stmt(stmt) => token::NtStmt(stmt), _ => unreachable!(), }; - TokenTree::token(token::Interpolated(Lrc::new(nt)), DUMMY_SP).into() + TokenStream::token_alone(token::Interpolated(Lrc::new(nt)), DUMMY_SP) } else { item.to_tokens() }; @@ -105,8 +134,9 @@ impl MultiItemModifier for DeriveProcMacro { recorder.record_arg_with_span(ecx.expansion_descr(), span); }); let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace; + let strategy = exec_strategy(ecx); let server = proc_macro_server::Rustc::new(ecx); - match self.client.run(&EXEC_STRATEGY, server, input, proc_macro_backtrace) { + match self.client.run(&strategy, server, input, proc_macro_backtrace) { Ok(stream) => stream, Err(e) => { let mut err = ecx.struct_span_err(span, "proc-macro derive panicked"); diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 176c77ca6ed..7d9a4aed0bf 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -92,9 +92,8 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec<TokenTree<TokenStre let mut trees = Vec::with_capacity(stream.len().next_power_of_two()); let mut cursor = stream.into_trees(); - while let Some((tree, spacing)) = cursor.next_with_spacing() { - let joint = spacing == Joint; - let Token { kind, span } = match tree { + while let Some(tree) = cursor.next() { + let (Token { kind, span }, joint) = match tree { tokenstream::TokenTree::Delimited(span, delim, tts) => { let delimiter = pm::Delimiter::from_internal(delim); trees.push(TokenTree::Group(Group { @@ -108,7 +107,7 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec<TokenTree<TokenStre })); continue; } - tokenstream::TokenTree::Token(token) => token, + tokenstream::TokenTree::Token(token, spacing) => (token, spacing == Joint), }; let mut op = |s: &str| { @@ -194,7 +193,7 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec<TokenTree<TokenStre TokenKind::lit(token::Str, Symbol::intern(&escaped), None), ] .into_iter() - .map(|kind| tokenstream::TokenTree::token(kind, span)) + .map(|kind| tokenstream::TokenTree::token_alone(kind, span)) .collect(); trees.push(TokenTree::Punct(Punct { ch: b'#', joint: false, span })); if attr_style == ast::AttrStyle::Inner { @@ -246,16 +245,15 @@ impl ToInternal<TokenStream> for (TokenTree<TokenStream, Span, Symbol>, &mut Rus let (ch, joint, span) = match tree { TokenTree::Punct(Punct { ch, joint, span }) => (ch, joint, span), TokenTree::Group(Group { delimiter, stream, span: DelimSpan { open, close, .. } }) => { - return tokenstream::TokenTree::Delimited( + return tokenstream::TokenStream::delimited( tokenstream::DelimSpan { open, close }, delimiter.to_internal(), stream.unwrap_or_default(), - ) - .into(); + ); } TokenTree::Ident(self::Ident { sym, is_raw, span }) => { rustc.sess().symbol_gallery.insert(sym, span); - return tokenstream::TokenTree::token(Ident(sym, is_raw), span).into(); + return tokenstream::TokenStream::token_alone(Ident(sym, is_raw), span); } TokenTree::Literal(self::Literal { kind: self::LitKind::Integer, @@ -266,8 +264,8 @@ impl ToInternal<TokenStream> for (TokenTree<TokenStream, Span, Symbol>, &mut Rus let minus = BinOp(BinOpToken::Minus); let symbol = Symbol::intern(&symbol.as_str()[1..]); let integer = TokenKind::lit(token::Integer, symbol, suffix); - let a = tokenstream::TokenTree::token(minus, span); - let b = tokenstream::TokenTree::token(integer, span); + let a = tokenstream::TokenTree::token_alone(minus, span); + let b = tokenstream::TokenTree::token_alone(integer, span); return [a, b].into_iter().collect(); } TokenTree::Literal(self::Literal { @@ -279,16 +277,15 @@ impl ToInternal<TokenStream> for (TokenTree<TokenStream, Span, Symbol>, &mut Rus let minus = BinOp(BinOpToken::Minus); let symbol = Symbol::intern(&symbol.as_str()[1..]); let float = TokenKind::lit(token::Float, symbol, suffix); - let a = tokenstream::TokenTree::token(minus, span); - let b = tokenstream::TokenTree::token(float, span); + let a = tokenstream::TokenTree::token_alone(minus, span); + let b = tokenstream::TokenTree::token_alone(float, span); return [a, b].into_iter().collect(); } TokenTree::Literal(self::Literal { kind, symbol, suffix, span }) => { - return tokenstream::TokenTree::token( + return tokenstream::TokenStream::token_alone( TokenKind::lit(kind.to_internal(), symbol, suffix), span, - ) - .into(); + ); } }; @@ -318,8 +315,11 @@ impl ToInternal<TokenStream> for (TokenTree<TokenStream, Span, Symbol>, &mut Rus _ => unreachable!(), }; - let tree = tokenstream::TokenTree::token(kind, span); - TokenStream::new(vec![(tree, if joint { Joint } else { Alone })]) + if joint { + tokenstream::TokenStream::token_joint(kind, span) + } else { + tokenstream::TokenStream::token_alone(kind, span) + } } } @@ -486,12 +486,11 @@ impl server::TokenStream for Rustc<'_, '_> { // 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) if l.token.kind == token::Bool => { - Ok(tokenstream::TokenTree::token(token::Ident(l.token.symbol, false), l.span) - .into()) - } + ast::ExprKind::Lit(l) if l.token.kind == token::Bool => Ok( + tokenstream::TokenStream::token_alone(token::Ident(l.token.symbol, false), l.span), + ), ast::ExprKind::Lit(l) => { - Ok(tokenstream::TokenTree::token(token::Literal(l.token), l.span).into()) + Ok(tokenstream::TokenStream::token_alone(token::Literal(l.token), l.span)) } ast::ExprKind::Unary(ast::UnOp::Neg, e) => match &e.kind { ast::ExprKind::Lit(l) => match l.token { @@ -499,8 +498,8 @@ impl server::TokenStream for Rustc<'_, '_> { Ok(Self::TokenStream::from_iter([ // FIXME: The span of the `-` token is lost when // parsing, so we cannot faithfully recover it here. - tokenstream::TokenTree::token(token::BinOp(token::Minus), e.span), - tokenstream::TokenTree::token(token::Literal(l.token), l.span), + tokenstream::TokenTree::token_alone(token::BinOp(token::Minus), e.span), + tokenstream::TokenTree::token_alone(token::Literal(l.token), l.span), ])) } _ => Err(()), diff --git a/compiler/rustc_expand/src/tokenstream/tests.rs b/compiler/rustc_expand/src/tokenstream/tests.rs index e4a4db204d9..eed69681011 100644 --- a/compiler/rustc_expand/src/tokenstream/tests.rs +++ b/compiler/rustc_expand/src/tokenstream/tests.rs @@ -1,7 +1,7 @@ use crate::tests::string_to_stream; use rustc_ast::token; -use rustc_ast::tokenstream::{Spacing, TokenStream, TokenStreamBuilder, TokenTree}; +use rustc_ast::tokenstream::{TokenStream, TokenStreamBuilder}; use rustc_span::create_default_session_globals_then; use rustc_span::{BytePos, Span, Symbol}; @@ -13,10 +13,6 @@ fn sp(a: u32, b: u32) -> Span { Span::with_root_ctxt(BytePos(a), BytePos(b)) } -fn joint(tree: TokenTree) -> TokenStream { - TokenStream::new(vec![(tree, Spacing::Joint)]) -} - #[test] fn test_concat() { create_default_session_globals_then(|| { @@ -90,9 +86,8 @@ fn test_diseq_1() { #[test] fn test_is_empty() { create_default_session_globals_then(|| { - let test0: TokenStream = Vec::<TokenTree>::new().into_iter().collect(); - let test1: TokenStream = - TokenTree::token(token::Ident(Symbol::intern("a"), false), sp(0, 1)).into(); + let test0 = TokenStream::default(); + let test1 = TokenStream::token_alone(token::Ident(Symbol::intern("a"), false), sp(0, 1)); let test2 = string_to_ts("foo(bar::baz)"); assert_eq!(test0.is_empty(), true); @@ -105,9 +100,9 @@ fn test_is_empty() { fn test_dotdotdot() { create_default_session_globals_then(|| { let mut builder = TokenStreamBuilder::new(); - builder.push(joint(TokenTree::token(token::Dot, sp(0, 1)))); - builder.push(joint(TokenTree::token(token::Dot, sp(1, 2)))); - builder.push(TokenTree::token(token::Dot, sp(2, 3))); + builder.push(TokenStream::token_joint(token::Dot, sp(0, 1))); + builder.push(TokenStream::token_joint(token::Dot, sp(1, 2))); + builder.push(TokenStream::token_alone(token::Dot, sp(2, 3))); let stream = builder.build(); assert!(stream.eq_unspanned(&string_to_ts("..."))); assert_eq!(stream.trees().count(), 1); diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index c806df82145..0e73d8fd7f6 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -532,6 +532,9 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!(rustc_allocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL), rustc_attr!(rustc_allocator_nounwind, Normal, template!(Word), WarnFollowing, IMPL_DETAIL), + rustc_attr!(rustc_reallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL), + rustc_attr!(rustc_deallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL), + rustc_attr!(rustc_allocator_zeroed, Normal, template!(Word), WarnFollowing, IMPL_DETAIL), gated!( alloc_error_handler, Normal, template!(Word), WarnFollowing, experimental!(alloc_error_handler) @@ -616,6 +619,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // Used by the `rustc::untranslatable_diagnostic` and `rustc::diagnostic_outside_of_impl` lints // to assist in changes to diagnostic APIs. rustc_attr!(rustc_lint_diagnostics, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE), + // Used by the `rustc::bad_opt_access` lint to identify `DebuggingOptions` and `CodegenOptions` + // types (as well as any others in future). + rustc_attr!(rustc_lint_opt_ty, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE), + // Used by the `rustc::bad_opt_access` lint on fields + // types (as well as any others in future). + rustc_attr!(rustc_lint_opt_deny_field_access, Normal, template!(List: "message"), WarnFollowing, INTERNAL_UNSTABLE), // ========================================================================== // Internal attributes, Const related: diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index d0893cd09d8..be5b7eccbaf 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -713,7 +713,7 @@ impl<Id> Res<Id> { } /// Resolution for a lifetime appearing in a type. -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum LifetimeRes { /// Successfully linked the lifetime to a generic parameter. Param { @@ -738,13 +738,8 @@ pub enum LifetimeRes { binder: NodeId, }, /// This variant is used for anonymous lifetimes that we did not resolve during - /// late resolution. Shifting the work to the HIR lifetime resolver. - Anonymous { - /// Id of the introducing place. See `Param`. - binder: NodeId, - /// Whether this lifetime was spelled or elided. - elided: bool, - }, + /// late resolution. Those lifetimes will be inferred by typechecking. + Infer, /// Explicit `'static` lifetime. Static, /// Resolution failure. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 48a41c8bd24..f71400898e6 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -90,9 +90,6 @@ pub enum LifetimeName { /// User-given names or fresh (synthetic) names. Param(LocalDefId, ParamName), - /// User wrote nothing (e.g., the lifetime in `&u32`). - Implicit, - /// Implicit lifetime in a context like `dyn Foo`. This is /// distinguished from implicit lifetimes elsewhere because the /// lifetime that they default to must appear elsewhere within the @@ -110,8 +107,9 @@ pub enum LifetimeName { /// that was already reported. Error, - /// User wrote specifies `'_`. - Underscore, + /// User wrote an anonymous lifetime, either `'_` or nothing. + /// The semantics of this lifetime should be inferred by typechecking code. + Infer, /// User wrote `'static`. Static, @@ -120,10 +118,8 @@ pub enum LifetimeName { impl LifetimeName { pub fn ident(&self) -> Ident { match *self { - LifetimeName::ImplicitObjectLifetimeDefault - | LifetimeName::Implicit - | LifetimeName::Error => Ident::empty(), - LifetimeName::Underscore => Ident::with_dummy_span(kw::UnderscoreLifetime), + LifetimeName::ImplicitObjectLifetimeDefault | LifetimeName::Error => Ident::empty(), + LifetimeName::Infer => Ident::with_dummy_span(kw::UnderscoreLifetime), LifetimeName::Static => Ident::with_dummy_span(kw::StaticLifetime), LifetimeName::Param(_, param_name) => param_name.ident(), } @@ -132,8 +128,7 @@ impl LifetimeName { pub fn is_anonymous(&self) -> bool { match *self { LifetimeName::ImplicitObjectLifetimeDefault - | LifetimeName::Implicit - | LifetimeName::Underscore + | LifetimeName::Infer | LifetimeName::Param(_, ParamName::Fresh) | LifetimeName::Error => true, LifetimeName::Static | LifetimeName::Param(..) => false, @@ -142,9 +137,7 @@ impl LifetimeName { pub fn is_elided(&self) -> bool { match self { - LifetimeName::ImplicitObjectLifetimeDefault - | LifetimeName::Implicit - | LifetimeName::Underscore => true, + LifetimeName::ImplicitObjectLifetimeDefault | LifetimeName::Infer => true, // It might seem surprising that `Fresh` counts as // *not* elided -- but this is because, as far as the code @@ -954,6 +947,16 @@ pub struct Block<'hir> { pub targeted_by_break: bool, } +impl<'hir> Block<'hir> { + pub fn innermost_block(&self) -> &Block<'hir> { + let mut block = self; + while let Some(Expr { kind: ExprKind::Block(inner_block, _), .. }) = block.expr { + block = inner_block; + } + block + } +} + #[derive(Debug, HashStable_Generic)] pub struct Pat<'hir> { #[stable_hasher(ignore)] diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index d00b65da7e6..640974115b9 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -496,9 +496,8 @@ pub fn walk_lifetime<'v, V: Visitor<'v>>(visitor: &mut V, lifetime: &'v Lifetime | LifetimeName::Param(_, ParamName::Error) | LifetimeName::Static | LifetimeName::Error - | LifetimeName::Implicit | LifetimeName::ImplicitObjectLifetimeDefault - | LifetimeName::Underscore => {} + | LifetimeName::Infer => {} } } diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index b0bfac8e1f5..13b3e954e1f 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -11,7 +11,7 @@ use crate::def_id::DefId; use crate::{MethodKind, Target}; use rustc_ast as ast; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_macros::HashStable_Generic; use rustc_span::symbol::{kw, sym, Symbol}; @@ -134,8 +134,8 @@ macro_rules! language_item_table { } /// A mapping from the name of the lang item to its order and the form it must be of. - pub static ITEM_REFS: LazyLock<FxHashMap<Symbol, (usize, Target)>> = LazyLock::new(|| { - let mut item_refs = FxHashMap::default(); + pub static ITEM_REFS: LazyLock<FxIndexMap<Symbol, (usize, Target)>> = LazyLock::new(|| { + let mut item_refs = FxIndexMap::default(); $( item_refs.insert($module::$name, (LangItem::$variant as usize, $target)); )* item_refs }); diff --git a/compiler/rustc_hir/src/pat_util.rs b/compiler/rustc_hir/src/pat_util.rs index b30076100bb..93112199b60 100644 --- a/compiler/rustc_hir/src/pat_util.rs +++ b/compiler/rustc_hir/src/pat_util.rs @@ -1,7 +1,7 @@ use crate::def::{CtorOf, DefKind, Res}; use crate::def_id::DefId; use crate::hir::{self, HirId, PatKind}; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_span::hygiene::DesugaringKind; use rustc_span::symbol::Ident; use rustc_span::Span; diff --git a/compiler/rustc_hir/src/weak_lang_items.rs b/compiler/rustc_hir/src/weak_lang_items.rs index dad22725511..b6a85c0472e 100644 --- a/compiler/rustc_hir/src/weak_lang_items.rs +++ b/compiler/rustc_hir/src/weak_lang_items.rs @@ -4,7 +4,7 @@ use crate::def_id::DefId; use crate::{lang_items, LangItem, LanguageItems}; use rustc_ast as ast; -use rustc_data_structures::stable_map::StableMap; +use rustc_data_structures::fx::FxIndexMap; use rustc_span::symbol::{sym, Symbol}; use std::sync::LazyLock; @@ -12,8 +12,8 @@ use std::sync::LazyLock; macro_rules! weak_lang_items { ($($name:ident, $item:ident, $sym:ident;)*) => ( -pub static WEAK_ITEMS_REFS: LazyLock<StableMap<Symbol, LangItem>> = LazyLock::new(|| { - let mut map = StableMap::default(); +pub static WEAK_ITEMS_REFS: LazyLock<FxIndexMap<Symbol, LangItem>> = LazyLock::new(|| { + let mut map = FxIndexMap::default(); $(map.insert(sym::$name, LangItem::$item);)* map }); diff --git a/compiler/rustc_incremental/src/assert_module_sources.rs b/compiler/rustc_incremental/src/assert_module_sources.rs index 00aefac645f..89d419bc8e9 100644 --- a/compiler/rustc_incremental/src/assert_module_sources.rs +++ b/compiler/rustc_incremental/src/assert_module_sources.rs @@ -23,7 +23,7 @@ //! was re-used. use rustc_ast as ast; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::mir::mono::CodegenUnitNameBuilder; use rustc_middle::ty::TyCtxt; diff --git a/compiler/rustc_incremental/src/persist/work_product.rs b/compiler/rustc_incremental/src/persist/work_product.rs index 1b184eca964..2f1853c441e 100644 --- a/compiler/rustc_incremental/src/persist/work_product.rs +++ b/compiler/rustc_incremental/src/persist/work_product.rs @@ -3,7 +3,7 @@ //! [work products]: WorkProduct use crate::persist::fs::*; -use rustc_data_structures::stable_map::FxHashMap; +use rustc_data_structures::fx::FxHashMap; use rustc_fs_util::link_or_copy; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_session::Session; diff --git a/compiler/rustc_index/src/vec.rs b/compiler/rustc_index/src/vec.rs index 1a55519d7b1..30ff364210d 100644 --- a/compiler/rustc_index/src/vec.rs +++ b/compiler/rustc_index/src/vec.rs @@ -234,7 +234,9 @@ impl<I: Idx, T> IndexVec<I, T> { self.raw.get_mut(index.index()) } - /// Returns mutable references to two distinct elements, a and b. Panics if a == b. + /// Returns mutable references to two distinct elements, `a` and `b`. + /// + /// Panics if `a == b`. #[inline] pub fn pick2_mut(&mut self, a: I, b: I) -> (&mut T, &mut T) { let (ai, bi) = (a.index(), b.index()); @@ -249,7 +251,9 @@ impl<I: Idx, T> IndexVec<I, T> { } } - /// Returns mutable references to three distinct elements or panics otherwise. + /// Returns mutable references to three distinct elements. + /// + /// Panics if the elements are not distinct. #[inline] pub fn pick3_mut(&mut self, a: I, b: I, c: I) -> (&mut T, &mut T, &mut T) { let (ai, bi, ci) = (a.index(), b.index(), c.index()); diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index ce2698ef44c..130214a653f 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -65,6 +65,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { Self { tcx: self.tcx, defining_use_anchor: self.defining_use_anchor, + considering_regions: self.considering_regions, in_progress_typeck_results: self.in_progress_typeck_results, inner: self.inner.clone(), skip_leak_check: self.skip_leak_check.clone(), diff --git a/compiler/rustc_infer/src/infer/canonical/substitute.rs b/compiler/rustc_infer/src/infer/canonical/substitute.rs index 45ed4b63009..34b6113427d 100644 --- a/compiler/rustc_infer/src/infer/canonical/substitute.rs +++ b/compiler/rustc_infer/src/infer/canonical/substitute.rs @@ -7,7 +7,7 @@ //! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html use crate::infer::canonical::{Canonical, CanonicalVarValues}; -use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::fold::{FnMutDelegate, TypeFoldable}; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{self, TyCtxt}; @@ -71,21 +71,21 @@ where if var_values.var_values.is_empty() { value } else { - let fld_r = |br: ty::BoundRegion| match var_values.var_values[br.var].unpack() { - GenericArgKind::Lifetime(l) => l, - r => bug!("{:?} is a region but value is {:?}", br, r), + let delegate = FnMutDelegate { + regions: |br: ty::BoundRegion| match var_values.var_values[br.var].unpack() { + GenericArgKind::Lifetime(l) => l, + r => bug!("{:?} is a region but value is {:?}", br, r), + }, + types: |bound_ty: ty::BoundTy| match var_values.var_values[bound_ty.var].unpack() { + GenericArgKind::Type(ty) => ty, + r => bug!("{:?} is a type but value is {:?}", bound_ty, r), + }, + consts: |bound_ct: ty::BoundVar, _| match var_values.var_values[bound_ct].unpack() { + GenericArgKind::Const(ct) => ct, + c => bug!("{:?} is a const but value is {:?}", bound_ct, c), + }, }; - let fld_t = |bound_ty: ty::BoundTy| match var_values.var_values[bound_ty.var].unpack() { - GenericArgKind::Type(ty) => ty, - r => bug!("{:?} is a type but value is {:?}", bound_ty, r), - }; - - let fld_c = |bound_ct: ty::BoundVar, _| match var_values.var_values[bound_ct].unpack() { - GenericArgKind::Const(ct) => ct, - c => bug!("{:?} is a const but value is {:?}", bound_ct, c), - }; - - tcx.replace_escaping_bound_vars_uncached(value, fld_r, fld_t, fld_c) + tcx.replace_escaping_bound_vars_uncached(value, delegate) } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index d7505717bf3..39faed0bf36 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -237,12 +237,14 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>( span: Span, hidden_ty: Ty<'tcx>, hidden_region: ty::Region<'tcx>, + opaque_ty: ty::OpaqueTypeKey<'tcx>, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { + let opaque_ty = tcx.mk_opaque(opaque_ty.def_id.to_def_id(), opaque_ty.substs); let mut err = struct_span_err!( tcx.sess, span, E0700, - "hidden type for `impl Trait` captures lifetime that does not appear in bounds", + "hidden type for `{opaque_ty}` captures lifetime that does not appear in bounds", ); // Explain the region we are capturing. @@ -316,37 +318,6 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>( err } -/// Structurally compares two types, modulo any inference variables. -/// -/// Returns `true` if two types are equal, or if one type is an inference variable compatible -/// with the other type. A TyVar inference type is compatible with any type, and an IntVar or -/// FloatVar inference type are compatible with themselves or their concrete types (Int and -/// Float types, respectively). When comparing two ADTs, these rules apply recursively. -pub fn same_type_modulo_infer<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { - match (&a.kind(), &b.kind()) { - (&ty::Adt(did_a, substs_a), &ty::Adt(did_b, substs_b)) => { - if did_a != did_b { - return false; - } - - substs_a.types().zip(substs_b.types()).all(|(a, b)| same_type_modulo_infer(a, b)) - } - (&ty::Int(_), &ty::Infer(ty::InferTy::IntVar(_))) - | (&ty::Infer(ty::InferTy::IntVar(_)), &ty::Int(_) | &ty::Infer(ty::InferTy::IntVar(_))) - | (&ty::Float(_), &ty::Infer(ty::InferTy::FloatVar(_))) - | ( - &ty::Infer(ty::InferTy::FloatVar(_)), - &ty::Float(_) | &ty::Infer(ty::InferTy::FloatVar(_)), - ) - | (&ty::Infer(ty::InferTy::TyVar(_)), _) - | (_, &ty::Infer(ty::InferTy::TyVar(_))) => true, - (&ty::Ref(_, ty_a, mut_a), &ty::Ref(_, ty_b, mut_b)) => { - mut_a == mut_b && same_type_modulo_infer(*ty_a, *ty_b) - } - _ => a == b, - } -} - impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn report_region_errors( &self, @@ -645,13 +616,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { err.span_label(span, "expected due to this"); } ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause { - semi_span, + arm_block_id, + arm_span, + arm_ty, + prior_arm_block_id, + prior_arm_span, + prior_arm_ty, source, ref prior_arms, - last_ty, scrut_hir_id, opt_suggest_box_span, - arm_span, scrut_span, .. }) => match source { @@ -682,10 +656,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } _ => { - // `last_ty` can be `!`, `expected` will have better info when present. + // `prior_arm_ty` can be `!`, `expected` will have better info when present. let t = self.resolve_vars_if_possible(match exp_found { Some(ty::error::ExpectedFound { expected, .. }) => expected, - _ => last_ty, + _ => prior_arm_ty, }); let source_map = self.tcx.sess.source_map(); let mut any_multiline_arm = source_map.is_multiline(arm_span); @@ -710,37 +684,15 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { }; let msg = "`match` arms have incompatible types"; err.span_label(outer_error_span, msg); - if let Some((sp, boxed)) = semi_span { - if let (StatementAsExpression::NeedsBoxing, [.., prior_arm]) = - (boxed, &prior_arms[..]) - { - err.multipart_suggestion( - "consider removing this semicolon and boxing the expressions", - vec![ - (prior_arm.shrink_to_lo(), "Box::new(".to_string()), - (prior_arm.shrink_to_hi(), ")".to_string()), - (arm_span.shrink_to_lo(), "Box::new(".to_string()), - (arm_span.shrink_to_hi(), ")".to_string()), - (sp, String::new()), - ], - Applicability::HasPlaceholders, - ); - } else if matches!(boxed, StatementAsExpression::NeedsBoxing) { - err.span_suggestion_short( - sp, - "consider removing this semicolon and boxing the expressions", - "", - Applicability::MachineApplicable, - ); - } else { - err.span_suggestion_short( - sp, - "consider removing this semicolon", - "", - Applicability::MachineApplicable, - ); - } - } + self.suggest_remove_semi_or_return_binding( + err, + prior_arm_block_id, + prior_arm_ty, + prior_arm_span, + arm_block_id, + arm_ty, + arm_span, + ); if let Some(ret_sp) = opt_suggest_box_span { // Get return type span and point to it. self.suggest_boxing_for_return_impl_trait( @@ -752,43 +704,33 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } }, ObligationCauseCode::IfExpression(box IfExpressionCause { - then, - else_sp, - outer, - semicolon, + then_id, + else_id, + then_ty, + else_ty, + outer_span, opt_suggest_box_span, }) => { - err.span_label(then, "expected because of this"); - if let Some(sp) = outer { + let then_span = self.find_block_span_from_hir_id(then_id); + let else_span = self.find_block_span_from_hir_id(then_id); + err.span_label(then_span, "expected because of this"); + if let Some(sp) = outer_span { err.span_label(sp, "`if` and `else` have incompatible types"); } - if let Some((sp, boxed)) = semicolon { - if matches!(boxed, StatementAsExpression::NeedsBoxing) { - err.multipart_suggestion( - "consider removing this semicolon and boxing the expression", - vec![ - (then.shrink_to_lo(), "Box::new(".to_string()), - (then.shrink_to_hi(), ")".to_string()), - (else_sp.shrink_to_lo(), "Box::new(".to_string()), - (else_sp.shrink_to_hi(), ")".to_string()), - (sp, String::new()), - ], - Applicability::MachineApplicable, - ); - } else { - err.span_suggestion_short( - sp, - "consider removing this semicolon", - "", - Applicability::MachineApplicable, - ); - } - } + self.suggest_remove_semi_or_return_binding( + err, + Some(then_id), + then_ty, + then_span, + Some(else_id), + else_ty, + else_span, + ); if let Some(ret_sp) = opt_suggest_box_span { self.suggest_boxing_for_return_impl_trait( err, ret_sp, - [then, else_sp].into_iter(), + [then_span, else_span].into_iter(), ); } } @@ -808,6 +750,56 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } + fn suggest_remove_semi_or_return_binding( + &self, + err: &mut Diagnostic, + first_id: Option<hir::HirId>, + first_ty: Ty<'tcx>, + first_span: Span, + second_id: Option<hir::HirId>, + second_ty: Ty<'tcx>, + second_span: Span, + ) { + let remove_semicolon = + [(first_id, second_ty), (second_id, first_ty)].into_iter().find_map(|(id, ty)| { + let hir::Node::Block(blk) = self.tcx.hir().get(id?) else { return None }; + self.could_remove_semicolon(blk, ty) + }); + match remove_semicolon { + Some((sp, StatementAsExpression::NeedsBoxing)) => { + err.multipart_suggestion( + "consider removing this semicolon and boxing the expressions", + vec![ + (first_span.shrink_to_lo(), "Box::new(".to_string()), + (first_span.shrink_to_hi(), ")".to_string()), + (second_span.shrink_to_lo(), "Box::new(".to_string()), + (second_span.shrink_to_hi(), ")".to_string()), + (sp, String::new()), + ], + Applicability::MachineApplicable, + ); + } + Some((sp, StatementAsExpression::CorrectType)) => { + err.span_suggestion_short( + sp, + "consider removing this semicolon", + "", + Applicability::MachineApplicable, + ); + } + None => { + for (id, ty) in [(first_id, second_ty), (second_id, first_ty)] { + if let Some(id) = id + && let hir::Node::Block(blk) = self.tcx.hir().get(id) + && self.consider_returning_binding(blk, ty, err) + { + break; + } + } + } + } + } + fn suggest_boxing_for_return_impl_trait( &self, err: &mut Diagnostic, @@ -1723,15 +1715,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { }; debug!("exp_found {:?} terr {:?} cause.code {:?}", exp_found, terr, cause.code()); if let Some(exp_found) = exp_found { - let should_suggest_fixes = if let ObligationCauseCode::Pattern { root_ty, .. } = - cause.code() - { - // Skip if the root_ty of the pattern is not the same as the expected_ty. - // If these types aren't equal then we've probably peeled off a layer of arrays. - same_type_modulo_infer(self.resolve_vars_if_possible(*root_ty), exp_found.expected) - } else { - true - }; + let should_suggest_fixes = + if let ObligationCauseCode::Pattern { root_ty, .. } = cause.code() { + // Skip if the root_ty of the pattern is not the same as the expected_ty. + // If these types aren't equal then we've probably peeled off a layer of arrays. + self.same_type_modulo_infer(*root_ty, exp_found.expected) + } else { + true + }; if should_suggest_fixes { self.suggest_tuple_pattern(cause, &exp_found, diag); @@ -1786,7 +1777,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { .filter_map(|variant| { let sole_field = &variant.fields[0]; let sole_field_ty = sole_field.ty(self.tcx, substs); - if same_type_modulo_infer(sole_field_ty, exp_found.found) { + if self.same_type_modulo_infer(sole_field_ty, exp_found.found) { let variant_path = with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id)); // FIXME #56861: DRYer prelude filtering @@ -1894,7 +1885,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { exp_span, exp_found.expected, exp_found.found, ); - if let ObligationCauseCode::CompareImplMethodObligation { .. } = cause.code() { + if let ObligationCauseCode::CompareImplItemObligation { .. } = cause.code() { return; } @@ -1902,12 +1893,15 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.get_impl_future_output_ty(exp_found.expected).map(Binder::skip_binder), self.get_impl_future_output_ty(exp_found.found).map(Binder::skip_binder), ) { - (Some(exp), Some(found)) if same_type_modulo_infer(exp, found) => match cause.code() { - ObligationCauseCode::IfExpression(box IfExpressionCause { then, .. }) => { + (Some(exp), Some(found)) if self.same_type_modulo_infer(exp, found) => match cause + .code() + { + ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => { + let then_span = self.find_block_span_from_hir_id(*then_id); diag.multipart_suggestion( "consider `await`ing on both `Future`s", vec![ - (then.shrink_to_hi(), ".await".to_string()), + (then_span.shrink_to_hi(), ".await".to_string()), (exp_span.shrink_to_hi(), ".await".to_string()), ], Applicability::MaybeIncorrect, @@ -1934,7 +1928,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { diag.help("consider `await`ing on both `Future`s"); } }, - (_, Some(ty)) if same_type_modulo_infer(exp_found.expected, ty) => { + (_, Some(ty)) if self.same_type_modulo_infer(exp_found.expected, ty) => { diag.span_suggestion_verbose( exp_span.shrink_to_hi(), "consider `await`ing on the `Future`", @@ -1942,11 +1936,20 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { Applicability::MaybeIncorrect, ); } - (Some(ty), _) if same_type_modulo_infer(ty, exp_found.found) => match cause.code() { - ObligationCauseCode::Pattern { span: Some(span), .. } - | ObligationCauseCode::IfExpression(box IfExpressionCause { then: span, .. }) => { + (Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code() + { + ObligationCauseCode::Pattern { span: Some(then_span), .. } => { diag.span_suggestion_verbose( - span.shrink_to_hi(), + then_span.shrink_to_hi(), + "consider `await`ing on the `Future`", + ".await", + Applicability::MaybeIncorrect, + ); + } + ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => { + let then_span = self.find_block_span_from_hir_id(*then_id); + diag.span_suggestion_verbose( + then_span.shrink_to_hi(), "consider `await`ing on the `Future`", ".await", Applicability::MaybeIncorrect, @@ -1992,7 +1995,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { .iter() .filter(|field| field.vis.is_accessible_from(field.did, self.tcx)) .map(|field| (field.name, field.ty(self.tcx, expected_substs))) - .find(|(_, ty)| same_type_modulo_infer(*ty, exp_found.found)) + .find(|(_, ty)| self.same_type_modulo_infer(*ty, exp_found.found)) { if let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code() { if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { @@ -2057,7 +2060,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { | (_, ty::Infer(_)) | (ty::Param(_), _) | (ty::Infer(_), _) => {} - _ if same_type_modulo_infer(exp_ty, found_ty) => {} + _ if self.same_type_modulo_infer(exp_ty, found_ty) => {} _ => show_suggestion = false, }; } @@ -2179,7 +2182,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ) { let [expected_tup_elem] = expected_fields[..] else { return }; - if !same_type_modulo_infer(expected_tup_elem, found) { + if !self.same_type_modulo_infer(expected_tup_elem, found) { return; } @@ -2350,7 +2353,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { GenericKind::Projection(ref p) => format!("the associated type `{}`", p), }; - if let Some(SubregionOrigin::CompareImplMethodObligation { + if let Some(SubregionOrigin::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id, @@ -2409,9 +2412,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { #[derive(Debug)] enum SubOrigin<'hir> { GAT(&'hir hir::Generics<'hir>), - Impl(&'hir hir::Generics<'hir>), - Trait(&'hir hir::Generics<'hir>), - Fn(&'hir hir::Generics<'hir>), + Impl, + Trait, + Fn, Unknown, } let sub_origin = 'origin: { @@ -2426,34 +2429,30 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { kind: hir::ImplItemKind::TyAlias(..), generics, .. - }) => SubOrigin::GAT(generics), - Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Fn(..), - generics, - .. - }) => SubOrigin::Fn(generics), - Node::TraitItem(hir::TraitItem { + }) + | Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Type(..), generics, .. }) => SubOrigin::GAT(generics), - Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(..), - generics, + Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Fn(..), .. - }) => SubOrigin::Fn(generics), - Node::Item(hir::Item { - kind: hir::ItemKind::Trait(_, _, generics, _, _), + }) + | Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(..), .. - }) => SubOrigin::Trait(generics), + }) + | Node::Item(hir::Item { + kind: hir::ItemKind::Fn(..), .. + }) => SubOrigin::Fn, Node::Item(hir::Item { - kind: hir::ItemKind::Impl(hir::Impl { generics, .. }), + kind: hir::ItemKind::Trait(..), .. - }) => SubOrigin::Impl(generics), + }) => SubOrigin::Trait, Node::Item(hir::Item { - kind: hir::ItemKind::Fn(_, generics, _), - .. - }) => SubOrigin::Fn(generics), + kind: hir::ItemKind::Impl(..), .. + }) => SubOrigin::Impl, _ => continue, }; } @@ -2647,6 +2646,76 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { span.is_desugaring(DesugaringKind::QuestionMark) && self.tcx.is_diagnostic_item(sym::From, trait_def_id) } + + /// Structurally compares two types, modulo any inference variables. + /// + /// Returns `true` if two types are equal, or if one type is an inference variable compatible + /// with the other type. A TyVar inference type is compatible with any type, and an IntVar or + /// FloatVar inference type are compatible with themselves or their concrete types (Int and + /// Float types, respectively). When comparing two ADTs, these rules apply recursively. + pub fn same_type_modulo_infer(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool { + let (a, b) = self.resolve_vars_if_possible((a, b)); + match (a.kind(), b.kind()) { + (&ty::Adt(def_a, substs_a), &ty::Adt(def_b, substs_b)) => { + if def_a != def_b { + return false; + } + + substs_a + .types() + .zip(substs_b.types()) + .all(|(a, b)| self.same_type_modulo_infer(a, b)) + } + (&ty::FnDef(did_a, substs_a), &ty::FnDef(did_b, substs_b)) => { + if did_a != did_b { + return false; + } + + substs_a + .types() + .zip(substs_b.types()) + .all(|(a, b)| self.same_type_modulo_infer(a, b)) + } + (&ty::Int(_) | &ty::Uint(_), &ty::Infer(ty::InferTy::IntVar(_))) + | ( + &ty::Infer(ty::InferTy::IntVar(_)), + &ty::Int(_) | &ty::Uint(_) | &ty::Infer(ty::InferTy::IntVar(_)), + ) + | (&ty::Float(_), &ty::Infer(ty::InferTy::FloatVar(_))) + | ( + &ty::Infer(ty::InferTy::FloatVar(_)), + &ty::Float(_) | &ty::Infer(ty::InferTy::FloatVar(_)), + ) + | (&ty::Infer(ty::InferTy::TyVar(_)), _) + | (_, &ty::Infer(ty::InferTy::TyVar(_))) => true, + (&ty::Ref(_, ty_a, mut_a), &ty::Ref(_, ty_b, mut_b)) => { + mut_a == mut_b && self.same_type_modulo_infer(ty_a, ty_b) + } + (&ty::RawPtr(a), &ty::RawPtr(b)) => { + a.mutbl == b.mutbl && self.same_type_modulo_infer(a.ty, b.ty) + } + (&ty::Slice(a), &ty::Slice(b)) => self.same_type_modulo_infer(a, b), + (&ty::Array(a_ty, a_ct), &ty::Array(b_ty, b_ct)) => { + self.same_type_modulo_infer(a_ty, b_ty) && a_ct == b_ct + } + (&ty::Tuple(a), &ty::Tuple(b)) => { + if a.len() != b.len() { + return false; + } + std::iter::zip(a.iter(), b.iter()).all(|(a, b)| self.same_type_modulo_infer(a, b)) + } + (&ty::FnPtr(a), &ty::FnPtr(b)) => { + let a = a.skip_binder().inputs_and_output; + let b = b.skip_binder().inputs_and_output; + if a.len() != b.len() { + return false; + } + std::iter::zip(a.iter(), b.iter()).all(|(a, b)| self.same_type_modulo_infer(a, b)) + } + // FIXME(compiler-errors): This needs to be generalized more + _ => a == b, + } + } } impl<'a, 'tcx> InferCtxt<'a, 'tcx> { @@ -2717,8 +2786,15 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> { use self::FailureCode::*; use crate::traits::ObligationCauseCode::*; match self.code() { - CompareImplMethodObligation { .. } => Error0308("method not compatible with trait"), - CompareImplTypeObligation { .. } => Error0308("type not compatible with trait"), + CompareImplItemObligation { kind: ty::AssocKind::Fn, .. } => { + Error0308("method not compatible with trait") + } + CompareImplItemObligation { kind: ty::AssocKind::Type, .. } => { + Error0308("type not compatible with trait") + } + CompareImplItemObligation { kind: ty::AssocKind::Const, .. } => { + Error0308("const not compatible with trait") + } MatchExpressionArm(box MatchExpressionArmCause { source, .. }) => { Error0308(match source { hir::MatchSource::TryDesugar => "`?` operator has incompatible types", @@ -2752,8 +2828,15 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> { fn as_requirement_str(&self) -> &'static str { use crate::traits::ObligationCauseCode::*; match self.code() { - CompareImplMethodObligation { .. } => "method type is compatible with trait", - CompareImplTypeObligation { .. } => "associated type is compatible with trait", + CompareImplItemObligation { kind: ty::AssocKind::Fn, .. } => { + "method type is compatible with trait" + } + CompareImplItemObligation { kind: ty::AssocKind::Type, .. } => { + "associated type is compatible with trait" + } + CompareImplItemObligation { kind: ty::AssocKind::Const, .. } => { + "const is compatible with trait" + } ExprAssignable => "expression is assignable", IfExpression { .. } => "`if` and `else` have incompatible types", IfExpressionWithNoElse => "`if` missing an `else` returns `()`", @@ -2798,3 +2881,237 @@ impl TyCategory { } } } + +impl<'tcx> InferCtxt<'_, 'tcx> { + /// Given a [`hir::Block`], get the span of its last expression or + /// statement, peeling off any inner blocks. + pub fn find_block_span(&self, block: &'tcx hir::Block<'tcx>) -> Span { + let block = block.innermost_block(); + if let Some(expr) = &block.expr { + expr.span + } else if let Some(stmt) = block.stmts.last() { + // possibly incorrect trailing `;` in the else arm + stmt.span + } else { + // empty block; point at its entirety + block.span + } + } + + /// Given a [`hir::HirId`] for a block, get the span of its last expression + /// or statement, peeling off any inner blocks. + pub fn find_block_span_from_hir_id(&self, hir_id: hir::HirId) -> Span { + match self.tcx.hir().get(hir_id) { + hir::Node::Block(blk) => self.find_block_span(blk), + // The parser was in a weird state if either of these happen, but + // it's better not to panic. + hir::Node::Expr(e) => e.span, + _ => rustc_span::DUMMY_SP, + } + } + + /// Be helpful when the user wrote `{... expr; }` and taking the `;` off + /// is enough to fix the error. + pub fn could_remove_semicolon( + &self, + blk: &'tcx hir::Block<'tcx>, + expected_ty: Ty<'tcx>, + ) -> Option<(Span, StatementAsExpression)> { + let blk = blk.innermost_block(); + // Do not suggest if we have a tail expr. + if blk.expr.is_some() { + return None; + } + let last_stmt = blk.stmts.last()?; + let hir::StmtKind::Semi(ref last_expr) = last_stmt.kind else { + return None; + }; + let last_expr_ty = self.in_progress_typeck_results?.borrow().expr_ty_opt(*last_expr)?; + let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) { + _ if last_expr_ty.references_error() => return None, + _ if self.same_type_modulo_infer(last_expr_ty, expected_ty) => { + StatementAsExpression::CorrectType + } + (ty::Opaque(last_def_id, _), ty::Opaque(exp_def_id, _)) + if last_def_id == exp_def_id => + { + StatementAsExpression::CorrectType + } + (ty::Opaque(last_def_id, last_bounds), ty::Opaque(exp_def_id, exp_bounds)) => { + debug!( + "both opaque, likely future {:?} {:?} {:?} {:?}", + last_def_id, last_bounds, exp_def_id, exp_bounds + ); + + let last_local_id = last_def_id.as_local()?; + let exp_local_id = exp_def_id.as_local()?; + + match ( + &self.tcx.hir().expect_item(last_local_id).kind, + &self.tcx.hir().expect_item(exp_local_id).kind, + ) { + ( + hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: last_bounds, .. }), + hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: exp_bounds, .. }), + ) if iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| { + match (left, right) { + ( + hir::GenericBound::Trait(tl, ml), + hir::GenericBound::Trait(tr, mr), + ) if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id() + && ml == mr => + { + true + } + ( + hir::GenericBound::LangItemTrait(langl, _, _, argsl), + hir::GenericBound::LangItemTrait(langr, _, _, argsr), + ) if langl == langr => { + // FIXME: consider the bounds! + debug!("{:?} {:?}", argsl, argsr); + true + } + _ => false, + } + }) => + { + StatementAsExpression::NeedsBoxing + } + _ => StatementAsExpression::CorrectType, + } + } + _ => return None, + }; + let span = if last_stmt.span.from_expansion() { + let mac_call = rustc_span::source_map::original_sp(last_stmt.span, blk.span); + self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)? + } else { + last_stmt.span.with_lo(last_stmt.span.hi() - BytePos(1)) + }; + Some((span, needs_box)) + } + + /// Suggest returning a local binding with a compatible type if the block + /// has no return expression. + pub fn consider_returning_binding( + &self, + blk: &'tcx hir::Block<'tcx>, + expected_ty: Ty<'tcx>, + err: &mut Diagnostic, + ) -> bool { + let blk = blk.innermost_block(); + // Do not suggest if we have a tail expr. + if blk.expr.is_some() { + return false; + } + let mut shadowed = FxHashSet::default(); + let mut candidate_idents = vec![]; + let mut find_compatible_candidates = |pat: &hir::Pat<'_>| { + if let hir::PatKind::Binding(_, hir_id, ident, _) = &pat.kind + && let Some(pat_ty) = self + .in_progress_typeck_results + .and_then(|typeck_results| typeck_results.borrow().node_type_opt(*hir_id)) + { + let pat_ty = self.resolve_vars_if_possible(pat_ty); + if self.same_type_modulo_infer(pat_ty, expected_ty) + && !(pat_ty, expected_ty).references_error() + && shadowed.insert(ident.name) + { + candidate_idents.push((*ident, pat_ty)); + } + } + true + }; + + let hir = self.tcx.hir(); + for stmt in blk.stmts.iter().rev() { + let hir::StmtKind::Local(local) = &stmt.kind else { continue; }; + local.pat.walk(&mut find_compatible_candidates); + } + match hir.find(hir.get_parent_node(blk.hir_id)) { + Some(hir::Node::Expr(hir::Expr { hir_id, .. })) => { + match hir.find(hir.get_parent_node(*hir_id)) { + Some(hir::Node::Arm(hir::Arm { pat, .. })) => { + pat.walk(&mut find_compatible_candidates); + } + Some( + hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body), .. }) + | hir::Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Fn(_, body), + .. + }) + | hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body)), + .. + }) + | hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Closure(hir::Closure { body, .. }), + .. + }), + ) => { + for param in hir.body(*body).params { + param.pat.walk(&mut find_compatible_candidates); + } + } + Some(hir::Node::Expr(hir::Expr { + kind: + hir::ExprKind::If( + hir::Expr { kind: hir::ExprKind::Let(let_), .. }, + then_block, + _, + ), + .. + })) if then_block.hir_id == *hir_id => { + let_.pat.walk(&mut find_compatible_candidates); + } + _ => {} + } + } + _ => {} + } + + match &candidate_idents[..] { + [(ident, _ty)] => { + let sm = self.tcx.sess.source_map(); + if let Some(stmt) = blk.stmts.last() { + let stmt_span = sm.stmt_span(stmt.span, blk.span); + let sugg = if sm.is_multiline(blk.span) + && let Some(spacing) = sm.indentation_before(stmt_span) + { + format!("\n{spacing}{ident}") + } else { + format!(" {ident}") + }; + err.span_suggestion_verbose( + stmt_span.shrink_to_hi(), + format!("consider returning the local binding `{ident}`"), + sugg, + Applicability::MaybeIncorrect, + ); + } else { + let sugg = if sm.is_multiline(blk.span) + && let Some(spacing) = sm.indentation_before(blk.span.shrink_to_lo()) + { + format!("\n{spacing} {ident}\n{spacing}") + } else { + format!(" {ident} ") + }; + let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi(); + err.span_suggestion_verbose( + sm.span_extend_while(left_span, |c| c.is_whitespace()).unwrap_or(left_span), + format!("consider returning the local binding `{ident}`"), + sugg, + Applicability::MaybeIncorrect, + ); + } + true + } + values if (1..3).contains(&values.len()) => { + let spans = values.iter().map(|(ident, _)| ident.span).collect::<Vec<_>>(); + err.span_note(spans, "consider returning one of these bindings"); + true + } + _ => false, + } + } +} 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 b267140daa9..561d1354edd 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 @@ -1,4 +1,4 @@ -use crate::infer::type_variable::TypeVariableOriginKind; +use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use crate::infer::InferCtxt; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; @@ -8,12 +8,12 @@ use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{Body, Closure, Expr, ExprKind, FnRetTy, HirId, Local, LocalSource}; use rustc_middle::hir::nested_filter; -use rustc_middle::infer::unify_key::ConstVariableOriginKind; +use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Print, Printer}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst, SubstsRef}; use rustc_middle::ty::{self, DefIdTree, InferConst}; -use rustc_middle::ty::{Ty, TyCtxt, TypeckResults}; +use rustc_middle::ty::{IsSuggestable, Ty, TyCtxt, TypeckResults}; use rustc_span::symbol::{kw, Ident}; use rustc_span::{BytePos, Span}; use std::borrow::Cow; @@ -407,11 +407,40 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { err.span_label(span, cannot_infer_msg); - let printer = fmt_printer(self, Namespace::TypeNS); - let args = printer.comma_sep(generic_args.iter().copied()).unwrap().into_buffer(); + let args = fmt_printer(self, Namespace::TypeNS) + .comma_sep(generic_args.iter().copied().map(|arg| { + if arg.is_suggestable(self.tcx, true) { + return arg; + } + + match arg.unpack() { + GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"), + GenericArgKind::Type(_) => self + .next_ty_var(TypeVariableOrigin { + span: rustc_span::DUMMY_SP, + kind: TypeVariableOriginKind::MiscVariable, + }) + .into(), + GenericArgKind::Const(arg) => self + .next_const_var( + arg.ty(), + ConstVariableOrigin { + span: rustc_span::DUMMY_SP, + kind: ConstVariableOriginKind::MiscVariable, + }, + ) + .into(), + } + })) + .unwrap() + .into_buffer(); + err.span_suggestion_verbose( insert_span, - &format!("consider specifying the generic argument{}", pluralize!(args.len()),), + &format!( + "consider specifying the generic argument{}", + pluralize!(generic_args.len()), + ), format!("::<{}>", args), Applicability::HasPlaceholders, ); 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 e5ae835e813..c1b201da691 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 @@ -100,23 +100,6 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { // the lifetime of the TyRptr let hir_id = lifetime.hir_id; match (self.tcx.named_region(hir_id), self.bound_region) { - // Find the index of the anonymous region that was part of the - // error. We will then search the function parameters for a bound - // region at the right depth with the same index - ( - Some(rl::Region::LateBoundAnon(debruijn_index, _, anon_index)), - ty::BrAnon(br_index), - ) => { - debug!( - "LateBoundAnon depth = {:?} anon_index = {:?} br_index={:?}", - debruijn_index, anon_index, br_index - ); - if debruijn_index == self.current_index && anon_index == br_index { - self.found_type = Some(arg); - return; // we can stop visiting now - } - } - // Find the index of the named region that was part of the // error. We will then search the function parameters for a bound // region at the right depth with the same index @@ -151,8 +134,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { rl::Region::Static | rl::Region::Free(_, _) | rl::Region::EarlyBound(_, _) - | rl::Region::LateBound(_, _, _) - | rl::Region::LateBoundAnon(_, _, _), + | rl::Region::LateBound(_, _, _), ) | None, _, @@ -206,16 +188,6 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> { fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) { match (self.tcx.named_region(lifetime.hir_id), self.bound_region) { // the lifetime of the TyPath! - ( - Some(rl::Region::LateBoundAnon(debruijn_index, _, anon_index)), - ty::BrAnon(br_index), - ) => { - if debruijn_index == self.current_index && anon_index == br_index { - self.found_it = true; - return; - } - } - (Some(rl::Region::EarlyBound(_, id)), ty::BrNamed(def_id, _)) => { debug!("EarlyBound id={:?} def_id={:?}", id, def_id); if id == def_id { @@ -239,7 +211,6 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> { rl::Region::Static | rl::Region::EarlyBound(_, _) | rl::Region::LateBound(_, _, _) - | rl::Region::LateBoundAnon(_, _, _) | rl::Region::Free(_, _), ) | None, diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs index 43d5c9fdf33..893ca3cf79d 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs @@ -6,7 +6,7 @@ use crate::infer::error_reporting::note_and_explain_region; use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::{SubregionOrigin, TypeTrace}; use crate::traits::ObligationCauseCode; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; use rustc_hir::intravisit::Visitor; diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs index 02928c4aa57..246d27be71c 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -4,7 +4,7 @@ use crate::infer::error_reporting::nice_region_error::NiceRegionError; use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::{SubregionOrigin, TypeTrace}; use crate::traits::{ObligationCauseCode, UnifyReceiverContext}; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{walk_ty, Visitor}; diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs index 91bf9695dfc..da465a76429 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs @@ -2,17 +2,17 @@ use crate::infer::error_reporting::nice_region_error::NiceRegionError; use crate::infer::lexical_region_resolve::RegionResolutionError; -use crate::infer::{SubregionOrigin, Subtype}; -use crate::traits::ObligationCauseCode::CompareImplMethodObligation; +use crate::infer::Subtype; +use crate::traits::ObligationCauseCode::CompareImplItemObligation; use rustc_errors::{ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::Res; -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_middle::hir::nested_filter; use rustc_middle::ty::print::RegionHighlightMode; use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor}; -use rustc_span::{Span, Symbol}; +use rustc_span::Span; use std::ops::ControlFlow; @@ -22,38 +22,22 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let error = self.error.as_ref()?; debug!("try_report_impl_not_conforming_to_trait {:?}", error); if let RegionResolutionError::SubSupConflict( - _, var_origin, sub_origin, _sub, sup_origin, _sup, _, + _, + var_origin, + sub_origin, + _sub, + sup_origin, + _sup, + _, ) = error.clone() - && let (&Subtype(ref sup_trace), &Subtype(ref sub_trace)) = (&sup_origin, &sub_origin) - && let ( - sub_expected_found @ Some((sub_expected, sub_found)), - sup_expected_found @ Some(_), - CompareImplMethodObligation { trait_item_def_id, .. }, - ) = (sub_trace.values.ty(), sup_trace.values.ty(), sub_trace.cause.code()) + && let (Subtype(sup_trace), Subtype(sub_trace)) = (&sup_origin, &sub_origin) + && let sub_expected_found @ Some((sub_expected, sub_found)) = sub_trace.values.ty() + && let sup_expected_found @ Some(_) = sup_trace.values.ty() + && let CompareImplItemObligation { trait_item_def_id, .. } = sub_trace.cause.code() && sup_expected_found == sub_expected_found { - let guar = self.emit_err( - var_origin.span(), - sub_expected, - sub_found, - *trait_item_def_id, - ); - return Some(guar); - } - if let RegionResolutionError::ConcreteFailure(origin, _, _) - | RegionResolutionError::GenericBoundFailure(origin, _, _) = error.clone() - && let SubregionOrigin::CompareImplTypeObligation { - span, - impl_item_def_id, - trait_item_def_id, - } = origin - { - let guar = self.emit_associated_type_err( - span, - self.infcx.tcx.item_name(impl_item_def_id.to_def_id()), - impl_item_def_id, - trait_item_def_id, - ); + let guar = + self.emit_err(var_origin.span(), sub_expected, sub_found, *trait_item_def_id); return Some(guar); } None @@ -147,25 +131,6 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } err.emit() } - - fn emit_associated_type_err( - &self, - span: Span, - item_name: Symbol, - impl_item_def_id: LocalDefId, - trait_item_def_id: DefId, - ) -> ErrorGuaranteed { - let impl_sp = self.tcx().def_span(impl_item_def_id); - let trait_sp = self.tcx().def_span(trait_item_def_id); - let mut err = self - .tcx() - .sess - .struct_span_err(span, &format!("`impl` associated type signature for `{}` doesn't match `trait` associated type signature", item_name)); - err.span_label(impl_sp, "found"); - err.span_label(trait_sp, "expected"); - - err.emit() - } } struct TypeParamSpanVisitor<'tcx> { diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs index b356da0be55..3e9d491af62 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs @@ -49,10 +49,10 @@ pub fn find_param_with_region<'tcx>( }; let hir = &tcx.hir(); - let hir_id = hir.local_def_id_to_hir_id(id.as_local()?); - let body_id = hir.maybe_body_owned_by(hir_id)?; - let body = hir.body(body_id); + let def_id = id.as_local()?; + let hir_id = hir.local_def_id_to_hir_id(def_id); + // FIXME: use def_kind // Don't perform this on closures match hir.get(hir_id) { hir::Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => { @@ -61,11 +61,14 @@ pub fn find_param_with_region<'tcx>( _ => {} } + let body_id = hir.maybe_body_owned_by(def_id)?; + let owner_id = hir.body_owner(body_id); let fn_decl = hir.fn_decl_by_hir_id(owner_id).unwrap(); let poly_fn_sig = tcx.fn_sig(id); let fn_sig = tcx.liberate_late_bound_regions(id, poly_fn_sig); + let body = hir.body(body_id); body.params .iter() .take(if fn_sig.c_variadic { diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs index 67bbace39e3..c1940c5c082 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs @@ -86,13 +86,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { "...so that the declared lifetime parameter bounds are satisfied", ); } - infer::CompareImplMethodObligation { span, .. } => { - label_or_note( - span, - "...so that the definition in impl matches the definition from the trait", - ); - } - infer::CompareImplTypeObligation { span, .. } => { + infer::CompareImplItemObligation { span, .. } => { label_or_note( span, "...so that the definition in impl matches the definition from the trait", @@ -329,15 +323,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ); err } - infer::CompareImplMethodObligation { span, impl_item_def_id, trait_item_def_id } => { - self.report_extra_impl_obligation( - span, - impl_item_def_id, - trait_item_def_id, - &format!("`{}: {}`", sup, sub), - ) - } - infer::CompareImplTypeObligation { span, impl_item_def_id, trait_item_def_id } => self + infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => self .report_extra_impl_obligation( span, impl_item_def_id, @@ -357,7 +343,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let trait_predicates = self.tcx.explicit_predicates_of(trait_item_def_id); let impl_predicates = self.tcx.explicit_predicates_of(impl_item_def_id); - let impl_predicates: rustc_data_structures::stable_set::FxHashSet<_> = + let impl_predicates: rustc_data_structures::fx::FxHashSet<_> = impl_predicates.predicates.into_iter().map(|(pred, _)| pred).collect(); let clauses: Vec<_> = trait_predicates .predicates diff --git a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs index e543ae1fcda..ed257c144e0 100644 --- a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs +++ b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs @@ -4,6 +4,7 @@ use super::combine::CombineFields; use super::{HigherRankedType, InferCtxt}; use crate::infer::CombinedSnapshot; +use rustc_middle::ty::fold::FnMutDelegate; use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; use rustc_middle::ty::{self, Binder, TypeFoldable}; @@ -79,31 +80,31 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let next_universe = self.create_next_universe(); - let fld_r = |br: ty::BoundRegion| { - self.tcx.mk_region(ty::RePlaceholder(ty::PlaceholderRegion { - universe: next_universe, - name: br.kind, - })) - }; - - let fld_t = |bound_ty: ty::BoundTy| { - self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType { - universe: next_universe, - name: bound_ty.var, - })) - }; - - let fld_c = |bound_var: ty::BoundVar, ty| { - self.tcx.mk_const(ty::ConstS { - kind: ty::ConstKind::Placeholder(ty::PlaceholderConst { + let delegate = FnMutDelegate { + regions: |br: ty::BoundRegion| { + self.tcx.mk_region(ty::RePlaceholder(ty::PlaceholderRegion { + universe: next_universe, + name: br.kind, + })) + }, + types: |bound_ty: ty::BoundTy| { + self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType { universe: next_universe, - name: ty::BoundConst { var: bound_var, ty }, - }), - ty, - }) + name: bound_ty.var, + })) + }, + consts: |bound_var: ty::BoundVar, ty| { + self.tcx.mk_const(ty::ConstS { + kind: ty::ConstKind::Placeholder(ty::PlaceholderConst { + universe: next_universe, + name: ty::BoundConst { var: bound_var, ty }, + }), + ty, + }) + }, }; - let result = self.tcx.replace_bound_vars_uncached(binder, fld_r, fld_t, fld_c); + let result = self.tcx.replace_bound_vars_uncached(binder, delegate); debug!(?next_universe, ?result); result } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index b3dc2e586d2..6d5f4993d8d 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -21,8 +21,9 @@ use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType}; use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult}; use rustc_middle::traits::select; -use rustc_middle::ty::abstract_const::AbstractConst; +use rustc_middle::ty::abstract_const::{AbstractConst, FailureKind}; use rustc_middle::ty::error::{ExpectedFound, TypeError}; +use rustc_middle::ty::fold::BoundVarReplacerDelegate; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::relate::RelateResult; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, SubstsRef}; @@ -239,17 +240,36 @@ impl<'tcx> InferCtxtInner<'tcx> { } } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum DefiningAnchor { + /// `DefId` of the item. + Bind(LocalDefId), + /// When opaque types are not resolved, we `Bubble` up, meaning + /// return the opaque/hidden type pair from query, for caller of query to handle it. + Bubble, + /// Used to catch type mismatch errors when handling opaque types. + Error, +} + pub struct InferCtxt<'a, 'tcx> { pub tcx: TyCtxt<'tcx>, /// The `DefId` of the item in whose context we are performing inference or typeck. /// It is used to check whether an opaque type use is a defining use. /// - /// If it is `None`, we can't resolve opaque types here and need to bubble up + /// If it is `DefiningAnchor::Bubble`, we can't resolve opaque types here and need to bubble up /// the obligation. This frequently happens for /// short lived InferCtxt within queries. The opaque type obligations are forwarded /// to the outside until the end up in an `InferCtxt` for typeck or borrowck. - pub defining_use_anchor: Option<LocalDefId>, + /// + /// It is default value is `DefiningAnchor::Error`, this way it is easier to catch errors that + /// might come up during inference or typeck. + pub defining_use_anchor: DefiningAnchor, + + /// Whether this inference context should care about region obligations in + /// the root universe. Most notably, this is used during hir typeck as region + /// solving is left to borrowck instead. + pub considering_regions: bool, /// During type-checking/inference of a body, `in_progress_typeck_results` /// contains a reference to the typeck results being built up, which are @@ -386,15 +406,7 @@ pub enum SubregionOrigin<'tcx> { /// Comparing the signature and requirements of an impl method against /// the containing trait. - CompareImplMethodObligation { - span: Span, - impl_item_def_id: LocalDefId, - trait_item_def_id: DefId, - }, - - /// Comparing the signature and requirements of an impl associated type - /// against the containing trait - CompareImplTypeObligation { span: Span, impl_item_def_id: LocalDefId, trait_item_def_id: DefId }, + CompareImplItemObligation { span: Span, impl_item_def_id: LocalDefId, trait_item_def_id: DefId }, /// Checking that the bounds of a trait's associated type hold for a given impl CheckAssociatedTypeBounds { @@ -525,8 +537,9 @@ impl<'tcx> fmt::Display for FixupError<'tcx> { /// without using `Rc` or something similar. pub struct InferCtxtBuilder<'tcx> { tcx: TyCtxt<'tcx>, + defining_use_anchor: DefiningAnchor, + considering_regions: bool, fresh_typeck_results: Option<RefCell<ty::TypeckResults<'tcx>>>, - defining_use_anchor: Option<LocalDefId>, } pub trait TyCtxtInferExt<'tcx> { @@ -535,7 +548,12 @@ pub trait TyCtxtInferExt<'tcx> { impl<'tcx> TyCtxtInferExt<'tcx> for TyCtxt<'tcx> { fn infer_ctxt(self) -> InferCtxtBuilder<'tcx> { - InferCtxtBuilder { tcx: self, defining_use_anchor: None, fresh_typeck_results: None } + InferCtxtBuilder { + tcx: self, + defining_use_anchor: DefiningAnchor::Error, + considering_regions: true, + fresh_typeck_results: None, + } } } @@ -545,7 +563,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> { /// Will also change the scope for opaque type defining use checks to the given owner. pub fn with_fresh_in_progress_typeck_results(mut self, table_owner: LocalDefId) -> Self { self.fresh_typeck_results = Some(RefCell::new(ty::TypeckResults::new(table_owner))); - self.with_opaque_type_inference(table_owner) + self.with_opaque_type_inference(DefiningAnchor::Bind(table_owner)) } /// Whenever the `InferCtxt` should be able to handle defining uses of opaque types, @@ -554,8 +572,13 @@ impl<'tcx> InferCtxtBuilder<'tcx> { /// It is only meant to be called in two places, for typeck /// (via `with_fresh_in_progress_typeck_results`) and for the inference context used /// in mir borrowck. - pub fn with_opaque_type_inference(mut self, defining_use_anchor: LocalDefId) -> Self { - self.defining_use_anchor = Some(defining_use_anchor); + pub fn with_opaque_type_inference(mut self, defining_use_anchor: DefiningAnchor) -> Self { + self.defining_use_anchor = defining_use_anchor; + self + } + + pub fn ignoring_regions(mut self) -> Self { + self.considering_regions = false; self } @@ -583,11 +606,17 @@ impl<'tcx> InferCtxtBuilder<'tcx> { } pub fn enter<R>(&mut self, f: impl for<'a> FnOnce(InferCtxt<'a, 'tcx>) -> R) -> R { - let InferCtxtBuilder { tcx, defining_use_anchor, ref fresh_typeck_results } = *self; + let InferCtxtBuilder { + tcx, + defining_use_anchor, + considering_regions, + ref fresh_typeck_results, + } = *self; let in_progress_typeck_results = fresh_typeck_results.as_ref(); f(InferCtxt { tcx, defining_use_anchor, + considering_regions, in_progress_typeck_results, inner: RefCell::new(InferCtxtInner::new()), lexical_region_resolutions: RefCell::new(None), @@ -938,14 +967,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { #[instrument(skip(self), level = "debug")] pub fn member_constraint( &self, - opaque_type_def_id: LocalDefId, + key: ty::OpaqueTypeKey<'tcx>, definition_span: Span, hidden_ty: Ty<'tcx>, region: ty::Region<'tcx>, in_regions: &Lrc<Vec<ty::Region<'tcx>>>, ) { self.inner.borrow_mut().unwrap_region_constraints().member_constraint( - opaque_type_def_id, + key, definition_span, hidden_ty, region, @@ -1025,16 +1054,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { &self, cause: &traits::ObligationCause<'tcx>, predicate: ty::PolyRegionOutlivesPredicate<'tcx>, - ) -> UnitResult<'tcx> { - self.commit_if_ok(|_snapshot| { - let ty::OutlivesPredicate(r_a, r_b) = - self.replace_bound_vars_with_placeholders(predicate); - let origin = SubregionOrigin::from_obligation_cause(cause, || { - RelateRegionParamBound(cause.span) - }); - self.sub_regions(origin, r_b, r_a); // `b : a` ==> `a <= b` - Ok(()) - }) + ) { + let ty::OutlivesPredicate(r_a, r_b) = self.replace_bound_vars_with_placeholders(predicate); + let origin = + SubregionOrigin::from_obligation_cause(cause, || RelateRegionParamBound(cause.span)); + self.sub_regions(origin, r_b, r_a); // `b : a` ==> `a <= b` } /// Number of type variables created so far. @@ -1301,6 +1325,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// result. After this, no more unification operations should be /// done -- or the compiler will panic -- but it is legal to use /// `resolve_vars_if_possible` as well as `fully_resolve`. + /// + /// Make sure to call [`InferCtxt::process_registered_region_obligations`] + /// first, or preferrably use [`InferCtxt::check_region_obligations_and_report_errors`] + /// to do both of these operations together. pub fn resolve_regions_and_report_errors( &self, generic_param_scope: LocalDefId, @@ -1541,32 +1569,56 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { return inner; } - let mut region_map = FxHashMap::default(); - let fld_r = |br: ty::BoundRegion| { - *region_map - .entry(br) - .or_insert_with(|| self.next_region_var(LateBoundRegion(span, br.kind, lbrct))) - }; + struct ToFreshVars<'a, 'tcx> { + infcx: &'a InferCtxt<'a, 'tcx>, + span: Span, + lbrct: LateBoundRegionConversionTime, + map: FxHashMap<ty::BoundVar, ty::GenericArg<'tcx>>, + } - let mut ty_map = FxHashMap::default(); - let fld_t = |bt: ty::BoundTy| { - *ty_map.entry(bt).or_insert_with(|| { - self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span, - }) - }) - }; - let mut ct_map = FxHashMap::default(); - let fld_c = |bc: ty::BoundVar, ty| { - *ct_map.entry(bc).or_insert_with(|| { - self.next_const_var( - ty, - ConstVariableOrigin { kind: ConstVariableOriginKind::MiscVariable, span }, - ) - }) - }; - self.tcx.replace_bound_vars_uncached(value, fld_r, fld_t, fld_c) + impl<'tcx> BoundVarReplacerDelegate<'tcx> for ToFreshVars<'_, 'tcx> { + fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx> { + self.map + .entry(br.var) + .or_insert_with(|| { + self.infcx + .next_region_var(LateBoundRegion(self.span, br.kind, self.lbrct)) + .into() + }) + .expect_region() + } + fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx> { + self.map + .entry(bt.var) + .or_insert_with(|| { + self.infcx + .next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: self.span, + }) + .into() + }) + .expect_ty() + } + fn replace_const(&mut self, bv: ty::BoundVar, ty: Ty<'tcx>) -> ty::Const<'tcx> { + self.map + .entry(bv) + .or_insert_with(|| { + self.infcx + .next_const_var( + ty, + ConstVariableOrigin { + kind: ConstVariableOriginKind::MiscVariable, + span: self.span, + }, + ) + .into() + }) + .expect_const() + } + } + let delegate = ToFreshVars { infcx: self, span, lbrct, map: Default::default() }; + self.tcx.replace_bound_vars_uncached(value, delegate) } /// See the [`region_constraints::RegionConstraintCollector::verify_generic_bound`] method. @@ -1652,7 +1704,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { #[instrument(skip(self), level = "debug")] pub fn const_eval_resolve( &self, - param_env: ty::ParamEnv<'tcx>, + mut param_env: ty::ParamEnv<'tcx>, unevaluated: ty::Unevaluated<'tcx>, span: Option<Span>, ) -> EvalToValTreeResult<'tcx> { @@ -1663,10 +1715,19 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // variables if substs.has_infer_types_or_consts() { let ac = AbstractConst::new(self.tcx, unevaluated.shrink()); - if let Ok(None) = ac { - substs = InternalSubsts::identity_for_item(self.tcx, unevaluated.def.did); - } else { - return Err(ErrorHandled::TooGeneric); + match ac { + Ok(None) => { + substs = InternalSubsts::identity_for_item(self.tcx, unevaluated.def.did); + param_env = self.tcx.param_env(unevaluated.def.did); + } + Ok(Some(ct)) => { + if ct.unify_failure_kind(self.tcx) == FailureKind::Concrete { + substs = replace_param_and_infer_substs_with_placeholder(self.tcx, substs); + } else { + return Err(ErrorHandled::TooGeneric); + } + } + Err(guar) => return Err(ErrorHandled::Reported(guar)), } } @@ -1914,8 +1975,7 @@ impl<'tcx> SubregionOrigin<'tcx> { ReborrowUpvar(a, _) => a, DataBorrowed(_, a) => a, ReferenceOutlivesReferent(_, a) => a, - CompareImplMethodObligation { span, .. } => span, - CompareImplTypeObligation { span, .. } => span, + CompareImplItemObligation { span, .. } => span, CheckAssociatedTypeBounds { ref parent, .. } => parent.span(), } } @@ -1929,19 +1989,11 @@ impl<'tcx> SubregionOrigin<'tcx> { SubregionOrigin::ReferenceOutlivesReferent(ref_type, cause.span) } - traits::ObligationCauseCode::CompareImplMethodObligation { - impl_item_def_id, - trait_item_def_id, - } => SubregionOrigin::CompareImplMethodObligation { - span: cause.span, - impl_item_def_id, - trait_item_def_id, - }, - - traits::ObligationCauseCode::CompareImplTypeObligation { + traits::ObligationCauseCode::CompareImplItemObligation { impl_item_def_id, trait_item_def_id, - } => SubregionOrigin::CompareImplTypeObligation { + kind: _, + } => SubregionOrigin::CompareImplItemObligation { span: cause.span, impl_item_def_id, trait_item_def_id, @@ -1986,3 +2038,43 @@ impl<'tcx> fmt::Debug for RegionObligation<'tcx> { ) } } + +/// Replaces substs that reference param or infer variables with suitable +/// placeholders. This function is meant to remove these param and infer +/// substs when they're not actually needed to evaluate a constant. +fn replace_param_and_infer_substs_with_placeholder<'tcx>( + tcx: TyCtxt<'tcx>, + substs: SubstsRef<'tcx>, +) -> SubstsRef<'tcx> { + tcx.mk_substs(substs.iter().enumerate().map(|(idx, arg)| { + match arg.unpack() { + GenericArgKind::Type(_) + if arg.has_param_types_or_consts() || arg.has_infer_types_or_consts() => + { + tcx.mk_ty(ty::Placeholder(ty::PlaceholderType { + universe: ty::UniverseIndex::ROOT, + name: ty::BoundVar::from_usize(idx), + })) + .into() + } + GenericArgKind::Const(ct) + if ct.has_infer_types_or_consts() || ct.has_param_types_or_consts() => + { + let ty = ct.ty(); + // If the type references param or infer, replace that too... + if ty.has_param_types_or_consts() || ty.has_infer_types_or_consts() { + bug!("const `{ct}`'s type should not reference params or types"); + } + tcx.mk_const(ty::ConstS { + ty, + kind: ty::ConstKind::Placeholder(ty::PlaceholderConst { + universe: ty::UniverseIndex::ROOT, + name: ty::BoundConst { ty, var: ty::BoundVar::from_usize(idx) }, + }), + }) + .into() + } + _ => arg, + } + })) +} diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index 4ee9c4eeda4..a1c7b70bd9c 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -1,4 +1,4 @@ -use crate::infer::{InferCtxt, InferOk}; +use crate::infer::{DefiningAnchor, InferCtxt, InferOk}; use crate::traits; use hir::def_id::{DefId, LocalDefId}; use hir::{HirId, OpaqueTyOrigin}; @@ -101,44 +101,46 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let process = |a: Ty<'tcx>, b: Ty<'tcx>| match *a.kind() { ty::Opaque(def_id, substs) if def_id.is_local() => { let def_id = def_id.expect_local(); - let origin = if self.defining_use_anchor.is_some() { - // Check that this is `impl Trait` type is - // declared by `parent_def_id` -- i.e., one whose - // value we are inferring. At present, this is - // always true during the first phase of - // type-check, but not always true later on during - // NLL. Once we support named opaque types more fully, - // this same scenario will be able to arise during all phases. - // - // Here is an example using type alias `impl Trait` - // that indicates the distinction we are checking for: - // - // ```rust - // mod a { - // pub type Foo = impl Iterator; - // pub fn make_foo() -> Foo { .. } - // } - // - // mod b { - // fn foo() -> a::Foo { a::make_foo() } - // } - // ``` - // - // Here, the return type of `foo` references an - // `Opaque` indeed, but not one whose value is - // presently being inferred. You can get into a - // similar situation with closure return types - // today: - // - // ```rust - // fn foo() -> impl Iterator { .. } - // fn bar() { - // let x = || foo(); // returns the Opaque assoc with `foo` - // } - // ``` - self.opaque_type_origin(def_id, cause.span)? - } else { - self.opaque_ty_origin_unchecked(def_id, cause.span) + let origin = match self.defining_use_anchor { + DefiningAnchor::Bind(_) => { + // Check that this is `impl Trait` type is + // declared by `parent_def_id` -- i.e., one whose + // value we are inferring. At present, this is + // always true during the first phase of + // type-check, but not always true later on during + // NLL. Once we support named opaque types more fully, + // this same scenario will be able to arise during all phases. + // + // Here is an example using type alias `impl Trait` + // that indicates the distinction we are checking for: + // + // ```rust + // mod a { + // pub type Foo = impl Iterator; + // pub fn make_foo() -> Foo { .. } + // } + // + // mod b { + // fn foo() -> a::Foo { a::make_foo() } + // } + // ``` + // + // Here, the return type of `foo` references an + // `Opaque` indeed, but not one whose value is + // presently being inferred. You can get into a + // similar situation with closure return types + // today: + // + // ```rust + // fn foo() -> impl Iterator { .. } + // fn bar() { + // let x = || foo(); // returns the Opaque assoc with `foo` + // } + // ``` + self.opaque_type_origin(def_id, cause.span)? + } + DefiningAnchor::Bubble => self.opaque_ty_origin_unchecked(def_id, cause.span), + DefiningAnchor::Error => return None, }; if let ty::Opaque(did2, _) = *b.kind() { // We could accept this, but there are various ways to handle this situation, and we don't @@ -392,22 +394,17 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ); concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { - op: |r| { - self.member_constraint( - opaque_type_key.def_id, - span, - concrete_ty, - r, - &choice_regions, - ) - }, + op: |r| self.member_constraint(opaque_type_key, span, concrete_ty, r, &choice_regions), }); } #[instrument(skip(self), level = "trace")] pub fn opaque_type_origin(&self, def_id: LocalDefId, span: Span) -> Option<OpaqueTyOrigin> { let opaque_hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); - let parent_def_id = self.defining_use_anchor?; + let parent_def_id = match self.defining_use_anchor { + DefiningAnchor::Bubble | DefiningAnchor::Error => return None, + DefiningAnchor::Bind(bind) => bind, + }; let item_kind = &self.tcx.hir().expect_item(def_id).kind; let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item_kind else { diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs index b897de7315a..b2decd64f0f 100644 --- a/compiler/rustc_infer/src/infer/outlives/env.rs +++ b/compiler/rustc_infer/src/infer/outlives/env.rs @@ -1,6 +1,7 @@ use crate::infer::free_regions::FreeRegionMap; use crate::infer::{GenericKind, InferCtxt}; use crate::traits::query::OutlivesBound; +use rustc_data_structures::fx::FxIndexSet; use rustc_middle::ty::{self, ReEarlyBound, ReFree, ReVar, Region}; use super::explicit_outlives_bounds; @@ -53,7 +54,8 @@ pub struct OutlivesEnvironment<'tcx> { /// "Region-bound pairs" tracks outlives relations that are known to /// be true, either because of explicit where-clauses like `T: 'a` or /// because of implied bounds. -pub type RegionBoundPairs<'tcx> = Vec<(Region<'tcx>, GenericKind<'tcx>)>; +pub type RegionBoundPairs<'tcx> = + FxIndexSet<ty::OutlivesPredicate<GenericKind<'tcx>, Region<'tcx>>>; impl<'a, 'tcx> OutlivesEnvironment<'tcx> { pub fn new(param_env: ty::ParamEnv<'tcx>) -> Self { @@ -97,10 +99,12 @@ impl<'a, 'tcx> OutlivesEnvironment<'tcx> { debug!("add_outlives_bounds: outlives_bound={:?}", outlives_bound); match outlives_bound { OutlivesBound::RegionSubParam(r_a, param_b) => { - self.region_bound_pairs.push((r_a, GenericKind::Param(param_b))); + self.region_bound_pairs + .insert(ty::OutlivesPredicate(GenericKind::Param(param_b), r_a)); } OutlivesBound::RegionSubProjection(r_a, projection_b) => { - self.region_bound_pairs.push((r_a, GenericKind::Projection(projection_b))); + self.region_bound_pairs + .insert(ty::OutlivesPredicate(GenericKind::Projection(projection_b), r_a)); } OutlivesBound::RegionSubRegion(r_a, r_b) => { if let (ReEarlyBound(_) | ReFree(_), ReVar(vid_b)) = (r_a.kind(), r_b.kind()) { diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index a57971bfb69..ad052f58ca8 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -111,6 +111,9 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { std::mem::take(&mut self.inner.borrow_mut().region_obligations) } + /// NOTE: Prefer using [`InferCtxt::check_region_obligations_and_report_errors`] + /// instead of calling this directly. + /// /// Process the region obligations that must be proven (during /// `regionck`) for the given `body_id`, given information about /// the region bounds in scope and so forth. This function must be @@ -162,6 +165,9 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { } } + /// Processes registered region obliations and resolves regions, reporting + /// any errors if any were raised. Prefer using this function over manually + /// calling `resolve_regions_and_report_errors`. pub fn check_region_obligations_and_report_errors( &self, generic_param_scope: LocalDefId, diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index faa0a18f93d..c7d7ef40d9d 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -6,7 +6,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, Subst}; -use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt}; +use rustc_middle::ty::{self, EarlyBinder, OutlivesPredicate, Ty, TyCtxt}; use smallvec::smallvec; @@ -259,16 +259,17 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { // The problem is that the type of `x` is `&'a A`. To be // well-formed, then, A must outlive `'a`, but we don't know that // this holds from first principles. - let from_region_bound_pairs = self.region_bound_pairs.iter().filter_map(|&(r, p)| { - debug!( - "declared_generic_bounds_from_env_for_erased_ty: region_bound_pair = {:?}", - (r, p) - ); - let p_ty = p.to_ty(tcx); - let erased_p_ty = self.tcx.erase_regions(p_ty); - (erased_p_ty == erased_ty) - .then_some(ty::Binder::dummy(ty::OutlivesPredicate(p.to_ty(tcx), r))) - }); + let from_region_bound_pairs = + self.region_bound_pairs.iter().filter_map(|&OutlivesPredicate(p, r)| { + debug!( + "declared_generic_bounds_from_env_for_erased_ty: region_bound_pair = {:?}", + (r, p) + ); + let p_ty = p.to_ty(tcx); + let erased_p_ty = self.tcx.erase_regions(p_ty); + (erased_p_ty == erased_ty) + .then_some(ty::Binder::dummy(ty::OutlivesPredicate(p.to_ty(tcx), r))) + }); param_bounds .chain(from_region_bound_pairs) diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index 551f398e0c2..0d4472a1cfd 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -12,7 +12,6 @@ use rustc_data_structures::intern::Interned; use rustc_data_structures::sync::Lrc; use rustc_data_structures::undo_log::UndoLogs; use rustc_data_structures::unify as ut; -use rustc_hir::def_id::LocalDefId; use rustc_index::vec::IndexVec; use rustc_middle::infer::unify_key::{RegionVidKey, UnifiedRegion}; use rustc_middle::ty::ReStatic; @@ -533,7 +532,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { pub fn member_constraint( &mut self, - opaque_type_def_id: LocalDefId, + key: ty::OpaqueTypeKey<'tcx>, definition_span: Span, hidden_ty: Ty<'tcx>, member_region: ty::Region<'tcx>, @@ -546,7 +545,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { } self.data.member_constraints.push(MemberConstraint { - opaque_type_def_id, + key, definition_span, hidden_ty, member_region, diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index 674c75fdee5..f5a1edf6d81 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -11,7 +11,7 @@ pub fn anonymize_predicate<'tcx>( tcx: TyCtxt<'tcx>, pred: ty::Predicate<'tcx>, ) -> ty::Predicate<'tcx> { - let new = tcx.anonymize_late_bound_regions(pred.kind()); + let new = tcx.anonymize_bound_vars(pred.kind()); tcx.reuse_or_mk_predicate(pred, new) } @@ -145,7 +145,12 @@ impl<'tcx> Elaborator<'tcx> { // Get predicates declared on the trait. let predicates = tcx.super_predicates_of(data.def_id()); - let obligations = predicates.predicates.iter().map(|&(pred, _)| { + let obligations = predicates.predicates.iter().map(|&(mut pred, _)| { + // when parent predicate is non-const, elaborate it to non-const predicates. + if data.constness == ty::BoundConstness::NotConst { + pred = pred.without_const(tcx); + } + predicate_obligation( pred.subst_supertrait(tcx, &bound_predicate.rebind(data.trait_ref)), obligation.param_env, @@ -329,7 +334,7 @@ pub fn transitive_bounds_that_define_assoc_type<'tcx>( std::iter::from_fn(move || { while let Some(trait_ref) = stack.pop() { - let anon_trait_ref = tcx.anonymize_late_bound_regions(trait_ref); + let anon_trait_ref = tcx.anonymize_bound_vars(trait_ref); if visited.insert(anon_trait_ref) { let super_predicates = tcx.super_predicates_that_define_assoc_type(( trait_ref.def_id(), diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 6c7ddb4531e..94f81b66077 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -329,6 +329,8 @@ pub fn create_compiler_and_run<R>(config: Config, f: impl FnOnce(&Compiler) -> R }) } +// JUSTIFICATION: before session exists, only config +#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R { tracing::trace!("run_compiler"); util::run_in_thread_pool_with_globals( diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 334a595a88a..8f083591786 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -210,7 +210,7 @@ pub fn register_plugins<'a>( let mut lint_store = rustc_lint::new_lint_store( sess.opts.unstable_opts.no_interleave_lints, - sess.unstable_options(), + sess.enable_internal_lints(), ); register_lints(sess, &mut lint_store); diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 9c0b534798e..a9fdfa24141 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -1,3 +1,4 @@ +#![cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] use crate::interface::parse_cfgspecs; use rustc_data_structures::fx::FxHashSet; @@ -10,7 +11,7 @@ use rustc_session::config::{ }; use rustc_session::config::{ BranchProtection, Externs, OomStrategy, OutputType, OutputTypes, PAuthKey, PacRet, - SymbolManglingVersion, WasiExecModel, + ProcMacroExecutionStrategy, SymbolManglingVersion, WasiExecModel, }; use rustc_session::config::{CFGuard, ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath}; use rustc_session::lint::Level; @@ -669,6 +670,7 @@ fn test_unstable_options_tracking_hash() { untracked!(ls, true); untracked!(macro_backtrace, true); untracked!(meta_stats, true); + untracked!(mir_pretty_relative_line_numbers, true); untracked!(nll_facts, true); untracked!(no_analysis, true); untracked!(no_interleave_lints, true); @@ -683,6 +685,7 @@ fn test_unstable_options_tracking_hash() { untracked!(print_mono_items, Some(String::from("abc"))); untracked!(print_type_sizes, true); untracked!(proc_macro_backtrace, true); + untracked!(proc_macro_execution_strategy, ProcMacroExecutionStrategy::CrossThread); untracked!(query_dep_graph, true); untracked!(save_analysis, true); untracked!(self_profile, SwitchWithOptPath::Enabled(None)); @@ -718,6 +721,7 @@ fn test_unstable_options_tracking_hash() { tracked!(asm_comments, true); tracked!(assume_incomplete_release, true); tracked!(binary_dep_depinfo, true); + tracked!(box_noalias, Some(false)); tracked!( branch_protection, Some(BranchProtection { @@ -732,8 +736,10 @@ fn test_unstable_options_tracking_hash() { tracked!(debug_macros, true); tracked!(dep_info_omit_d_target, true); tracked!(drop_tracking, true); + tracked!(export_executable_symbols, true); tracked!(dual_proc_macros, true); tracked!(dwarf_version, Some(5)); + tracked!(emit_thin_lto, false); tracked!(fewer_names, Some(true)); tracked!(force_unstable_if_unmarked, true); tracked!(fuel, Some(("abc".to_string(), 99))); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 97856ecf22c..4c64e679b95 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -559,6 +559,8 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec<C // Only check command line flags if present. If no types are specified by // command line, then reuse the empty `base` Vec to hold the types that // will be found in crate attributes. + // JUSTIFICATION: before wrapper fn is available + #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] let mut base = session.opts.crate_types.clone(); if base.is_empty() { base.extend(attr_types); diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index a0472f98d72..ca3e6ce4d60 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1974,7 +1974,7 @@ impl KeywordIdents { for tt in tokens.into_trees() { match tt { // Only report non-raw idents. - TokenTree::Token(token) => { + TokenTree::Token(token, _) => { if let Some((ident, false)) = token.ident() { self.check_ident_token(cx, UnderMacro(true), ident); } @@ -2858,9 +2858,10 @@ impl ClashingExternDeclarations { let a_poly_sig = a.fn_sig(tcx); let b_poly_sig = b.fn_sig(tcx); - // As we don't compare regions, skip_binder is fine. - let a_sig = a_poly_sig.skip_binder(); - let b_sig = b_poly_sig.skip_binder(); + // We don't compare regions, but leaving bound regions around ICEs, so + // we erase them. + let a_sig = tcx.erase_late_bound_regions(a_poly_sig); + let b_sig = tcx.erase_late_bound_regions(b_poly_sig); (a_sig.abi, a_sig.unsafety, a_sig.c_variadic) == (b_sig.abi, b_sig.unsafety, b_sig.c_variadic) diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index b4b472fe2df..04ac50f1d48 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -861,10 +861,10 @@ pub trait LintContext: Sized { if let Some(positional_arg) = positional_arg { let msg = format!("this formatting argument uses named argument `{}` by position", name); db.span_label(positional_arg, msg); - db.span_suggestion_verbose( + db.span_suggestion_verbose( positional_arg, "use the named argument by name to avoid ambiguity", - format!("{{{}}}", name), + name, Applicability::MaybeIncorrect, ); } diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index b0a7a87fda4..d13711c3ab5 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -90,9 +90,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> fn visit_foreign_item(&mut self, it: &'a ast::ForeignItem) { self.with_lint_attrs(it.id, &it.attrs, |cx| { - run_early_pass!(cx, check_foreign_item, it); ast_visit::walk_foreign_item(cx, it); - run_early_pass!(cx, check_foreign_item_post, it); }) } @@ -104,7 +102,6 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> } fn visit_anon_const(&mut self, c: &'a ast::AnonConst) { - run_early_pass!(self, check_anon_const, c); self.check_id(c.id); ast_visit::walk_anon_const(self, c); } @@ -154,22 +151,17 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> self.check_id(closure_id); } } - - run_early_pass!(self, check_fn_post, fk, span, id); } fn visit_variant_data(&mut self, s: &'a ast::VariantData) { - run_early_pass!(self, check_struct_def, s); if let Some(ctor_hir_id) = s.ctor_id() { self.check_id(ctor_hir_id); } ast_visit::walk_struct_def(self, s); - run_early_pass!(self, check_struct_def_post, s); } fn visit_field_def(&mut self, s: &'a ast::FieldDef) { self.with_lint_attrs(s.id, &s.attrs, |cx| { - run_early_pass!(cx, check_field_def, s); ast_visit::walk_field_def(cx, s); }) } @@ -178,7 +170,6 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> self.with_lint_attrs(v.id, &v.attrs, |cx| { run_early_pass!(cx, check_variant, v); ast_visit::walk_variant(cx, v); - run_early_pass!(cx, check_variant_post, v); }) } @@ -203,7 +194,6 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> run_early_pass!(self, check_block, b); self.check_id(b.id); ast_visit::walk_block(self, b); - run_early_pass!(self, check_block_post, b); } fn visit_arm(&mut self, a: &'a ast::Arm) { @@ -214,8 +204,6 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> } fn visit_expr_post(&mut self, e: &'a ast::Expr) { - run_early_pass!(self, check_expr_post, e); - // Explicitly check for lints associated with 'closure_id', since // it does not have a corresponding AST node match e.kind { @@ -242,7 +230,6 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> } fn visit_where_predicate(&mut self, p: &'a ast::WherePredicate) { - run_early_pass!(self, check_where_predicate, p); ast_visit::walk_where_predicate(self, p); } @@ -256,23 +243,19 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> ast_visit::AssocCtxt::Trait => { run_early_pass!(cx, check_trait_item, item); ast_visit::walk_assoc_item(cx, item, ctxt); - run_early_pass!(cx, check_trait_item_post, item); } ast_visit::AssocCtxt::Impl => { run_early_pass!(cx, check_impl_item, item); ast_visit::walk_assoc_item(cx, item, ctxt); - run_early_pass!(cx, check_impl_item_post, item); } }); } fn visit_lifetime(&mut self, lt: &'a ast::Lifetime, _: ast_visit::LifetimeCtxt) { - run_early_pass!(self, check_lifetime, lt); self.check_id(lt.id); } fn visit_path(&mut self, p: &'a ast::Path, id: ast::NodeId) { - run_early_pass!(self, check_path, p, id); self.check_id(id); ast_visit::walk_path(self, p); } diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 738f475983e..c26d7824758 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -51,20 +51,6 @@ fn typeck_results_of_method_fn<'tcx>( cx: &LateContext<'tcx>, expr: &Expr<'_>, ) -> Option<(Span, DefId, ty::subst::SubstsRef<'tcx>)> { - // FIXME(rustdoc): Lints which use this function use typecheck results which can cause - // `rustdoc` to error if there are resolution failures. - // - // As internal lints are currently always run if there are `unstable_options`, they are added - // to the lint store of rustdoc. Internal lints are also not used via the `lint_mod` query. - // Crate lints run outside of a query so rustdoc currently doesn't disable them. - // - // Instead of relying on this, either change crate lints to a query disabled by rustdoc, only - // run internal lints if the user is explicitly opting in or figure out a different way to - // avoid running lints for rustdoc. - if cx.tcx.sess.opts.actually_rustdoc { - return None; - } - match expr.kind { ExprKind::MethodCall(segment, _, _) if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) => @@ -446,3 +432,38 @@ impl LateLintPass<'_> for Diagnostics { } } } + +declare_tool_lint! { + pub rustc::BAD_OPT_ACCESS, + Deny, + "prevent using options by field access when there is a wrapper function", + report_in_external_macro: true +} + +declare_lint_pass!(BadOptAccess => [ BAD_OPT_ACCESS ]); + +impl LateLintPass<'_> for BadOptAccess { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + let ExprKind::Field(base, target) = expr.kind else { return }; + let Some(adt_def) = cx.typeck_results().expr_ty(base).ty_adt_def() else { return }; + // Skip types without `#[rustc_lint_opt_ty]` - only so that the rest of the lint can be + // avoided. + if !cx.tcx.has_attr(adt_def.did(), sym::rustc_lint_opt_ty) { + return; + } + + for field in adt_def.all_fields() { + if field.name == target.name && + let Some(attr) = cx.tcx.get_attr(field.did, sym::rustc_lint_opt_deny_field_access) && + let Some(items) = attr.meta_item_list() && + let Some(item) = items.first() && + let Some(literal) = item.literal() && + let ast::LitKind::Str(val, _) = literal.kind + { + cx.struct_span_lint(BAD_OPT_ACCESS, expr.span, |lint| { + lint.build(val.as_str()).emit(); } + ); + } + } + } +} diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index afb18451cf3..a329b37519d 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -81,7 +81,6 @@ impl<'tcx, T: LateLintPass<'tcx>> LateContextAndPass<'tcx, T> { fn process_mod(&mut self, m: &'tcx hir::Mod<'tcx>, s: Span, n: hir::HirId) { lint_callback!(self, check_mod, m, s, n); hir_visit::walk_mod(self, m, n); - lint_callback!(self, check_mod_post, m, s, n); } } @@ -118,7 +117,6 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { self.with_lint_attrs(param.hir_id, |cx| { - lint_callback!(cx, check_param, param); hir_visit::walk_param(cx, param); }); } @@ -151,7 +149,6 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas cx.with_param_env(it.hir_id(), |cx| { lint_callback!(cx, check_foreign_item, it); hir_visit::walk_foreign_item(cx, it); - lint_callback!(cx, check_foreign_item_post, it); }); }) } @@ -193,7 +190,6 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas let body = self.context.tcx.hir().body(body_id); lint_callback!(self, check_fn, fk, decl, body, span, id); hir_visit::walk_fn(self, fk, decl, body_id, span, id); - lint_callback!(self, check_fn_post, fk, decl, body, span, id); self.context.enclosing_body = old_enclosing_body; self.context.cached_typeck_results.set(old_cached_typeck_results); } @@ -208,7 +204,6 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas ) { lint_callback!(self, check_struct_def, s); hir_visit::walk_struct_def(self, s); - lint_callback!(self, check_struct_def_post, s); } fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) { @@ -227,7 +222,6 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas self.with_lint_attrs(v.id, |cx| { lint_callback!(cx, check_variant, v); hir_visit::walk_variant(cx, v, g, item_id); - lint_callback!(cx, check_variant_post, v); }) } @@ -237,14 +231,9 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas } fn visit_infer(&mut self, inf: &'tcx hir::InferArg) { - lint_callback!(self, check_infer, inf); hir_visit::walk_inf(self, inf); } - fn visit_name(&mut self, sp: Span, name: Symbol) { - lint_callback!(self, check_name, sp, name); - } - fn visit_mod(&mut self, m: &'tcx hir::Mod<'tcx>, s: Span, n: hir::HirId) { if !self.context.only_module { self.process_mod(m, s, n); @@ -280,7 +269,6 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas } fn visit_where_predicate(&mut self, p: &'tcx hir::WherePredicate<'tcx>) { - lint_callback!(self, check_where_predicate, p); hir_visit::walk_where_predicate(self, p); } @@ -300,7 +288,6 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas cx.with_param_env(trait_item.hir_id(), |cx| { lint_callback!(cx, check_trait_item, trait_item); hir_visit::walk_trait_item(cx, trait_item); - lint_callback!(cx, check_trait_item_post, trait_item); }); }); self.context.generics = generics; @@ -320,7 +307,6 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas } fn visit_lifetime(&mut self, lt: &'tcx hir::Lifetime) { - lint_callback!(self, check_lifetime, lt); hir_visit::walk_lifetime(self, lt); } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 8726d36498b..ae2088fbeb6 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -75,7 +75,7 @@ use rustc_middle::ty::TyCtxt; use rustc_session::lint::builtin::{ BARE_TRAIT_OBJECTS, ELIDED_LIFETIMES_IN_PATHS, EXPLICIT_OUTLIVES_REQUIREMENTS, }; -use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::symbol::Ident; use rustc_span::Span; use array_into_iter::ArrayIntoIter; @@ -509,8 +509,14 @@ fn register_internals(store: &mut LintStore) { store.register_late_pass(|| Box::new(TyTyKind)); store.register_lints(&Diagnostics::get_lints()); store.register_late_pass(|| Box::new(Diagnostics)); + store.register_lints(&BadOptAccess::get_lints()); + store.register_late_pass(|| Box::new(BadOptAccess)); store.register_lints(&PassByValue::get_lints()); store.register_late_pass(|| Box::new(PassByValue)); + // FIXME(davidtwco): deliberately do not include `UNTRANSLATABLE_DIAGNOSTIC` and + // `DIAGNOSTIC_OUTSIDE_OF_IMPL` here because `-Wrustc::internal` is provided to every crate and + // these lints will trigger all of the time - change this once migration to diagnostic structs + // and translation is completed store.register_group( false, "rustc::internal", @@ -523,6 +529,7 @@ fn register_internals(store: &mut LintStore) { LintId::of(LINT_PASS_IMPL_WITHOUT_MACRO), LintId::of(USAGE_OF_QUALIFIED_TY), LintId::of(EXISTING_DOC_KEYWORD), + LintId::of(BAD_OPT_ACCESS), ], ); } diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs index b1b4229b1f7..cb7bd407ed4 100644 --- a/compiler/rustc_lint/src/passes.rs +++ b/compiler/rustc_lint/src/passes.rs @@ -5,23 +5,19 @@ use rustc_data_structures::sync; use rustc_hir as hir; use rustc_session::lint::builtin::HardwiredLints; use rustc_session::lint::LintPass; -use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::symbol::Ident; use rustc_span::Span; #[macro_export] macro_rules! late_lint_methods { ($macro:path, $args:tt, [$hir:tt]) => ( $macro!($args, [$hir], [ - fn check_param(a: &$hir hir::Param<$hir>); fn check_body(a: &$hir hir::Body<$hir>); fn check_body_post(a: &$hir hir::Body<$hir>); - fn check_name(a: Span, b: Symbol); fn check_crate(); fn check_crate_post(); fn check_mod(a: &$hir hir::Mod<$hir>, b: Span, c: hir::HirId); - fn check_mod_post(a: &$hir hir::Mod<$hir>, b: Span, c: hir::HirId); fn check_foreign_item(a: &$hir hir::ForeignItem<$hir>); - fn check_foreign_item_post(a: &$hir hir::ForeignItem<$hir>); fn check_item(a: &$hir hir::Item<$hir>); fn check_item_post(a: &$hir hir::Item<$hir>); fn check_local(a: &$hir hir::Local<$hir>); @@ -33,11 +29,8 @@ macro_rules! late_lint_methods { fn check_expr(a: &$hir hir::Expr<$hir>); fn check_expr_post(a: &$hir hir::Expr<$hir>); fn check_ty(a: &$hir hir::Ty<$hir>); - fn check_infer(a: &$hir hir::InferArg); - fn check_generic_arg(a: &$hir hir::GenericArg<$hir>); fn check_generic_param(a: &$hir hir::GenericParam<$hir>); fn check_generics(a: &$hir hir::Generics<$hir>); - fn check_where_predicate(a: &$hir hir::WherePredicate<$hir>); fn check_poly_trait_ref(a: &$hir hir::PolyTraitRef<$hir>, b: hir::TraitBoundModifier); fn check_fn( a: rustc_hir::intravisit::FnKind<$hir>, @@ -45,23 +38,12 @@ macro_rules! late_lint_methods { c: &$hir hir::Body<$hir>, d: Span, e: hir::HirId); - fn check_fn_post( - a: rustc_hir::intravisit::FnKind<$hir>, - b: &$hir hir::FnDecl<$hir>, - c: &$hir hir::Body<$hir>, - d: Span, - e: hir::HirId - ); fn check_trait_item(a: &$hir hir::TraitItem<$hir>); - fn check_trait_item_post(a: &$hir hir::TraitItem<$hir>); fn check_impl_item(a: &$hir hir::ImplItem<$hir>); fn check_impl_item_post(a: &$hir hir::ImplItem<$hir>); fn check_struct_def(a: &$hir hir::VariantData<$hir>); - fn check_struct_def_post(a: &$hir hir::VariantData<$hir>); fn check_field_def(a: &$hir hir::FieldDef<$hir>); fn check_variant(a: &$hir hir::Variant<$hir>); - fn check_variant_post(a: &$hir hir::Variant<$hir>); - fn check_lifetime(a: &$hir hir::Lifetime); fn check_path(a: &$hir hir::Path<$hir>, b: hir::HirId); fn check_attribute(a: &$hir ast::Attribute); @@ -161,44 +143,25 @@ macro_rules! early_lint_methods { fn check_ident(a: Ident); fn check_crate(a: &ast::Crate); fn check_crate_post(a: &ast::Crate); - fn check_foreign_item(a: &ast::ForeignItem); - fn check_foreign_item_post(a: &ast::ForeignItem); fn check_item(a: &ast::Item); fn check_item_post(a: &ast::Item); fn check_local(a: &ast::Local); fn check_block(a: &ast::Block); - fn check_block_post(a: &ast::Block); fn check_stmt(a: &ast::Stmt); fn check_arm(a: &ast::Arm); fn check_pat(a: &ast::Pat); - fn check_anon_const(a: &ast::AnonConst); fn check_pat_post(a: &ast::Pat); fn check_expr(a: &ast::Expr); - fn check_expr_post(a: &ast::Expr); fn check_ty(a: &ast::Ty); fn check_generic_arg(a: &ast::GenericArg); fn check_generic_param(a: &ast::GenericParam); fn check_generics(a: &ast::Generics); - fn check_where_predicate(a: &ast::WherePredicate); fn check_poly_trait_ref(a: &ast::PolyTraitRef, b: &ast::TraitBoundModifier); fn check_fn(a: rustc_ast::visit::FnKind<'_>, c: Span, d_: ast::NodeId); - fn check_fn_post( - a: rustc_ast::visit::FnKind<'_>, - c: Span, - d: ast::NodeId - ); fn check_trait_item(a: &ast::AssocItem); - fn check_trait_item_post(a: &ast::AssocItem); fn check_impl_item(a: &ast::AssocItem); - fn check_impl_item_post(a: &ast::AssocItem); - fn check_struct_def(a: &ast::VariantData); - fn check_struct_def_post(a: &ast::VariantData); - fn check_field_def(a: &ast::FieldDef); fn check_variant(a: &ast::Variant); - fn check_variant_post(a: &ast::Variant); - fn check_lifetime(a: &ast::Lifetime); - fn check_path(a: &ast::Path, b: ast::NodeId); fn check_attribute(a: &ast::Attribute); fn check_mac_def(a: &ast::MacroDef, b: ast::NodeId); fn check_mac(a: &ast::MacCall); diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index 7729ec6bef4..62ef5804dce 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -4,6 +4,29 @@ use std::fmt::Display; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; +const OPTIONAL_COMPONENTS: &[&str] = &[ + "x86", + "arm", + "aarch64", + "amdgpu", + "avr", + "m68k", + "mips", + "powerpc", + "systemz", + "jsbackend", + "webassembly", + "msp430", + "sparc", + "nvptx", + "hexagon", + "riscv", + "bpf", +]; + +const REQUIRED_COMPONENTS: &[&str] = + &["ipo", "bitreader", "bitwriter", "linker", "asmparser", "lto", "coverage", "instrumentation"]; + fn detect_llvm_link() -> (&'static str, &'static str) { // Force the link mode we want, preferring static by default, but // possibly overridden by `configure --enable-llvm-link-shared`. @@ -76,6 +99,10 @@ fn output(cmd: &mut Command) -> String { } fn main() { + for component in REQUIRED_COMPONENTS.iter().chain(OPTIONAL_COMPONENTS.iter()) { + println!("cargo:rustc-check-cfg=values(llvm_component,\"{}\")", component); + } + if tracked_env_var_os("RUST_CHECK").is_some() { // If we're just running `check`, there's no need for LLVM to be built. return; @@ -131,42 +158,11 @@ fn main() { let host = env::var("HOST").expect("HOST was not set"); let is_crossed = target != host; - let optional_components = &[ - "x86", - "arm", - "aarch64", - "amdgpu", - "avr", - "m68k", - "mips", - "powerpc", - "systemz", - "jsbackend", - "webassembly", - "msp430", - "sparc", - "nvptx", - "hexagon", - "riscv", - "bpf", - ]; - - let required_components = &[ - "ipo", - "bitreader", - "bitwriter", - "linker", - "asmparser", - "lto", - "coverage", - "instrumentation", - ]; - let components = output(Command::new(&llvm_config).arg("--components")); let mut components = components.split_whitespace().collect::<Vec<_>>(); - components.retain(|c| optional_components.contains(c) || required_components.contains(c)); + components.retain(|c| OPTIONAL_COMPONENTS.contains(c) || REQUIRED_COMPONENTS.contains(c)); - for component in required_components { + for component in REQUIRED_COMPONENTS { if !components.contains(component) { panic!("require llvm component {} but wasn't found", component); } diff --git a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h index 9fe84a6309b..015c1c52bef 100644 --- a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h +++ b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h @@ -85,6 +85,12 @@ enum LLVMRustAttribute { NoUndef = 33, SanitizeMemTag = 34, NoCfCheck = 35, + ShadowCallStack = 36, + AllocSize = 37, +#if LLVM_VERSION_GE(15, 0) + AllocatedPointer = 38, + AllocAlign = 39, +#endif }; typedef struct OpaqueRustString *RustStringRef; diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index be8fbf7677b..0a6bd49992d 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -34,6 +34,7 @@ #include "llvm/Transforms/Utils/AddDiscriminators.h" #include "llvm/Transforms/Utils/FunctionImportUtils.h" #include "llvm/LTO/LTO.h" +#include "llvm/Bitcode/BitcodeWriterPass.h" #include "llvm-c/Transforms/PassManagerBuilder.h" #include "llvm/Transforms/Instrumentation.h" @@ -89,23 +90,6 @@ extern "C" void LLVMTimeTraceProfilerFinish(const char* FileName) { timeTraceProfilerCleanup(); } -enum class LLVMRustPassKind { - Other, - Function, - Module, -}; - -static LLVMRustPassKind toRust(PassKind Kind) { - switch (Kind) { - case PT_Function: - return LLVMRustPassKind::Function; - case PT_Module: - return LLVMRustPassKind::Module; - default: - return LLVMRustPassKind::Other; - } -} - extern "C" LLVMPassRef LLVMRustFindAndCreatePass(const char *PassName) { #if LLVM_VERSION_LT(15, 0) StringRef SR(PassName); @@ -171,12 +155,6 @@ extern "C" LLVMPassRef LLVMRustCreateHWAddressSanitizerPass(bool Recover) { #endif } -extern "C" LLVMRustPassKind LLVMRustPassKind(LLVMPassRef RustPass) { - assert(RustPass); - Pass *Pass = unwrap(RustPass); - return toRust(Pass->getPassKind()); -} - extern "C" void LLVMRustAddPass(LLVMPassManagerRef PMR, LLVMPassRef RustPass) { #if LLVM_VERSION_LT(15, 0) assert(RustPass); @@ -1603,28 +1581,6 @@ LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data, LLVMModuleRef M, return true; } -extern "C" typedef void (*LLVMRustModuleNameCallback)(void*, // payload - const char*, // importing module name - const char*); // imported module name - -// Calls `module_name_callback` for each module import done by ThinLTO. -// The callback is provided with regular null-terminated C strings. -extern "C" void -LLVMRustGetThinLTOModules(const LLVMRustThinLTOData *data, - LLVMRustModuleNameCallback module_name_callback, - void* callback_payload) { - for (const auto& importing_module : data->ImportLists) { - const std::string importing_module_id = importing_module.getKey().str(); - const auto& imports = importing_module.getValue(); - for (const auto& imported_module : imports) { - const std::string imported_module_id = imported_module.getKey().str(); - module_name_callback(callback_payload, - importing_module_id.c_str(), - imported_module_id.c_str()); - } - } -} - // This struct and various functions are sort of a hack right now, but the // problem is that we've got in-memory LLVM modules after we generate and // optimize all codegen-units for one compilation in rustc. To be compatible @@ -1638,13 +1594,17 @@ struct LLVMRustThinLTOBuffer { }; extern "C" LLVMRustThinLTOBuffer* -LLVMRustThinLTOBufferCreate(LLVMModuleRef M) { +LLVMRustThinLTOBufferCreate(LLVMModuleRef M, bool is_thin) { auto Ret = std::make_unique<LLVMRustThinLTOBuffer>(); { raw_string_ostream OS(Ret->data); { legacy::PassManager PM; - PM.add(createWriteThinLTOBitcodePass(OS)); + if (is_thin) { + PM.add(createWriteThinLTOBitcodePass(OS)); + } else { + PM.add(createBitcodeWriterPass(OS)); + } PM.run(*unwrap(M)); } } diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 2d35ee8976e..c333738ded4 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -6,6 +6,7 @@ #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Intrinsics.h" +#include "llvm/IR/IntrinsicsARM.h" #include "llvm/IR/Mangler.h" #include "llvm/Object/Archive.h" #include "llvm/Object/COFFImportFile.h" @@ -88,10 +89,6 @@ extern "C" char *LLVMRustGetLastError(void) { return Ret; } -extern "C" unsigned int LLVMRustGetInstructionCount(LLVMModuleRef M) { - return unwrap(M)->getInstructionCount(); -} - extern "C" void LLVMRustSetLastError(const char *Err) { free((void *)LastError); LastError = strdup(Err); @@ -232,6 +229,16 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) { return Attribute::NoUndef; case SanitizeMemTag: return Attribute::SanitizeMemTag; + case ShadowCallStack: + return Attribute::ShadowCallStack; + case AllocSize: + return Attribute::AllocSize; +#if LLVM_VERSION_GE(15, 0) + case AllocatedPointer: + return Attribute::AllocatedPointer; + case AllocAlign: + return Attribute::AllocAlign; +#endif } report_fatal_error("bad AttributeKind"); } @@ -294,6 +301,14 @@ extern "C" LLVMAttributeRef LLVMRustCreateStructRetAttr(LLVMContextRef C, LLVMTy return wrap(Attribute::getWithStructRetType(*unwrap(C), unwrap(Ty))); } +extern "C" LLVMAttributeRef LLVMRustCreateElementTypeAttr(LLVMContextRef C, LLVMTypeRef Ty) { +#if LLVM_VERSION_GE(15, 0) + return wrap(Attribute::get(*unwrap(C), Attribute::ElementType, unwrap(Ty))); +#else + report_fatal_error("Should not be needed on LLVM < 15"); +#endif +} + extern "C" LLVMAttributeRef LLVMRustCreateUWTableAttr(LLVMContextRef C, bool Async) { #if LLVM_VERSION_LT(15, 0) return wrap(Attribute::get(*unwrap(C), Attribute::UWTable)); @@ -303,6 +318,67 @@ extern "C" LLVMAttributeRef LLVMRustCreateUWTableAttr(LLVMContextRef C, bool Asy #endif } +extern "C" LLVMAttributeRef LLVMRustCreateAllocSizeAttr(LLVMContextRef C, uint32_t ElementSizeArg) { + return wrap(Attribute::getWithAllocSizeArgs(*unwrap(C), ElementSizeArg, None)); +} + +#if LLVM_VERSION_GE(15, 0) + +// These values **must** match ffi::AllocKindFlags. +// It _happens_ to match the LLVM values of llvm::AllocFnKind, +// but that's happenstance and we do explicit conversions before +// passing them to LLVM. +enum class LLVMRustAllocKindFlags : uint64_t { + Unknown = 0, + Alloc = 1, + Realloc = 1 << 1, + Free = 1 << 2, + Uninitialized = 1 << 3, + Zeroed = 1 << 4, + Aligned = 1 << 5, +}; + +static LLVMRustAllocKindFlags operator&(LLVMRustAllocKindFlags A, LLVMRustAllocKindFlags B) { + return static_cast<LLVMRustAllocKindFlags>(static_cast<uint64_t>(A) & + static_cast<uint64_t>(B)); +} + +static bool isSet(LLVMRustAllocKindFlags F) { return F != LLVMRustAllocKindFlags::Unknown; } + +static llvm::AllocFnKind allocKindFromRust(LLVMRustAllocKindFlags F) { + llvm::AllocFnKind AFK = llvm::AllocFnKind::Unknown; + if (isSet(F & LLVMRustAllocKindFlags::Alloc)) { + AFK |= llvm::AllocFnKind::Alloc; + } + if (isSet(F & LLVMRustAllocKindFlags::Realloc)) { + AFK |= llvm::AllocFnKind::Realloc; + } + if (isSet(F & LLVMRustAllocKindFlags::Free)) { + AFK |= llvm::AllocFnKind::Free; + } + if (isSet(F & LLVMRustAllocKindFlags::Uninitialized)) { + AFK |= llvm::AllocFnKind::Uninitialized; + } + if (isSet(F & LLVMRustAllocKindFlags::Zeroed)) { + AFK |= llvm::AllocFnKind::Zeroed; + } + if (isSet(F & LLVMRustAllocKindFlags::Aligned)) { + AFK |= llvm::AllocFnKind::Aligned; + } + return AFK; +} +#endif + +extern "C" LLVMAttributeRef LLVMRustCreateAllocKindAttr(LLVMContextRef C, uint64_t AllocKindArg) { +#if LLVM_VERSION_GE(15, 0) + return wrap(Attribute::get(*unwrap(C), Attribute::AllocKind, + static_cast<uint64_t>(allocKindFromRust(static_cast<LLVMRustAllocKindFlags>(AllocKindArg))))); +#else + report_fatal_error( + "allockind attributes are new in LLVM 15 and should not be used on older LLVMs"); +#endif +} + // Enable a fast-math flag // // https://llvm.org/docs/LangRef.html#fast-math-flags @@ -1458,11 +1534,6 @@ extern "C" void LLVMRustSetComdat(LLVMModuleRef M, LLVMValueRef V, } } -extern "C" void LLVMRustUnsetComdat(LLVMValueRef V) { - GlobalObject *GV = unwrap<GlobalObject>(V); - GV->setComdat(nullptr); -} - enum class LLVMRustLinkage { ExternalLinkage = 0, AvailableExternallyLinkage = 1, @@ -1881,3 +1952,16 @@ extern "C" LLVMValueRef LLVMGetAggregateElement(LLVMValueRef C, unsigned Idx) { return wrap(unwrap<Constant>(C)->getAggregateElement(Idx)); } #endif + +extern "C" int32_t LLVMRustGetElementTypeArgIndex(LLVMValueRef CallSite) { +#if LLVM_VERSION_GE(15, 0) + auto *CB = unwrap<CallBase>(CallSite); + switch (CB->getIntrinsicID()) { + case Intrinsic::arm_ldrex: + return 0; + case Intrinsic::arm_strex: + return 1; + } +#endif + return -1; +} diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index 5c5275b7cfb..6c9561925fe 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -212,7 +212,7 @@ impl DiagnosticDeriveBuilder { } NestedMeta::Meta(meta @ Meta::NameValue(_)) if !is_help_note_or_warn - && meta.path().segments.last().unwrap().ident.to_string() == "code" => + && meta.path().segments.last().unwrap().ident == "code" => { // don't error for valid follow-up attributes } diff --git a/compiler/rustc_macros/src/diagnostics/fluent.rs b/compiler/rustc_macros/src/diagnostics/fluent.rs index 1170d2b3c59..562d5e9f4d2 100644 --- a/compiler/rustc_macros/src/diagnostics/fluent.rs +++ b/compiler/rustc_macros/src/diagnostics/fluent.rs @@ -194,8 +194,8 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok let snake_name = Ident::new( // FIXME: should probably trim prefix, not replace all occurrences &name - .replace(&format!("{}-", res.ident).replace("_", "-"), "") - .replace("-", "_"), + .replace(&format!("{}-", res.ident).replace('_', "-"), "") + .replace('-', "_"), span, ); constants.extend(quote! { @@ -207,7 +207,7 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok }); for Attribute { id: Identifier { name: attr_name }, .. } in attributes { - let snake_name = Ident::new(&attr_name.replace("-", "_"), span); + let snake_name = Ident::new(&attr_name.replace('-', "_"), span); if !previous_attrs.insert(snake_name.clone()) { continue; } diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index a72bcb9a2db..2c1c84b0be2 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -67,7 +67,7 @@ //! //! ## What criterion to select on? //! -//! This a pretty tricky area of loading crates. Given a file, how do we know +//! This is a pretty tricky area of loading crates. Given a file, how do we know //! whether it's the right crate? Currently, the rules look along these lines: //! //! 1. Does the filename match an rlib/dylib pattern? That is to say, does the diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index f0874f8f2da..6b0b5ac7da9 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -41,7 +41,7 @@ use std::io; use std::iter::TrustedLen; use std::mem; use std::num::NonZeroUsize; -use std::path::PathBuf; +use std::path::Path; use tracing::debug; pub(super) use cstore_impl::provide; @@ -83,7 +83,7 @@ pub(crate) struct CrateMetadata { // --- Some data pre-decoded from the metadata blob, usually for performance --- /// NOTE(eddyb) we pass `'static` to a `'tcx` parameter because this - /// lifetime is only used behind `Lazy`, and therefore acts like a + /// lifetime is only used behind `LazyValue`, `LazyArray`, or `LazyTable`, and therefore acts like a /// universal (`for<'tcx>`), that is paired up with whichever `TyCtxt` /// is being used to decode those values. root: CrateRoot, @@ -951,6 +951,13 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { tcx.arena.alloc_from_iter(self.root.lib_features.decode(self)) } + /// Iterates over the stability implications in the given crate (when a `#[unstable]` attribute + /// has an `implied_by` meta item, then the mapping from the implied feature to the actual + /// feature is a stability implication). + fn get_stability_implications(self, tcx: TyCtxt<'tcx>) -> &'tcx [(Symbol, Symbol)] { + tcx.arena.alloc_from_iter(self.root.stability_implications.decode(self)) + } + /// Iterates over the language items in the given crate. fn get_lang_items(self, tcx: TyCtxt<'tcx>) -> &'tcx [(DefId, usize)] { tcx.arena.alloc_from_iter( @@ -1467,30 +1474,30 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { /// Proc macro crates don't currently export spans, so this function does not have /// to work for them. fn imported_source_files(self, sess: &Session) -> &'a [ImportedSourceFile] { + fn filter<'a>(sess: &Session, path: Option<&'a Path>) -> Option<&'a Path> { + path.filter(|_| { + // Only spend time on further checks if we have what to translate *to*. + sess.opts.real_rust_source_base_dir.is_some() + // Some tests need the translation to be always skipped. + && sess.opts.unstable_opts.translate_remapped_path_to_local_path + }) + .filter(|virtual_dir| { + // Don't translate away `/rustc/$hash` if we're still remapping to it, + // since that means we're still building `std`/`rustc` that need it, + // and we don't want the real path to leak into codegen/debuginfo. + !sess.opts.remap_path_prefix.iter().any(|(_from, to)| to == virtual_dir) + }) + } + // Translate the virtual `/rustc/$hash` prefix back to a real directory // that should hold actual sources, where possible. // // NOTE: if you update this, you might need to also update bootstrap's code for generating // the `rust-src` component in `Src::run` in `src/bootstrap/dist.rs`. let virtual_rust_source_base_dir = [ - option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR").map(PathBuf::from), - sess.opts.unstable_opts.simulate_remapped_rust_src_base.clone(), - ] - .into_iter() - .filter(|_| { - // Only spend time on further checks if we have what to translate *to*. - sess.opts.real_rust_source_base_dir.is_some() - // Some tests need the translation to be always skipped. - && sess.opts.unstable_opts.translate_remapped_path_to_local_path - }) - .flatten() - .filter(|virtual_dir| { - // Don't translate away `/rustc/$hash` if we're still remapping to it, - // since that means we're still building `std`/`rustc` that need it, - // and we don't want the real path to leak into codegen/debuginfo. - !sess.opts.remap_path_prefix.iter().any(|(_from, to)| to == virtual_dir) - }) - .collect::<Vec<_>>(); + filter(sess, option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR").map(Path::new)), + filter(sess, sess.opts.unstable_opts.simulate_remapped_rust_src_base.as_deref()), + ]; let try_to_translate_virtual_to_real = |name: &mut rustc_span::FileName| { debug!( @@ -1499,7 +1506,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { name, virtual_rust_source_base_dir, sess.opts.real_rust_source_base_dir, ); - for virtual_dir in &virtual_rust_source_base_dir { + for virtual_dir in virtual_rust_source_base_dir.iter().flatten() { if let Some(real_dir) = &sess.opts.real_rust_source_base_dir { if let rustc_span::FileName::Real(old_name) = name { if let rustc_span::RealFileName::Remapped { local_path: _, virtual_name } = diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 565eec18ea9..6bf237b8ed5 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -291,6 +291,9 @@ provide! { <'tcx> tcx, def_id, other, cdata, tcx.arena.alloc_slice(&result) } defined_lib_features => { cdata.get_lib_features(tcx) } + stability_implications => { + cdata.get_stability_implications(tcx).iter().copied().collect() + } is_intrinsic => { cdata.get_is_intrinsic(def_id.index) } defined_lang_items => { cdata.get_lang_items(tcx) } diagnostic_items => { cdata.get_diagnostic_items() } @@ -372,9 +375,13 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) { use std::collections::vec_deque::VecDeque; let mut visible_parent_map: DefIdMap<DefId> = Default::default(); - // This is a secondary visible_parent_map, storing the DefId of parents that re-export - // the child as `_`. Since we prefer parents that don't do this, merge this map at the - // end, only if we're missing any keys from the former. + // This is a secondary visible_parent_map, storing the DefId of + // parents that re-export the child as `_` or module parents + // which are `#[doc(hidden)]`. Since we prefer paths that don't + // do this, merge this map at the end, only if we're missing + // keys from the former. + // This is a rudimentary check that does not catch all cases, + // just the easiest. let mut fallback_map: DefIdMap<DefId> = Default::default(); // Issue 46112: We want the map to prefer the shortest @@ -409,6 +416,11 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) { return; } + if ty::util::is_doc_hidden(tcx, parent) { + fallback_map.insert(def_id, parent); + return; + } + match visible_parent_map.entry(def_id) { Entry::Occupied(mut entry) => { // If `child` is defined in crate `cnum`, ensure @@ -436,8 +448,9 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) { } } - // Fill in any missing entries with the (less preferable) path ending in `::_`. - // We still use this path in a diagnostic that suggests importing `::*`. + // Fill in any missing entries with the less preferable path. + // If this path re-exports the child as `_`, we still use this + // path in a diagnostic that suggests importing `::*`. for (child, parent) in fallback_map { visible_parent_map.entry(child).or_insert(parent); } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 8e973009777..f0886036899 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -538,6 +538,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let lib_features = self.encode_lib_features(); let lib_feature_bytes = self.position() - i; + // Encode the stability implications. + i = self.position(); + let stability_implications = self.encode_stability_implications(); + let stability_implications_bytes = self.position() - i; + // Encode the language items. i = self.position(); let lang_items = self.encode_lang_items(); @@ -686,6 +691,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { crate_deps, dylib_dependency_formats, lib_features, + stability_implications, lang_items, diagnostic_items, lang_items_missing, @@ -710,6 +716,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let computed_total_bytes = preamble_bytes + dep_bytes + lib_feature_bytes + + stability_implications_bytes + lang_item_bytes + diagnostic_item_bytes + native_lib_bytes @@ -761,6 +768,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { p("preamble", preamble_bytes); p("dep", dep_bytes); p("lib feature", lib_feature_bytes); + p("stability_implications", stability_implications_bytes); p("lang item", lang_item_bytes); p("diagnostic item", diagnostic_item_bytes); p("native lib", native_lib_bytes); @@ -1608,7 +1616,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { fn encode_info_for_anon_const(&mut self, id: hir::HirId) { let def_id = self.tcx.hir().local_def_id(id); debug!("EncodeContext::encode_info_for_anon_const({:?})", def_id); - let body_id = self.tcx.hir().body_owned_by(id); + let body_id = self.tcx.hir().body_owned_by(def_id); let const_data = self.encode_rendered_const_for_body(body_id); let qualifs = self.tcx.mir_const_qualif(def_id); @@ -1777,6 +1785,13 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.lazy_array(lib_features.to_vec()) } + fn encode_stability_implications(&mut self) -> LazyArray<(Symbol, Symbol)> { + empty_proc_macro!(self); + let tcx = self.tcx; + let implications = tcx.stability_implications(LOCAL_CRATE); + self.lazy_array(implications.iter().map(|(k, v)| (*k, *v))) + } + fn encode_diagnostic_items(&mut self) -> LazyArray<(Symbol, DefIndex)> { empty_proc_macro!(self); let tcx = self.tcx; diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index af1c09f4ae8..23198a85369 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -66,13 +66,13 @@ pub const METADATA_HEADER: &[u8] = &[b'r', b'u', b's', b't', 0, 0, 0, METADATA_V /// /// Metadata is effective a tree, encoded in post-order, /// and with the root's position written next to the header. -/// That means every single `Lazy` points to some previous +/// That means every single `LazyValue` points to some previous /// location in the metadata and is part of a larger node. /// -/// The first `Lazy` in a node is encoded as the backwards +/// The first `LazyValue` in a node is encoded as the backwards /// distance from the position where the containing node -/// starts and where the `Lazy` points to, while the rest -/// use the forward distance from the previous `Lazy`. +/// starts and where the `LazyValue` points to, while the rest +/// use the forward distance from the previous `LazyValue`. /// Distances start at 1, as 0-byte nodes are invalid. /// Also invalid are nodes being referred in a different /// order than they were encoded in. @@ -94,12 +94,12 @@ impl<T> LazyValue<T> { /// A list of lazily-decoded values. /// -/// Unlike `Lazy<Vec<T>>`, the length is encoded next to the +/// Unlike `LazyValue<Vec<T>>`, the length is encoded next to the /// position, not at the position, which means that the length /// doesn't need to be known before encoding all the elements. /// /// If the length is 0, no position is encoded, but otherwise, -/// the encoding is that of `Lazy`, with the distinction that +/// the encoding is that of `LazyArray`, with the distinction that /// the minimal distance the length of the sequence, i.e. /// it's assumed there's no 0-byte element in the sequence. struct LazyArray<T> { @@ -167,17 +167,17 @@ impl<I, T> Clone for LazyTable<I, T> { } } -/// Encoding / decoding state for `Lazy`. +/// Encoding / decoding state for `Lazy`s (`LazyValue`, `LazyArray`, and `LazyTable`). #[derive(Copy, Clone, PartialEq, Eq, Debug)] enum LazyState { /// Outside of a metadata node. NoNode, - /// Inside a metadata node, and before any `Lazy`. + /// Inside a metadata node, and before any `Lazy`s. /// The position is that of the node itself. NodeStart(NonZeroUsize), - /// Inside a metadata node, with a previous `Lazy`. + /// Inside a metadata node, with a previous `Lazy`s. /// The position is where that previous `Lazy` would start. Previous(NonZeroUsize), } @@ -226,6 +226,7 @@ pub(crate) struct CrateRoot { crate_deps: LazyArray<CrateDep>, dylib_dependency_formats: LazyArray<Option<LinkagePreference>>, lib_features: LazyArray<(Symbol, Option<Symbol>)>, + stability_implications: LazyArray<(Symbol, Symbol)>, lang_items: LazyArray<(DefIndex, usize)>, lang_items_missing: LazyArray<lang_items::LangItem>, diagnostic_items: LazyArray<(Symbol, DefIndex)>, diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index 42759f0a652..21841ae2532 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -141,7 +141,7 @@ fixed_size_enum! { } } -// We directly encode `DefPathHash` because a `Lazy` would encur a 25% cost. +// We directly encode `DefPathHash` because a `LazyValue` would incur a 25% cost. impl FixedSizeEncoding for Option<DefPathHash> { type ByteArray = [u8; 16]; @@ -159,7 +159,7 @@ impl FixedSizeEncoding for Option<DefPathHash> { } } -// We directly encode RawDefId because using a `Lazy` would incur a 50% overhead in the worst case. +// We directly encode RawDefId because using a `LazyValue` would incur a 50% overhead in the worst case. impl FixedSizeEncoding for Option<RawDefId> { type ByteArray = [u8; 8]; diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 0001e1aa53e..47b04c33ec1 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -396,10 +396,10 @@ impl<'hir> Map<'hir> { } } - pub fn enclosing_body_owner(self, hir_id: HirId) -> HirId { + pub fn enclosing_body_owner(self, hir_id: HirId) -> LocalDefId { for (parent, _) in self.parent_iter(hir_id) { - if let Some(body) = self.maybe_body_owned_by(parent) { - return self.body_owner(body); + if let Some(body) = self.find(parent).map(associated_body).flatten() { + return self.body_owner_def_id(body); } } @@ -419,19 +419,20 @@ impl<'hir> Map<'hir> { self.local_def_id(self.body_owner(id)) } - /// Given a `HirId`, returns the `BodyId` associated with it, + /// Given a `LocalDefId`, returns the `BodyId` associated with it, /// if the node is a body owner, otherwise returns `None`. - pub fn maybe_body_owned_by(self, hir_id: HirId) -> Option<BodyId> { - self.find(hir_id).map(associated_body).flatten() + pub fn maybe_body_owned_by(self, id: LocalDefId) -> Option<BodyId> { + self.get_if_local(id.to_def_id()).map(associated_body).flatten() } /// Given a body owner's id, returns the `BodyId` associated with it. - pub fn body_owned_by(self, id: HirId) -> BodyId { + pub fn body_owned_by(self, id: LocalDefId) -> BodyId { self.maybe_body_owned_by(id).unwrap_or_else(|| { + let hir_id = self.local_def_id_to_hir_id(id); span_bug!( - self.span(id), + self.span(hir_id), "body_owned_by: {} has no associated body", - self.node_to_string(id) + self.node_to_string(hir_id) ); }) } @@ -670,7 +671,7 @@ impl<'hir> Map<'hir> { /// Whether the expression pointed at by `hir_id` belongs to a `const` evaluation context. /// Used exclusively for diagnostics, to avoid suggestion function calls. pub fn is_inside_const_context(self, hir_id: HirId) -> bool { - self.body_const_context(self.local_def_id(self.enclosing_body_owner(hir_id))).is_some() + self.body_const_context(self.enclosing_body_owner(hir_id)).is_some() } /// Retrieves the `HirId` for `id`'s enclosing method, unless there's a diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index a605e234be9..211a614717f 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -157,8 +157,9 @@ pub fn provide(providers: &mut Providers) { }; providers.fn_arg_names = |tcx, id| { let hir = tcx.hir(); - let hir_id = hir.local_def_id_to_hir_id(id.expect_local()); - if let Some(body_id) = hir.maybe_body_owned_by(hir_id) { + let def_id = id.expect_local(); + let hir_id = hir.local_def_id_to_hir_id(def_id); + if let Some(body_id) = hir.maybe_body_owned_by(def_id) { tcx.arena.alloc_from_iter(hir.body_param_names(body_id)) } else if let Node::TraitItem(&TraitItem { kind: TraitItemKind::Fn(_, TraitFn::Required(idents)), diff --git a/compiler/rustc_middle/src/infer/mod.rs b/compiler/rustc_middle/src/infer/mod.rs index 8b2f9bdfd48..38868c21049 100644 --- a/compiler/rustc_middle/src/infer/mod.rs +++ b/compiler/rustc_middle/src/infer/mod.rs @@ -2,9 +2,8 @@ pub mod canonical; pub mod unify_key; use crate::ty::Region; -use crate::ty::Ty; +use crate::ty::{OpaqueTypeKey, Ty}; use rustc_data_structures::sync::Lrc; -use rustc_hir::def_id::LocalDefId; use rustc_span::Span; /// Requires that `region` must be equal to one of the regions in `choice_regions`. @@ -15,8 +14,9 @@ use rustc_span::Span; /// ``` #[derive(Debug, Clone, HashStable, TypeFoldable, TypeVisitable, Lift)] pub struct MemberConstraint<'tcx> { - /// The `DefId` of the opaque type causing this constraint: used for error reporting. - pub opaque_type_def_id: LocalDefId, + /// The `DefId` and substs of the opaque type causing this constraint. + /// Used for error reporting. + pub key: OpaqueTypeKey<'tcx>, /// The span where the hidden type was instantiated. pub definition_span: Span, diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 321fcd43797..45d33a1659f 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -50,7 +50,7 @@ bitflags! { /// the hot path. const COLD = 1 << 0; /// `#[rustc_allocator]`: a hint to LLVM that the pointer returned from this - /// function is never null. + /// function is never null and the function has no side effects other than allocating. const ALLOCATOR = 1 << 1; /// An indicator that function will never unwind. Will become obsolete /// once C-unwind is fully stabilized. @@ -91,6 +91,12 @@ bitflags! { const NO_COVERAGE = 1 << 15; /// `#[used(linker)]`: indicates that LLVM nor the linker can eliminate this function. const USED_LINKER = 1 << 16; + /// `#[rustc_deallocator]`: a hint to LLVM that the function only deallocates memory. + const DEALLOCATOR = 1 << 17; + /// `#[rustc_reallocator]`: a hint to LLVM that the function only reallocates memory. + const REALLOCATOR = 1 << 18; + /// `#[rustc_allocator_zeroed]`: a hint to LLVM that the function only allocates zeroed memory. + const ALLOCATOR_ZEROED = 1 << 19; } } diff --git a/compiler/rustc_middle/src/middle/mod.rs b/compiler/rustc_middle/src/middle/mod.rs index fc35cafcc77..8dc68b1f5a8 100644 --- a/compiler/rustc_middle/src/middle/mod.rs +++ b/compiler/rustc_middle/src/middle/mod.rs @@ -3,14 +3,14 @@ pub mod dependency_format; pub mod exported_symbols; pub mod lang_items; pub mod lib_features { - use rustc_data_structures::fx::{FxHashMap, FxHashSet}; - use rustc_span::symbol::Symbol; + use rustc_data_structures::fx::FxHashMap; + use rustc_span::{symbol::Symbol, Span}; #[derive(HashStable, Debug)] pub struct LibFeatures { - // A map from feature to stabilisation version. - pub stable: FxHashMap<Symbol, Symbol>, - pub unstable: FxHashSet<Symbol>, + /// A map from feature to stabilisation version. + pub stable: FxHashMap<Symbol, (Symbol, Span)>, + pub unstable: FxHashMap<Symbol, Span>, } impl LibFeatures { @@ -18,8 +18,8 @@ pub mod lib_features { let mut all_features: Vec<_> = self .stable .iter() - .map(|(f, s)| (*f, Some(*s))) - .chain(self.unstable.iter().map(|f| (*f, None))) + .map(|(f, (s, _))| (*f, Some(*s))) + .chain(self.unstable.iter().map(|(f, _)| (*f, None))) .collect(); all_features.sort_unstable_by(|a, b| a.0.as_str().partial_cmp(b.0.as_str()).unwrap()); all_features diff --git a/compiler/rustc_middle/src/middle/resolve_lifetime.rs b/compiler/rustc_middle/src/middle/resolve_lifetime.rs index c71ba7b1753..9b2f4456705 100644 --- a/compiler/rustc_middle/src/middle/resolve_lifetime.rs +++ b/compiler/rustc_middle/src/middle/resolve_lifetime.rs @@ -12,7 +12,6 @@ pub enum Region { Static, EarlyBound(/* index */ u32, /* lifetime decl */ DefId), LateBound(ty::DebruijnIndex, /* late-bound index */ u32, /* lifetime decl */ DefId), - LateBoundAnon(ty::DebruijnIndex, /* late-bound index */ u32, /* anon index */ u32), Free(DefId, /* lifetime decl */ DefId), } diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 96e068a3601..414912dd0f7 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -62,6 +62,19 @@ pub struct Index { pub stab_map: FxHashMap<LocalDefId, Stability>, pub const_stab_map: FxHashMap<LocalDefId, ConstStability>, pub depr_map: FxHashMap<LocalDefId, DeprecationEntry>, + /// Mapping from feature name to feature name based on the `implied_by` field of `#[unstable]` + /// attributes. If a `#[unstable(feature = "implier", implied_by = "impliee")]` attribute + /// exists, then this map will have a `impliee -> implier` entry. + /// + /// This mapping is necessary unless both the `#[stable]` and `#[unstable]` attributes should + /// specify their implications (both `implies` and `implied_by`). If only one of the two + /// attributes do (as in the current implementation, `implied_by` in `#[unstable]`), then this + /// mapping is necessary for diagnostics. When a "unnecessary feature attribute" error is + /// reported, only the `#[stable]` attribute information is available, so the map is necessary + /// to know that the feature implies another feature. If it were reversed, and the `#[stable]` + /// attribute had an `implies` meta item, then a map would be necessary when avoiding a "use of + /// unstable feature" error for a feature that was implied. + pub implications: FxHashMap<Symbol, Symbol>, } impl Index { @@ -423,7 +436,9 @@ impl<'tcx> TyCtxt<'tcx> { match stability { Some(Stability { - level: attr::Unstable { reason, issue, is_soft }, feature, .. + level: attr::Unstable { reason, issue, is_soft, implied_by }, + feature, + .. }) => { if span.allows_unstable(feature) { debug!("stability: skipping span={:?} since it is internal", span); @@ -433,6 +448,13 @@ impl<'tcx> TyCtxt<'tcx> { return EvalResult::Allow; } + // If this item was previously part of a now-stabilized feature which is still + // active (i.e. the user hasn't removed the attribute for the stabilized feature + // yet) then allow use of this item. + if let Some(implied_by) = implied_by && self.features().active(implied_by) { + return EvalResult::Allow; + } + // When we're compiling the compiler itself we may pull in // crates from crates.io, but those crates may depend on other // crates also pulled in from crates.io. We want to ideally be @@ -453,7 +475,13 @@ impl<'tcx> TyCtxt<'tcx> { } let suggestion = suggestion_for_allocator_api(self, def_id, span, feature); - EvalResult::Deny { feature, reason, issue, suggestion, is_soft } + EvalResult::Deny { + feature, + reason: reason.to_opt_reason(), + issue, + suggestion, + is_soft, + } } Some(_) => { // Stable APIs are always ok to call and deprecated APIs are diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index eed52ca3eea..db7e0fb8a3b 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -30,7 +30,7 @@ use crate::ty; // hashed. (see the `Hash` impl below for more details), so the impl is not derived. #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)] #[derive(HashStable)] -pub struct Allocation<Tag = AllocId, Extra = ()> { +pub struct Allocation<Prov = AllocId, Extra = ()> { /// The actual bytes of the allocation. /// Note that the bytes of a pointer represent the offset of the pointer. bytes: Box<[u8]>, @@ -38,7 +38,7 @@ pub struct Allocation<Tag = AllocId, Extra = ()> { /// Only the first byte of a pointer is inserted into the map; i.e., /// every entry in this map applies to `pointer_size` consecutive bytes starting /// at the given offset. - relocations: Relocations<Tag>, + relocations: Relocations<Prov>, /// Denotes which part of this allocation is initialized. init_mask: InitMask, /// The alignment of the allocation to detect unaligned reads. @@ -102,8 +102,8 @@ impl hash::Hash for Allocation { /// (`ConstAllocation`) are used quite a bit. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, HashStable)] #[rustc_pass_by_value] -pub struct ConstAllocation<'tcx, Tag = AllocId, Extra = ()>( - pub Interned<'tcx, Allocation<Tag, Extra>>, +pub struct ConstAllocation<'tcx, Prov = AllocId, Extra = ()>( + pub Interned<'tcx, Allocation<Prov, Extra>>, ); impl<'tcx> fmt::Debug for ConstAllocation<'tcx> { @@ -114,8 +114,8 @@ impl<'tcx> fmt::Debug for ConstAllocation<'tcx> { } } -impl<'tcx, Tag, Extra> ConstAllocation<'tcx, Tag, Extra> { - pub fn inner(self) -> &'tcx Allocation<Tag, Extra> { +impl<'tcx, Prov, Extra> ConstAllocation<'tcx, Prov, Extra> { + pub fn inner(self) -> &'tcx Allocation<Prov, Extra> { self.0.0 } } @@ -200,7 +200,7 @@ impl AllocRange { } // The constructors are all without extra; the extra gets added by a machine hook later. -impl<Tag> Allocation<Tag> { +impl<Prov> Allocation<Prov> { /// Creates an allocation initialized by the given bytes pub fn from_bytes<'a>( slice: impl Into<Cow<'a, [u8]>>, @@ -256,14 +256,15 @@ impl<Tag> Allocation<Tag> { } impl Allocation { - /// Convert Tag and add Extra fields - pub fn convert_tag_add_extra<Tag, Extra, Err>( + /// Adjust allocation from the ones in tcx to a custom Machine instance + /// with a different Provenance and Extra type. + pub fn adjust_from_tcx<Prov, Extra, Err>( self, cx: &impl HasDataLayout, extra: Extra, - mut tagger: impl FnMut(Pointer<AllocId>) -> Result<Pointer<Tag>, Err>, - ) -> Result<Allocation<Tag, Extra>, Err> { - // Compute new pointer tags, which also adjusts the bytes. + mut adjust_ptr: impl FnMut(Pointer<AllocId>) -> Result<Pointer<Prov>, Err>, + ) -> Result<Allocation<Prov, Extra>, Err> { + // Compute new pointer provenance, which also adjusts the bytes. let mut bytes = self.bytes; let mut new_relocations = Vec::with_capacity(self.relocations.0.len()); let ptr_size = cx.data_layout().pointer_size.bytes_usize(); @@ -272,10 +273,10 @@ impl Allocation { let idx = offset.bytes_usize(); let ptr_bytes = &mut bytes[idx..idx + ptr_size]; let bits = read_target_uint(endian, ptr_bytes).unwrap(); - let (ptr_tag, ptr_offset) = - tagger(Pointer::new(alloc_id, Size::from_bytes(bits)))?.into_parts(); + let (ptr_prov, ptr_offset) = + adjust_ptr(Pointer::new(alloc_id, Size::from_bytes(bits)))?.into_parts(); write_target_uint(endian, ptr_bytes, ptr_offset.bytes().into()).unwrap(); - new_relocations.push((offset, ptr_tag)); + new_relocations.push((offset, ptr_prov)); } // Create allocation. Ok(Allocation { @@ -290,7 +291,7 @@ impl Allocation { } /// Raw accessors. Provide access to otherwise private bytes. -impl<Tag, Extra> Allocation<Tag, Extra> { +impl<Prov, Extra> Allocation<Prov, Extra> { pub fn len(&self) -> usize { self.bytes.len() } @@ -313,13 +314,13 @@ impl<Tag, Extra> Allocation<Tag, Extra> { } /// Returns the relocation list. - pub fn relocations(&self) -> &Relocations<Tag> { + pub fn relocations(&self) -> &Relocations<Prov> { &self.relocations } } /// Byte accessors. -impl<Tag: Provenance, Extra> Allocation<Tag, Extra> { +impl<Prov: Provenance, Extra> Allocation<Prov, Extra> { /// This is the entirely abstraction-violating way to just grab the raw bytes without /// caring about relocations. It just deduplicates some code between `read_scalar` /// and `get_bytes_internal`. @@ -413,7 +414,7 @@ impl<Tag: Provenance, Extra> Allocation<Tag, Extra> { } /// Reading and writing. -impl<Tag: Provenance, Extra> Allocation<Tag, Extra> { +impl<Prov: Provenance, Extra> Allocation<Prov, Extra> { /// Validates that `ptr.offset` and `ptr.offset + size` do not point to the middle of a /// relocation. If `allow_uninit`/`allow_ptr` is `false`, also enforces that the memory in the /// given range contains no uninitialized bytes/relocations. @@ -451,7 +452,7 @@ impl<Tag: Provenance, Extra> Allocation<Tag, Extra> { cx: &impl HasDataLayout, range: AllocRange, read_provenance: bool, - ) -> AllocResult<ScalarMaybeUninit<Tag>> { + ) -> AllocResult<ScalarMaybeUninit<Prov>> { if read_provenance { assert_eq!(range.size, cx.data_layout().pointer_size); } @@ -475,7 +476,7 @@ impl<Tag: Provenance, Extra> Allocation<Tag, Extra> { // If we are *not* reading a pointer, and we can just ignore relocations, // then do exactly that. - if !read_provenance && Tag::OFFSET_IS_ADDR { + if !read_provenance && Prov::OFFSET_IS_ADDR { // We just strip provenance. let bytes = self.get_bytes_even_more_internal(range); let bits = read_target_uint(cx.data_layout().endian, bytes).unwrap(); @@ -506,7 +507,7 @@ impl<Tag: Provenance, Extra> Allocation<Tag, Extra> { &mut self, cx: &impl HasDataLayout, range: AllocRange, - val: ScalarMaybeUninit<Tag>, + val: ScalarMaybeUninit<Prov>, ) -> AllocResult { assert!(self.mutability == Mutability::Mut); @@ -548,9 +549,9 @@ impl<Tag: Provenance, Extra> Allocation<Tag, Extra> { } /// Relocations. -impl<Tag: Copy, Extra> Allocation<Tag, Extra> { +impl<Prov: Copy, Extra> Allocation<Prov, Extra> { /// Returns all relocations overlapping with the given pointer-offset pair. - fn get_relocations(&self, cx: &impl HasDataLayout, range: AllocRange) -> &[(Size, Tag)] { + fn get_relocations(&self, cx: &impl HasDataLayout, range: AllocRange) -> &[(Size, Prov)] { // We have to go back `pointer_size - 1` bytes, as that one would still overlap with // the beginning of this range. let start = range.start.bytes().saturating_sub(cx.data_layout().pointer_size.bytes() - 1); @@ -580,7 +581,7 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> { /// immediately in that case. fn clear_relocations(&mut self, cx: &impl HasDataLayout, range: AllocRange) -> AllocResult where - Tag: Provenance, + Prov: Provenance, { // Find the start and end of the given range and its outermost relocations. let (first, last) = { @@ -602,7 +603,7 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> { // FIXME: Miri should preserve partial relocations; see // https://github.com/rust-lang/miri/issues/2181. if first < start { - if Tag::ERR_ON_PARTIAL_PTR_OVERWRITE { + if Prov::ERR_ON_PARTIAL_PTR_OVERWRITE { return Err(AllocError::PartialPointerOverwrite(first)); } warn!( @@ -611,7 +612,7 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> { self.init_mask.set_range(first, start, false); } if last > end { - if Tag::ERR_ON_PARTIAL_PTR_OVERWRITE { + if Prov::ERR_ON_PARTIAL_PTR_OVERWRITE { return Err(AllocError::PartialPointerOverwrite( last - cx.data_layout().pointer_size, )); @@ -642,22 +643,22 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> { /// "Relocations" stores the provenance information of pointers stored in memory. #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] -pub struct Relocations<Tag = AllocId>(SortedMap<Size, Tag>); +pub struct Relocations<Prov = AllocId>(SortedMap<Size, Prov>); -impl<Tag> Relocations<Tag> { +impl<Prov> Relocations<Prov> { pub fn new() -> Self { Relocations(SortedMap::new()) } // The caller must guarantee that the given relocations are already sorted // by address and contain no duplicates. - pub fn from_presorted(r: Vec<(Size, Tag)>) -> Self { + pub fn from_presorted(r: Vec<(Size, Prov)>) -> Self { Relocations(SortedMap::from_presorted_elements(r)) } } -impl<Tag> Deref for Relocations<Tag> { - type Target = SortedMap<Size, Tag>; +impl<Prov> Deref for Relocations<Prov> { + type Target = SortedMap<Size, Prov>; fn deref(&self) -> &Self::Target { &self.0 @@ -667,18 +668,18 @@ impl<Tag> Deref for Relocations<Tag> { /// A partial, owned list of relocations to transfer into another allocation. /// /// Offsets are already adjusted to the destination allocation. -pub struct AllocationRelocations<Tag> { - dest_relocations: Vec<(Size, Tag)>, +pub struct AllocationRelocations<Prov> { + dest_relocations: Vec<(Size, Prov)>, } -impl<Tag: Copy, Extra> Allocation<Tag, Extra> { +impl<Prov: Copy, Extra> Allocation<Prov, Extra> { pub fn prepare_relocation_copy( &self, cx: &impl HasDataLayout, src: AllocRange, dest: Size, count: u64, - ) -> AllocationRelocations<Tag> { + ) -> AllocationRelocations<Prov> { let relocations = self.get_relocations(cx, src); if relocations.is_empty() { return AllocationRelocations { dest_relocations: Vec::new() }; @@ -688,7 +689,7 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> { let mut new_relocations = Vec::with_capacity(relocations.len() * (count as usize)); // If `count` is large, this is rather wasteful -- we are allocating a big array here, which - // is mostly filled with redundant information since it's just N copies of the same `Tag`s + // is mostly filled with redundant information since it's just N copies of the same `Prov`s // at slightly adjusted offsets. The reason we do this is so that in `mark_relocation_range` // we can use `insert_presorted`. That wouldn't work with an `Iterator` that just produces // the right sequence of relocations for all N copies. @@ -713,7 +714,7 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> { /// /// This is dangerous to use as it can violate internal `Allocation` invariants! /// It only exists to support an efficient implementation of `mem_copy_repeatedly`. - pub fn mark_relocation_range(&mut self, relocations: AllocationRelocations<Tag>) { + pub fn mark_relocation_range(&mut self, relocations: AllocationRelocations<Prov>) { self.relocations.0.insert_presorted(relocations.dest_relocations); } } @@ -1178,7 +1179,7 @@ impl<'a> Iterator for InitChunkIter<'a> { } /// Uninitialized bytes. -impl<Tag: Copy, Extra> Allocation<Tag, Extra> { +impl<Prov: Copy, Extra> Allocation<Prov, Extra> { /// Checks whether the given range is entirely initialized. /// /// Returns `Ok(())` if it's initialized. Otherwise returns the range of byte @@ -1226,7 +1227,7 @@ impl InitMaskCompressed { } /// Transferring the initialization mask to other allocations. -impl<Tag, Extra> Allocation<Tag, Extra> { +impl<Prov, Extra> Allocation<Prov, Extra> { /// Creates a run-length encoding of the initialization mask; panics if range is empty. /// /// This is essentially a more space-efficient version of diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 795f23edb31..cecb55578d3 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, AllocRange, ConstAlloc, Pointer, Scalar}; use crate::mir::interpret::ConstValue; -use crate::ty::{layout, query::TyCtxtAt, tls, FnSig, Ty, ValTree}; +use crate::ty::{layout, query::TyCtxtAt, tls, Ty, ValTree}; use rustc_data_structures::sync::Lock; use rustc_errors::{pluralize, struct_span_err, DiagnosticBuilder, ErrorGuaranteed}; @@ -219,7 +219,7 @@ pub struct ScalarSizeMismatch { } /// Error information for when the program caused Undefined Behavior. -pub enum UndefinedBehaviorInfo<'tcx> { +pub enum UndefinedBehaviorInfo { /// Free-form case. Only for errors that are never caught! Ub(String), /// Unreachable code was executed. @@ -241,12 +241,6 @@ pub enum UndefinedBehaviorInfo<'tcx> { PointerArithOverflow, /// Invalid metadata in a wide pointer (using `str` to avoid allocations). InvalidMeta(&'static str), - /// Invalid drop function in vtable. - InvalidVtableDropFn(FnSig<'tcx>), - /// Invalid size in a vtable: too large. - InvalidVtableSize, - /// Invalid alignment in a vtable: too large, or not a power of 2. - InvalidVtableAlignment(String), /// Reading a C string that does not end within its allocation. UnterminatedCString(Pointer), /// Dereferencing a dangling pointer after it got freed. @@ -271,6 +265,8 @@ pub enum UndefinedBehaviorInfo<'tcx> { WriteToReadOnly(AllocId), // Trying to access the data behind a function pointer. DerefFunctionPointer(AllocId), + // Trying to access the data behind a vtable pointer. + DerefVTablePointer(AllocId), /// The value validity check found a problem. /// Should only be thrown by `validity.rs` and always point out which part of the value /// is the problem. @@ -288,6 +284,8 @@ pub enum UndefinedBehaviorInfo<'tcx> { InvalidTag(Scalar), /// Using a pointer-not-to-a-function as function pointer. InvalidFunctionPointer(Pointer), + /// Using a pointer-not-to-a-vtable as vtable pointer. + InvalidVTablePointer(Pointer), /// Using a string that is not valid UTF-8, InvalidStr(std::str::Utf8Error), /// Using uninitialized data where it is not allowed. @@ -300,7 +298,7 @@ pub enum UndefinedBehaviorInfo<'tcx> { UninhabitedEnumVariantWritten, } -impl fmt::Display for UndefinedBehaviorInfo<'_> { +impl fmt::Display for UndefinedBehaviorInfo { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use UndefinedBehaviorInfo::*; match self { @@ -315,14 +313,6 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> { RemainderOverflow => write!(f, "overflow in signed remainder (dividing MIN by -1)"), PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"), InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {msg}"), - InvalidVtableDropFn(sig) => write!( - f, - "invalid drop function signature: got {sig}, expected exactly one argument which must be a pointer type", - ), - InvalidVtableSize => { - write!(f, "invalid vtable: size is bigger than largest supported object") - } - InvalidVtableAlignment(msg) => write!(f, "invalid vtable: alignment {msg}"), UnterminatedCString(p) => write!( f, "reading a null-terminated string starting at {p:?} with no null found before end of allocation", @@ -359,6 +349,7 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> { ), WriteToReadOnly(a) => write!(f, "writing to {a:?} which is read-only"), DerefFunctionPointer(a) => write!(f, "accessing {a:?} which contains a function"), + DerefVTablePointer(a) => write!(f, "accessing {a:?} which contains a vtable"), ValidationFailure { path: None, msg } => { write!(f, "constructing invalid value: {msg}") } @@ -375,6 +366,9 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> { InvalidFunctionPointer(p) => { write!(f, "using {p:?} as function pointer but it does not point to a function") } + InvalidVTablePointer(p) => { + write!(f, "using {p:?} as vtable pointer but it does not point to a vtable") + } InvalidStr(err) => write!(f, "this string is not valid UTF-8: {err}"), InvalidUninitBytes(Some((alloc, info))) => write!( f, @@ -494,7 +488,7 @@ impl dyn MachineStopType { pub enum InterpError<'tcx> { /// The program caused undefined behavior. - UndefinedBehavior(UndefinedBehaviorInfo<'tcx>), + UndefinedBehavior(UndefinedBehaviorInfo), /// The program did something the interpreter does not support (some of these *might* be UB /// but the interpreter is not sure). Unsupported(UnsupportedOpInfo), diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 698024b2330..967f8ece16c 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -196,6 +196,7 @@ impl fmt::Debug for AllocId { enum AllocDiscriminant { Alloc, Fn, + VTable, Static, } @@ -215,6 +216,12 @@ pub fn specialized_encode_alloc_id<'tcx, E: TyEncoder<I = TyCtxt<'tcx>>>( AllocDiscriminant::Fn.encode(encoder); fn_instance.encode(encoder); } + GlobalAlloc::VTable(ty, poly_trait_ref) => { + trace!("encoding {:?} with {ty:#?}, {poly_trait_ref:#?}", alloc_id); + AllocDiscriminant::VTable.encode(encoder); + ty.encode(encoder); + poly_trait_ref.encode(encoder); + } GlobalAlloc::Static(did) => { assert!(!tcx.is_thread_local_static(did)); // References to statics doesn't need to know about their allocations, @@ -305,7 +312,9 @@ impl<'s> AllocDecodingSession<'s> { State::InProgress(TinyList::new_single(self.session_id), alloc_id); Some(alloc_id) } - AllocDiscriminant::Fn | AllocDiscriminant::Static => { + AllocDiscriminant::Fn + | AllocDiscriminant::Static + | AllocDiscriminant::VTable => { // Fns and statics cannot be cyclic, and their `AllocId` // is determined later by interning. *entry = @@ -355,6 +364,16 @@ impl<'s> AllocDecodingSession<'s> { let alloc_id = decoder.interner().create_fn_alloc(instance); alloc_id } + AllocDiscriminant::VTable => { + assert!(alloc_id.is_none()); + trace!("creating vtable alloc ID"); + let ty = <Ty<'_> as Decodable<D>>::decode(decoder); + let poly_trait_ref = + <Option<ty::PolyExistentialTraitRef<'_>> as Decodable<D>>::decode(decoder); + trace!("decoded vtable alloc instance: {ty:?}, {poly_trait_ref:?}"); + let alloc_id = decoder.interner().create_vtable_alloc(ty, poly_trait_ref); + alloc_id + } AllocDiscriminant::Static => { assert!(alloc_id.is_none()); trace!("creating extern static alloc ID"); @@ -380,6 +399,8 @@ impl<'s> AllocDecodingSession<'s> { pub enum GlobalAlloc<'tcx> { /// The alloc ID is used as a function pointer. Function(Instance<'tcx>), + /// This alloc ID points to a symbolic (not-reified) vtable. + VTable(Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>), /// The alloc ID points to a "lazy" static variable that did not get computed (yet). /// This is also used to break the cycle in recursive statics. Static(DefId), @@ -407,6 +428,16 @@ impl<'tcx> GlobalAlloc<'tcx> { _ => bug!("expected function, got {:?}", self), } } + + /// Panics if the `GlobalAlloc` is not `GlobalAlloc::VTable` + #[track_caller] + #[inline] + pub fn unwrap_vtable(&self) -> (Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>) { + match *self { + GlobalAlloc::VTable(ty, poly_trait_ref) => (ty, poly_trait_ref), + _ => bug!("expected vtable, got {:?}", self), + } + } } pub(crate) struct AllocMap<'tcx> { @@ -454,12 +485,12 @@ impl<'tcx> TyCtxt<'tcx> { } /// Reserves a new ID *if* this allocation has not been dedup-reserved before. - /// Should only be used for function pointers and statics, we don't want - /// to dedup IDs for "real" memory! + /// Should only be used for "symbolic" allocations (function pointers, vtables, statics), we + /// don't want to dedup IDs for "real" memory! fn reserve_and_set_dedup(self, alloc: GlobalAlloc<'tcx>) -> AllocId { let mut alloc_map = self.alloc_map.lock(); match alloc { - GlobalAlloc::Function(..) | GlobalAlloc::Static(..) => {} + GlobalAlloc::Function(..) | GlobalAlloc::Static(..) | GlobalAlloc::VTable(..) => {} GlobalAlloc::Memory(..) => bug!("Trying to dedup-reserve memory with real data!"), } if let Some(&alloc_id) = alloc_map.dedup.get(&alloc) { @@ -504,6 +535,15 @@ impl<'tcx> TyCtxt<'tcx> { } } + /// Generates an `AllocId` for a (symbolic, not-reified) vtable. Will get deduplicated. + pub fn create_vtable_alloc( + self, + ty: Ty<'tcx>, + poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>, + ) -> AllocId { + self.reserve_and_set_dedup(GlobalAlloc::VTable(ty, poly_trait_ref)) + } + /// Interns the `Allocation` and return a new `AllocId`, even if there's already an identical /// `Allocation` with a different `AllocId`. /// Statics with identical content will still point to the same `Allocation`, i.e., @@ -521,7 +561,7 @@ impl<'tcx> TyCtxt<'tcx> { /// This function exists to allow const eval to detect the difference between evaluation- /// local dangling pointers and allocations in constants/statics. #[inline] - pub fn get_global_alloc(self, id: AllocId) -> Option<GlobalAlloc<'tcx>> { + pub fn try_get_global_alloc(self, id: AllocId) -> Option<GlobalAlloc<'tcx>> { self.alloc_map.lock().alloc_map.get(&id).cloned() } @@ -532,7 +572,7 @@ impl<'tcx> TyCtxt<'tcx> { /// ids), this function is frequently used throughout rustc, but should not be used within /// the miri engine. pub fn global_alloc(self, id: AllocId) -> GlobalAlloc<'tcx> { - match self.get_global_alloc(id) { + match self.try_get_global_alloc(id) { Some(alloc) => alloc, None => bug!("could not find allocation for {id:?}"), } diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs index d4cdf45d186..384954cbbd5 100644 --- a/compiler/rustc_middle/src/mir/interpret/pointer.rs +++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs @@ -159,34 +159,34 @@ impl Provenance for AllocId { /// Pointers are "tagged" with provenance information; typically the `AllocId` they belong to. #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, TyEncodable, TyDecodable, Hash)] #[derive(HashStable)] -pub struct Pointer<Tag = AllocId> { - pub(super) offset: Size, // kept private to avoid accidental misinterpretation (meaning depends on `Tag` type) - pub provenance: Tag, +pub struct Pointer<Prov = AllocId> { + pub(super) offset: Size, // kept private to avoid accidental misinterpretation (meaning depends on `Prov` type) + pub provenance: Prov, } static_assert_size!(Pointer, 16); -// `Option<Tag>` pointers are also passed around quite a bit +// `Option<Prov>` pointers are also passed around quite a bit // (but not stored in permanent machine state). static_assert_size!(Pointer<Option<AllocId>>, 16); // We want the `Debug` output to be readable as it is used by `derive(Debug)` for // all the Miri types. -impl<Tag: Provenance> fmt::Debug for Pointer<Tag> { +impl<Prov: Provenance> fmt::Debug for Pointer<Prov> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Provenance::fmt(self, f) } } -impl<Tag: Provenance> fmt::Debug for Pointer<Option<Tag>> { +impl<Prov: Provenance> fmt::Debug for Pointer<Option<Prov>> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.provenance { - Some(tag) => Provenance::fmt(&Pointer::new(tag, self.offset), f), + Some(prov) => Provenance::fmt(&Pointer::new(prov, self.offset), f), None => write!(f, "{:#x}[noalloc]", self.offset.bytes()), } } } -impl<Tag: Provenance> fmt::Display for Pointer<Option<Tag>> { +impl<Prov: Provenance> fmt::Display for Pointer<Option<Prov>> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.provenance.is_none() && self.offset.bytes() == 0 { write!(f, "null pointer") @@ -204,38 +204,38 @@ impl From<AllocId> for Pointer { } } -impl<Tag> From<Pointer<Tag>> for Pointer<Option<Tag>> { +impl<Prov> From<Pointer<Prov>> for Pointer<Option<Prov>> { #[inline(always)] - fn from(ptr: Pointer<Tag>) -> Self { - let (tag, offset) = ptr.into_parts(); - Pointer::new(Some(tag), offset) + fn from(ptr: Pointer<Prov>) -> Self { + let (prov, offset) = ptr.into_parts(); + Pointer::new(Some(prov), offset) } } -impl<Tag> Pointer<Option<Tag>> { - /// Convert this pointer that *might* have a tag into a pointer that *definitely* has a tag, or - /// an absolute address. +impl<Prov> Pointer<Option<Prov>> { + /// Convert this pointer that *might* have a provenance into a pointer that *definitely* has a + /// provenance, or an absolute address. /// /// This is rarely what you want; call `ptr_try_get_alloc_id` instead. - pub fn into_pointer_or_addr(self) -> Result<Pointer<Tag>, Size> { + pub fn into_pointer_or_addr(self) -> Result<Pointer<Prov>, Size> { match self.provenance { - Some(tag) => Ok(Pointer::new(tag, self.offset)), + Some(prov) => Ok(Pointer::new(prov, self.offset)), None => Err(self.offset), } } /// Returns the absolute address the pointer points to. - /// Only works if Tag::OFFSET_IS_ADDR is true! + /// Only works if Prov::OFFSET_IS_ADDR is true! pub fn addr(self) -> Size where - Tag: Provenance, + Prov: Provenance, { - assert!(Tag::OFFSET_IS_ADDR); + assert!(Prov::OFFSET_IS_ADDR); self.offset } } -impl<Tag> Pointer<Option<Tag>> { +impl<Prov> Pointer<Option<Prov>> { #[inline(always)] pub fn from_addr(addr: u64) -> Self { Pointer { provenance: None, offset: Size::from_bytes(addr) } @@ -247,21 +247,21 @@ impl<Tag> Pointer<Option<Tag>> { } } -impl<'tcx, Tag> Pointer<Tag> { +impl<'tcx, Prov> Pointer<Prov> { #[inline(always)] - pub fn new(provenance: Tag, offset: Size) -> Self { + pub fn new(provenance: Prov, offset: Size) -> Self { Pointer { provenance, offset } } - /// Obtain the constituents of this pointer. Not that the meaning of the offset depends on the type `Tag`! + /// Obtain the constituents of this pointer. Not that the meaning of the offset depends on the type `Prov`! /// This function must only be used in the implementation of `Machine::ptr_get_alloc`, /// and when a `Pointer` is taken apart to be stored efficiently in an `Allocation`. #[inline(always)] - pub fn into_parts(self) -> (Tag, Size) { + pub fn into_parts(self) -> (Prov, Size) { (self.provenance, self.offset) } - pub fn map_provenance(self, f: impl FnOnce(Tag) -> Tag) -> Self { + pub fn map_provenance(self, f: impl FnOnce(Prov) -> Prov) -> Self { Pointer { provenance: f(self.provenance), ..self } } diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index 22bbe29c105..834c114ee1c 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -126,7 +126,7 @@ impl<'tcx> ConstValue<'tcx> { /// Do *not* match on a `Scalar`! Use the various `to_*` methods instead. #[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, TyEncodable, TyDecodable, Hash)] #[derive(HashStable)] -pub enum Scalar<Tag = AllocId> { +pub enum Scalar<Prov = AllocId> { /// The raw bytes of a simple value. Int(ScalarInt), @@ -137,7 +137,7 @@ pub enum Scalar<Tag = AllocId> { /// We also store the size of the pointer, such that a `Scalar` always knows how big it is. /// The size is always the pointer size of the current target, but this is not information /// that we always have readily available. - Ptr(Pointer<Tag>, u8), + Ptr(Pointer<Prov>, u8), } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] @@ -145,7 +145,7 @@ static_assert_size!(Scalar, 24); // We want the `Debug` output to be readable as it is used by `derive(Debug)` for // all the Miri types. -impl<Tag: Provenance> fmt::Debug for Scalar<Tag> { +impl<Prov: Provenance> fmt::Debug for Scalar<Prov> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Scalar::Ptr(ptr, _size) => write!(f, "{:?}", ptr), @@ -154,7 +154,7 @@ impl<Tag: Provenance> fmt::Debug for Scalar<Tag> { } } -impl<Tag: Provenance> fmt::Display for Scalar<Tag> { +impl<Prov: Provenance> fmt::Display for Scalar<Prov> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Scalar::Ptr(ptr, _size) => write!(f, "pointer to {:?}", ptr), @@ -163,7 +163,7 @@ impl<Tag: Provenance> fmt::Display for Scalar<Tag> { } } -impl<Tag: Provenance> fmt::LowerHex for Scalar<Tag> { +impl<Prov: Provenance> fmt::LowerHex for Scalar<Prov> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Scalar::Ptr(ptr, _size) => write!(f, "pointer to {:?}", ptr), @@ -172,37 +172,38 @@ impl<Tag: Provenance> fmt::LowerHex for Scalar<Tag> { } } -impl<Tag> From<Single> for Scalar<Tag> { +impl<Prov> From<Single> for Scalar<Prov> { #[inline(always)] fn from(f: Single) -> Self { Scalar::from_f32(f) } } -impl<Tag> From<Double> for Scalar<Tag> { +impl<Prov> From<Double> for Scalar<Prov> { #[inline(always)] fn from(f: Double) -> Self { Scalar::from_f64(f) } } -impl<Tag> From<ScalarInt> for Scalar<Tag> { +impl<Prov> From<ScalarInt> for Scalar<Prov> { #[inline(always)] fn from(ptr: ScalarInt) -> Self { Scalar::Int(ptr) } } -impl<Tag> Scalar<Tag> { +impl<Prov> Scalar<Prov> { #[inline(always)] - pub fn from_pointer(ptr: Pointer<Tag>, cx: &impl HasDataLayout) -> Self { + pub fn from_pointer(ptr: Pointer<Prov>, cx: &impl HasDataLayout) -> Self { Scalar::Ptr(ptr, u8::try_from(cx.pointer_size().bytes()).unwrap()) } - /// Create a Scalar from a pointer with an `Option<_>` tag (where `None` represents a plain integer). - pub fn from_maybe_pointer(ptr: Pointer<Option<Tag>>, cx: &impl HasDataLayout) -> Self { + /// Create a Scalar from a pointer with an `Option<_>` provenance (where `None` represents a + /// plain integer / "invalid" pointer). + pub fn from_maybe_pointer(ptr: Pointer<Option<Prov>>, cx: &impl HasDataLayout) -> Self { match ptr.into_parts() { - (Some(tag), offset) => Scalar::from_pointer(Pointer::new(tag, offset), cx), + (Some(prov), offset) => Scalar::from_pointer(Pointer::new(prov, offset), cx), (None, offset) => { Scalar::Int(ScalarInt::try_from_uint(offset.bytes(), cx.pointer_size()).unwrap()) } @@ -310,7 +311,7 @@ impl<Tag> Scalar<Tag> { pub fn to_bits_or_ptr_internal( self, target_size: Size, - ) -> Result<Result<u128, Pointer<Tag>>, ScalarSizeMismatch> { + ) -> Result<Result<u128, Pointer<Prov>>, ScalarSizeMismatch> { assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST"); Ok(match self { Scalar::Int(int) => Ok(int.to_bits(target_size).map_err(|size| { @@ -329,7 +330,20 @@ impl<Tag> Scalar<Tag> { } } -impl<'tcx, Tag: Provenance> Scalar<Tag> { +impl<'tcx, Prov: Provenance> Scalar<Prov> { + pub fn to_pointer(self, cx: &impl HasDataLayout) -> InterpResult<'tcx, Pointer<Option<Prov>>> { + match self + .to_bits_or_ptr_internal(cx.pointer_size()) + .map_err(|s| err_ub!(ScalarSizeMismatch(s)))? + { + Err(ptr) => Ok(ptr.into()), + Ok(bits) => { + let addr = u64::try_from(bits).unwrap(); + Ok(Pointer::from_addr(addr)) + } + } + } + /// Fundamental scalar-to-int (cast) operation. Many convenience wrappers exist below, that you /// likely want to use instead. /// @@ -341,13 +355,13 @@ impl<'tcx, Tag: Provenance> Scalar<Tag> { match self { Scalar::Int(int) => Ok(int), Scalar::Ptr(ptr, sz) => { - if Tag::OFFSET_IS_ADDR { + if Prov::OFFSET_IS_ADDR { Ok(ScalarInt::try_from_uint(ptr.offset.bytes(), Size::from_bytes(sz)).unwrap()) } else { // We know `offset` is relative, since `OFFSET_IS_ADDR == false`. - let (tag, offset) = ptr.into_parts(); + let (prov, offset) = ptr.into_parts(); // Because `OFFSET_IS_ADDR == false`, this unwrap can never fail. - Err(Scalar::Ptr(Pointer::new(tag.get_alloc_id().unwrap(), offset), sz)) + Err(Scalar::Ptr(Pointer::new(prov.get_alloc_id().unwrap(), offset), sz)) } } } @@ -489,24 +503,24 @@ impl<'tcx, Tag: Provenance> Scalar<Tag> { } #[derive(Clone, Copy, Eq, PartialEq, TyEncodable, TyDecodable, HashStable, Hash)] -pub enum ScalarMaybeUninit<Tag = AllocId> { - Scalar(Scalar<Tag>), +pub enum ScalarMaybeUninit<Prov = AllocId> { + Scalar(Scalar<Prov>), Uninit, } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] static_assert_size!(ScalarMaybeUninit, 24); -impl<Tag> From<Scalar<Tag>> for ScalarMaybeUninit<Tag> { +impl<Prov> From<Scalar<Prov>> for ScalarMaybeUninit<Prov> { #[inline(always)] - fn from(s: Scalar<Tag>) -> Self { + fn from(s: Scalar<Prov>) -> Self { ScalarMaybeUninit::Scalar(s) } } // We want the `Debug` output to be readable as it is used by `derive(Debug)` for // all the Miri types. -impl<Tag: Provenance> fmt::Debug for ScalarMaybeUninit<Tag> { +impl<Prov: Provenance> fmt::Debug for ScalarMaybeUninit<Prov> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ScalarMaybeUninit::Uninit => write!(f, "<uninitialized>"), @@ -515,7 +529,7 @@ impl<Tag: Provenance> fmt::Debug for ScalarMaybeUninit<Tag> { } } -impl<Tag: Provenance> fmt::LowerHex for ScalarMaybeUninit<Tag> { +impl<Prov: Provenance> fmt::LowerHex for ScalarMaybeUninit<Prov> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ScalarMaybeUninit::Uninit => write!(f, "uninitialized bytes"), @@ -524,19 +538,19 @@ impl<Tag: Provenance> fmt::LowerHex for ScalarMaybeUninit<Tag> { } } -impl<Tag> ScalarMaybeUninit<Tag> { +impl<Prov> ScalarMaybeUninit<Prov> { #[inline] - pub fn from_pointer(ptr: Pointer<Tag>, cx: &impl HasDataLayout) -> Self { + pub fn from_pointer(ptr: Pointer<Prov>, cx: &impl HasDataLayout) -> Self { ScalarMaybeUninit::Scalar(Scalar::from_pointer(ptr, cx)) } #[inline] - pub fn from_maybe_pointer(ptr: Pointer<Option<Tag>>, cx: &impl HasDataLayout) -> Self { + pub fn from_maybe_pointer(ptr: Pointer<Option<Prov>>, cx: &impl HasDataLayout) -> Self { ScalarMaybeUninit::Scalar(Scalar::from_maybe_pointer(ptr, cx)) } #[inline] - pub fn check_init<'tcx>(self) -> InterpResult<'tcx, Scalar<Tag>> { + pub fn check_init<'tcx>(self) -> InterpResult<'tcx, Scalar<Prov>> { match self { ScalarMaybeUninit::Scalar(scalar) => Ok(scalar), ScalarMaybeUninit::Uninit => throw_ub!(InvalidUninitBytes(None)), @@ -544,7 +558,12 @@ impl<Tag> ScalarMaybeUninit<Tag> { } } -impl<'tcx, Tag: Provenance> ScalarMaybeUninit<Tag> { +impl<'tcx, Prov: Provenance> ScalarMaybeUninit<Prov> { + #[inline(always)] + pub fn to_pointer(self, cx: &impl HasDataLayout) -> InterpResult<'tcx, Pointer<Option<Prov>>> { + self.check_init()?.to_pointer(cx) + } + #[inline(always)] pub fn to_bool(self) -> InterpResult<'tcx, bool> { self.check_init()?.to_bool() diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 0b5d23be58d..f7311ebdabf 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1397,7 +1397,6 @@ impl<V, T> ProjectionElem<V, T> { Self::Field(_, _) | Self::Index(_) - | Self::OpaqueCast(_) | Self::ConstantIndex { .. } | Self::Subslice { .. } | Self::Downcast(_, _) => false, @@ -1462,6 +1461,14 @@ impl<'tcx> Place<'tcx> { self.projection.iter().any(|elem| elem.is_indirect()) } + /// If MirPhase >= Derefered and if projection contains Deref, + /// It's guaranteed to be in the first place + pub fn has_deref(&self) -> bool { + // To make sure this is not accidently used in wrong mir phase + debug_assert!(!self.projection[1..].contains(&PlaceElem::Deref)); + self.projection.first() == Some(&PlaceElem::Deref) + } + /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or /// a single deref of a local. #[inline(always)] @@ -1534,6 +1541,12 @@ impl<'tcx> PlaceRef<'tcx> { } } + /// If MirPhase >= Derefered and if projection contains Deref, + /// It's guaranteed to be in the first place + pub fn has_deref(&self) -> bool { + self.projection.first() == Some(&PlaceElem::Deref) + } + /// If this place represents a local variable like `_X` with no /// projections, return `Some(_X)`. #[inline] @@ -1575,9 +1588,7 @@ impl Debug for Place<'_> { fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { for elem in self.projection.iter().rev() { match elem { - ProjectionElem::OpaqueCast(_) - | ProjectionElem::Downcast(_, _) - | ProjectionElem::Field(_, _) => { + ProjectionElem::Downcast(_, _) | ProjectionElem::Field(_, _) => { write!(fmt, "(").unwrap(); } ProjectionElem::Deref => { @@ -1593,9 +1604,6 @@ impl Debug for Place<'_> { for elem in self.projection.iter() { match elem { - ProjectionElem::OpaqueCast(ty) => { - write!(fmt, " as {})", ty)?; - } ProjectionElem::Downcast(Some(name), _index) => { write!(fmt, " as {})", name)?; } @@ -1668,6 +1676,22 @@ impl SourceScope { ClearCrossCrate::Clear => None, } } + + /// The instance this source scope was inlined from, if any. + #[inline] + pub fn inlined_instance<'tcx>( + self, + source_scopes: &IndexVec<SourceScope, SourceScopeData<'tcx>>, + ) -> Option<ty::Instance<'tcx>> { + let scope_data = &source_scopes[self]; + if let Some((inlined_instance, _)) = scope_data.inlined { + Some(inlined_instance) + } else if let Some(inlined_scope) = scope_data.inlined_parent_scope { + Some(source_scopes[inlined_scope].inlined.unwrap().0) + } else { + None + } + } } #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 8b51c5b3da5..21ae121e1ce 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -362,7 +362,7 @@ impl<'tcx> CodegenUnit<'tcx> { // the codegen tests and can even make item order // unstable. InstanceDef::Item(def) => def.did.as_local().map(Idx::index), - InstanceDef::VtableShim(..) + InstanceDef::VTableShim(..) | InstanceDef::ReifyShim(..) | InstanceDef::Intrinsic(..) | InstanceDef::FnPtrShim(..) diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 970043d427f..0ce41337b91 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -360,7 +360,7 @@ where "{:A$} // {}{}", indented_body, if tcx.sess.verbose() { format!("{:?}: ", current_location) } else { String::new() }, - comment(tcx, statement.source_info), + comment(tcx, statement.source_info, body.span), A = ALIGN, )?; @@ -381,7 +381,7 @@ where "{:A$} // {}{}", indented_terminator, if tcx.sess.verbose() { format!("{:?}: ", current_location) } else { String::new() }, - comment(tcx, data.terminator().source_info), + comment(tcx, data.terminator().source_info, body.span), A = ALIGN, )?; @@ -518,8 +518,14 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { } } -fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo) -> String { - format!("scope {} at {}", scope.index(), tcx.sess.source_map().span_to_embeddable_string(span)) +fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo, function_span: Span) -> String { + let location = if tcx.sess.opts.unstable_opts.mir_pretty_relative_line_numbers { + tcx.sess.source_map().span_to_relative_line_string(span, function_span) + } else { + tcx.sess.source_map().span_to_embeddable_string(span) + }; + + format!("scope {} at {}", scope.index(), location,) } /// Prints local variables in a scope tree. @@ -550,7 +556,7 @@ fn write_scope_tree( "{0:1$} // in {2}", indented_debug_info, ALIGN, - comment(tcx, var_debug_info.source_info), + comment(tcx, var_debug_info.source_info, body.span), )?; } @@ -585,7 +591,7 @@ fn write_scope_tree( indented_decl, ALIGN, local_name, - comment(tcx, local_decl.source_info), + comment(tcx, local_decl.source_info, body.span), )?; } @@ -720,11 +726,17 @@ pub fn write_allocations<'tcx>( write!(w, "{}", display_allocation(tcx, alloc.inner())) }; write!(w, "\n{id:?}")?; - match tcx.get_global_alloc(id) { + match tcx.try_get_global_alloc(id) { // This can't really happen unless there are bugs, but it doesn't cost us anything to // gracefully handle it and allow buggy rustc to be debugged via allocation printing. None => write!(w, " (deallocated)")?, Some(GlobalAlloc::Function(inst)) => write!(w, " (fn: {inst})")?, + Some(GlobalAlloc::VTable(ty, Some(trait_ref))) => { + write!(w, " (vtable: impl {trait_ref} for {ty})")? + } + Some(GlobalAlloc::VTable(ty, None)) => { + write!(w, " (vtable: impl <auto trait> for {ty})")? + } Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => { match tcx.eval_static_initializer(did) { Ok(alloc) => { @@ -767,21 +779,21 @@ pub fn write_allocations<'tcx>( /// After the hex dump, an ascii dump follows, replacing all unprintable characters (control /// characters or characters whose value is larger than 127) with a `.` /// This also prints relocations adequately. -pub fn display_allocation<'a, 'tcx, Tag, Extra>( +pub fn display_allocation<'a, 'tcx, Prov, Extra>( tcx: TyCtxt<'tcx>, - alloc: &'a Allocation<Tag, Extra>, -) -> RenderAllocation<'a, 'tcx, Tag, Extra> { + alloc: &'a Allocation<Prov, Extra>, +) -> RenderAllocation<'a, 'tcx, Prov, Extra> { RenderAllocation { tcx, alloc } } #[doc(hidden)] -pub struct RenderAllocation<'a, 'tcx, Tag, Extra> { +pub struct RenderAllocation<'a, 'tcx, Prov, Extra> { tcx: TyCtxt<'tcx>, - alloc: &'a Allocation<Tag, Extra>, + alloc: &'a Allocation<Prov, Extra>, } -impl<'a, 'tcx, Tag: Provenance, Extra> std::fmt::Display - for RenderAllocation<'a, 'tcx, Tag, Extra> +impl<'a, 'tcx, Prov: Provenance, Extra> std::fmt::Display + for RenderAllocation<'a, 'tcx, Prov, Extra> { fn fmt(&self, w: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let RenderAllocation { tcx, alloc } = *self; @@ -825,9 +837,9 @@ fn write_allocation_newline( /// The `prefix` argument allows callers to add an arbitrary prefix before each line (even if there /// is only one line). Note that your prefix should contain a trailing space as the lines are /// printed directly after it. -fn write_allocation_bytes<'tcx, Tag: Provenance, Extra>( +fn write_allocation_bytes<'tcx, Prov: Provenance, Extra>( tcx: TyCtxt<'tcx>, - alloc: &Allocation<Tag, Extra>, + alloc: &Allocation<Prov, Extra>, w: &mut dyn std::fmt::Write, prefix: &str, ) -> std::fmt::Result { @@ -861,7 +873,7 @@ fn write_allocation_bytes<'tcx, Tag: Provenance, Extra>( if i != line_start { write!(w, " ")?; } - if let Some(&tag) = alloc.relocations().get(&i) { + if let Some(&prov) = alloc.relocations().get(&i) { // Memory with a relocation must be defined assert!(alloc.init_mask().is_range_initialized(i, i + ptr_size).is_ok()); let j = i.bytes_usize(); @@ -870,7 +882,7 @@ fn write_allocation_bytes<'tcx, Tag: Provenance, Extra>( let offset = read_target_uint(tcx.data_layout.endian, offset).unwrap(); let offset = Size::from_bytes(offset); let relocation_width = |bytes| bytes * 3; - let ptr = Pointer::new(tag, offset); + let ptr = Pointer::new(prov, offset); let mut target = format!("{:?}", ptr); if target.len() > relocation_width(ptr_size.bytes_usize() - 1) { // This is too long, try to save some space. diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 423e84d88cf..dd9f8795f94 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -2,7 +2,7 @@ use crate::mir::{Body, ConstantKind, Promoted}; use crate::ty::{self, OpaqueHiddenType, Ty, TyCtxt}; -use rustc_data_structures::stable_map::FxHashMap; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::vec_map::VecMap; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; diff --git a/compiler/rustc_middle/src/mir/switch_sources.rs b/compiler/rustc_middle/src/mir/switch_sources.rs index d1f3e6b6fe6..b91c0c25782 100644 --- a/compiler/rustc_middle/src/mir/switch_sources.rs +++ b/compiler/rustc_middle/src/mir/switch_sources.rs @@ -1,8 +1,8 @@ //! Lazily compute the inverse of each `SwitchInt`'s switch targets. Modeled after //! `Predecessors`/`PredecessorCache`. +use rustc_data_structures::fx::FxHashMap; 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::{Decodable, Decoder, Encodable, Encoder}; diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 263c2ca3c70..510316c778b 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -754,9 +754,6 @@ pub type AssertMessage<'tcx> = AssertKind<Operand<'tcx>>; /// generator has more than one variant, the parent place's variant index must be set, indicating /// which variant is being used. If it has just one variant, the variant index may or may not be /// included - the single possible variant is inferred if it is not included. -/// - [`OpaqueCast`](ProjectionElem::OpaqueCast): This projection changes the place's type to the -/// given one, and makes no other changes. A `OpaqueCast` projection on any type other than an -/// opaque type from the current crate is not well-formed. /// - [`ConstantIndex`](ProjectionElem::ConstantIndex): Computes an offset in units of `T` into the /// place as described in the documentation for the `ProjectionElem`. The resulting address is /// the parent's address plus that offset, and the type is `T`. This is only legal if the parent @@ -859,10 +856,6 @@ pub enum ProjectionElem<V, T> { /// /// The included Symbol is the name of the variant, used for printing MIR. Downcast(Option<Symbol>, VariantIdx), - - /// Like an explicit cast from an opaque type to a concrete type, but without - /// requiring an intermediate variable. - OpaqueCast(T), } /// Alias for projections as they appear in places, where the base is a place diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index c6975df45ef..fd3359ea80f 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -57,7 +57,7 @@ impl<'tcx> PlaceTy<'tcx> { /// `PlaceElem`, where we can just use the `Ty` that is already /// stored inline on field projection elems. pub fn projection_ty(self, tcx: TyCtxt<'tcx>, elem: PlaceElem<'tcx>) -> PlaceTy<'tcx> { - self.projection_ty_core(tcx, ty::ParamEnv::empty(), &elem, |_, _, ty| ty, |_, ty| ty) + self.projection_ty_core(tcx, ty::ParamEnv::empty(), &elem, |_, _, ty| ty) } /// `place_ty.projection_ty_core(tcx, elem, |...| { ... })` @@ -71,7 +71,6 @@ impl<'tcx> PlaceTy<'tcx> { param_env: ty::ParamEnv<'tcx>, elem: &ProjectionElem<V, T>, mut handle_field: impl FnMut(&Self, Field, T) -> Ty<'tcx>, - mut handle_opaque_cast: impl FnMut(&Self, T) -> Ty<'tcx>, ) -> PlaceTy<'tcx> where V: ::std::fmt::Debug, @@ -110,7 +109,6 @@ impl<'tcx> PlaceTy<'tcx> { PlaceTy { ty: self.ty, variant_index: Some(index) } } ProjectionElem::Field(f, fty) => PlaceTy::from_ty(handle_field(&self, f, fty)), - ProjectionElem::OpaqueCast(ty) => PlaceTy::from_ty(handle_opaque_cast(&self, ty)), }; debug!("projection_ty self: {:?} elem: {:?} yields: {:?}", self, elem, answer); answer diff --git a/compiler/rustc_middle/src/mir/type_foldable.rs b/compiler/rustc_middle/src/mir/type_foldable.rs index a73ef23e281..82a6b0c506f 100644 --- a/compiler/rustc_middle/src/mir/type_foldable.rs +++ b/compiler/rustc_middle/src/mir/type_foldable.rs @@ -182,7 +182,6 @@ impl<'tcx> TypeFoldable<'tcx> for PlaceElem<'tcx> { Ok(match self { Deref => Deref, Field(f, ty) => Field(f, ty.try_fold_with(folder)?), - OpaqueCast(ty) => OpaqueCast(ty.try_fold_with(folder)?), Index(v) => Index(v.try_fold_with(folder)?), Downcast(symbol, variantidx) => Downcast(symbol, variantidx), ConstantIndex { offset, min_length, from_end } => { diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index e5599fb15ad..89160876401 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -394,7 +394,7 @@ macro_rules! make_mir_visitor { ty::InstanceDef::Item(_def_id) => {} ty::InstanceDef::Intrinsic(_def_id) | - ty::InstanceDef::VtableShim(_def_id) | + ty::InstanceDef::VTableShim(_def_id) | ty::InstanceDef::ReifyShim(_def_id) | ty::InstanceDef::Virtual(_def_id, _) | ty::InstanceDef::ClosureOnceShim { call_once: _def_id, track_caller: _ } | @@ -1064,11 +1064,6 @@ macro_rules! visit_place_fns { self.visit_ty(&mut new_ty, TyContext::Location(location)); if ty != new_ty { Some(PlaceElem::Field(field, new_ty)) } else { None } } - PlaceElem::OpaqueCast(ty) => { - let mut new_ty = ty; - self.visit_ty(&mut new_ty, TyContext::Location(location)); - if ty != new_ty { Some(PlaceElem::OpaqueCast(new_ty)) } else { None } - } PlaceElem::Deref | PlaceElem::ConstantIndex { .. } | PlaceElem::Subslice { .. } @@ -1138,7 +1133,7 @@ macro_rules! visit_place_fns { location: Location, ) { match elem { - ProjectionElem::OpaqueCast(ty) | ProjectionElem::Field(_, ty) => { + ProjectionElem::Field(_field, ty) => { self.visit_ty(ty, TyContext::Location(location)); } ProjectionElem::Index(local) => { diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 0581ef41f66..466a0fc25f7 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1634,11 +1634,15 @@ rustc_queries! { storage(ArenaCacheSelector<'tcx>) desc { "calculating the lib features map" } } - query defined_lib_features(_: CrateNum) - -> &'tcx [(Symbol, Option<Symbol>)] { + query defined_lib_features(_: CrateNum) -> &'tcx [(Symbol, Option<Symbol>)] { desc { "calculating the lib features defined in a crate" } separate_provide_extern } + query stability_implications(_: CrateNum) -> FxHashMap<Symbol, Symbol> { + storage(ArenaCacheSelector<'tcx>) + desc { "calculating the implications between `#[unstable]` 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) } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 75559d4f8b8..72b848c3ee2 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -311,18 +311,10 @@ pub enum ObligationCauseCode<'tcx> { }, /// Error derived when matching traits/impls; see ObligationCause for more details - CompareImplConstObligation, - - /// Error derived when matching traits/impls; see ObligationCause for more details - CompareImplMethodObligation { - impl_item_def_id: LocalDefId, - trait_item_def_id: DefId, - }, - - /// Error derived when matching traits/impls; see ObligationCause for more details - CompareImplTypeObligation { + CompareImplItemObligation { impl_item_def_id: LocalDefId, trait_item_def_id: DefId, + kind: ty::AssocKind, }, /// Checking that the bounds of a trait's associated type hold for a given impl @@ -351,7 +343,7 @@ pub enum ObligationCauseCode<'tcx> { ConstPatternStructural, /// Computing common supertype in an if expression - IfExpression(Box<IfExpressionCause>), + IfExpression(Box<IfExpressionCause<'tcx>>), /// Computing common supertype of an if expression with no else counter-part IfExpressionWithNoElse, @@ -488,22 +480,27 @@ impl<'tcx> ty::Lift<'tcx> for StatementAsExpression { #[derive(Clone, Debug, PartialEq, Eq, Hash, Lift)] pub struct MatchExpressionArmCause<'tcx> { + pub arm_block_id: Option<hir::HirId>, + pub arm_ty: Ty<'tcx>, pub arm_span: Span, + pub prior_arm_block_id: Option<hir::HirId>, + pub prior_arm_ty: Ty<'tcx>, + pub prior_arm_span: Span, pub scrut_span: Span, - pub semi_span: Option<(Span, StatementAsExpression)>, pub source: hir::MatchSource, pub prior_arms: Vec<Span>, - pub last_ty: Ty<'tcx>, pub scrut_hir_id: hir::HirId, pub opt_suggest_box_span: Option<Span>, } -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct IfExpressionCause { - pub then: Span, - pub else_sp: Span, - pub outer: Option<Span>, - pub semicolon: Option<(Span, StatementAsExpression)>, +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Lift, TypeFoldable, TypeVisitable)] +pub struct IfExpressionCause<'tcx> { + pub then_id: hir::HirId, + pub else_id: hir::HirId, + pub then_ty: Ty<'tcx>, + pub else_ty: Ty<'tcx>, + pub outer_span: Option<Span>, pub opt_suggest_box_span: Option<Span>, } diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index 937b166d484..1f9b474ade1 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -9,13 +9,9 @@ use crate::infer::canonical::{Canonical, QueryResponse}; use crate::ty::error::TypeError; use crate::ty::subst::GenericArg; use crate::ty::{self, Ty, TyCtxt}; - -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_errors::struct_span_err; -use rustc_query_system::ich::StableHashingContext; use rustc_span::source_map::Span; use std::iter::FromIterator; -use std::mem; pub mod type_op { use crate::ty::fold::TypeFoldable; @@ -226,29 +222,9 @@ pub struct NormalizationResult<'tcx> { /// case they are called implied bounds). They are fed to the /// `OutlivesEnv` which in turn is supplied to the region checker and /// other parts of the inference system. -#[derive(Clone, Debug, TypeFoldable, TypeVisitable, Lift)] +#[derive(Clone, Debug, TypeFoldable, TypeVisitable, Lift, HashStable)] pub enum OutlivesBound<'tcx> { RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>), RegionSubParam(ty::Region<'tcx>, ty::ParamTy), RegionSubProjection(ty::Region<'tcx>, ty::ProjectionTy<'tcx>), } - -impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for OutlivesBound<'tcx> { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - mem::discriminant(self).hash_stable(hcx, hasher); - match *self { - OutlivesBound::RegionSubRegion(ref a, ref b) => { - a.hash_stable(hcx, hasher); - b.hash_stable(hcx, hasher); - } - OutlivesBound::RegionSubParam(ref a, ref b) => { - a.hash_stable(hcx, hasher); - b.hash_stable(hcx, hasher); - } - OutlivesBound::RegionSubProjection(ref a, ref b) => { - a.hash_stable(hcx, hasher); - b.hash_stable(hcx, hasher); - } - } - } -} diff --git a/compiler/rustc_middle/src/traits/structural_impls.rs b/compiler/rustc_middle/src/traits/structural_impls.rs index 8f1a1564fc8..7fbd57ac735 100644 --- a/compiler/rustc_middle/src/traits/structural_impls.rs +++ b/compiler/rustc_middle/src/traits/structural_impls.rs @@ -130,7 +130,6 @@ impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceConstDestructData<N> { // Lift implementations TrivialTypeTraversalAndLiftImpls! { - super::IfExpressionCause, super::ImplSourceDiscriminantKindData, super::ImplSourcePointeeData, } diff --git a/compiler/rustc_middle/src/traits/util.rs b/compiler/rustc_middle/src/traits/util.rs index 8ce428c9799..d54b8c599d9 100644 --- a/compiler/rustc_middle/src/traits/util.rs +++ b/compiler/rustc_middle/src/traits/util.rs @@ -1,4 +1,4 @@ -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use crate::ty::{PolyTraitRef, TyCtxt}; diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index 2c93af50667..eb732148e3e 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -105,6 +105,16 @@ impl AssocKind { } } +impl std::fmt::Display for AssocKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + AssocKind::Fn => write!(f, "method"), + AssocKind::Const => write!(f, "associated const"), + AssocKind::Type => write!(f, "associated type"), + } + } +} + /// A list of `ty::AssocItem`s in definition order that allows for efficient lookup by name. /// /// When doing lookup by name, we try to postpone hygienic comparison for as long as possible since diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index e6ea3d88853..51137c52659 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -523,4 +523,5 @@ impl_binder_encode_decode! { ty::ExistentialPredicate<'tcx>, ty::TraitRef<'tcx>, Vec<ty::GeneratorInteriorTypeCause<'tcx>>, + ty::ExistentialTraitRef<'tcx>, } diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index a4e7a12bba3..f8792edc017 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -203,7 +203,7 @@ impl<'tcx> Const<'tcx> { pub fn to_valtree(self) -> ty::ValTree<'tcx> { match self.kind() { ty::ConstKind::Value(valtree) => valtree, - _ => bug!("expected ConstKind::Value"), + _ => bug!("expected ConstKind::Value, got {:?}", self.kind()), } } diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs index 973dc3dd4a1..93707bb18ce 100644 --- a/compiler/rustc_middle/src/ty/consts/valtree.rs +++ b/compiler/rustc_middle/src/ty/consts/valtree.rs @@ -80,33 +80,25 @@ impl<'tcx> ValTree<'tcx> { } /// Get the values inside the ValTree as a slice of bytes. This only works for - /// constants with types &str and &[u8]. + /// constants with types &str, &[u8], or [u8; _]. pub fn try_to_raw_bytes(self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx [u8]> { match ty.kind() { ty::Ref(_, inner_ty, _) => match inner_ty.kind() { - ty::Str => { - let leafs = self - .unwrap_branch() - .into_iter() - .map(|v| v.unwrap_leaf().try_to_u8().unwrap()) - .collect::<Vec<_>>(); - - return Some(tcx.arena.alloc_from_iter(leafs.into_iter())); - } - ty::Slice(slice_ty) if *slice_ty == tcx.types.u8 => { - let leafs = self - .unwrap_branch() - .into_iter() - .map(|v| v.unwrap_leaf().try_to_u8().unwrap()) - .collect::<Vec<_>>(); - - return Some(tcx.arena.alloc_from_iter(leafs.into_iter())); - } - _ => {} + // `&str` can be interpreted as raw bytes + ty::Str => {} + // `&[u8]` can be interpreted as raw bytes + ty::Slice(slice_ty) if *slice_ty == tcx.types.u8 => {} + // other `&_` can't be interpreted as raw bytes + _ => return None, }, - _ => {} + // `[u8; N]` can be interpreted as raw bytes + ty::Array(array_ty, _) if *array_ty == tcx.types.u8 => {} + // Otherwise, type cannot be interpreted as raw bytes + _ => return None, } - None + Some(tcx.arena.alloc_from_iter( + self.unwrap_branch().into_iter().map(|v| v.unwrap_leaf().try_to_u8().unwrap()), + )) } } diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 25bc6dc6167..dd2f4321060 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -3,7 +3,7 @@ use std::ops::ControlFlow; use crate::ty::{ - visit::TypeVisitable, Const, ConstKind, DefIdTree, ExistentialPredicate, InferTy, + visit::TypeVisitable, Const, ConstKind, DefIdTree, ExistentialPredicate, InferConst, InferTy, PolyTraitPredicate, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor, }; @@ -82,15 +82,18 @@ pub trait IsSuggestable<'tcx> { /// meaningful rendered suggestions when pretty-printed. We leave some /// nonsense, such as region vars, since those render as `'_` and are /// usually okay to reinterpret as elided lifetimes. - fn is_suggestable(self, tcx: TyCtxt<'tcx>) -> bool; + /// + /// Only if `infer_suggestable` is true, we consider type and const + /// inference variables to be suggestable. + fn is_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> bool; } impl<'tcx, T> IsSuggestable<'tcx> for T where T: TypeVisitable<'tcx>, { - fn is_suggestable(self, tcx: TyCtxt<'tcx>) -> bool { - self.visit_with(&mut IsSuggestableVisitor { tcx }).is_continue() + fn is_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> bool { + self.visit_with(&mut IsSuggestableVisitor { tcx, infer_suggestable }).is_continue() } } @@ -100,7 +103,7 @@ pub fn suggest_arbitrary_trait_bound<'tcx>( err: &mut Diagnostic, trait_pred: PolyTraitPredicate<'tcx>, ) -> bool { - if !trait_pred.is_suggestable(tcx) { + if !trait_pred.is_suggestable(tcx, false) { return false; } @@ -419,6 +422,7 @@ impl<'v> hir::intravisit::Visitor<'v> for StaticLifetimeVisitor<'v> { pub struct IsSuggestableVisitor<'tcx> { tcx: TyCtxt<'tcx>, + infer_suggestable: bool, } impl<'tcx> TypeVisitor<'tcx> for IsSuggestableVisitor<'tcx> { @@ -426,6 +430,8 @@ impl<'tcx> TypeVisitor<'tcx> for IsSuggestableVisitor<'tcx> { fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { match t.kind() { + Infer(InferTy::TyVar(_)) if self.infer_suggestable => {} + FnDef(..) | Closure(..) | Infer(..) @@ -479,6 +485,8 @@ impl<'tcx> TypeVisitor<'tcx> for IsSuggestableVisitor<'tcx> { fn visit_const(&mut self, c: Const<'tcx>) -> ControlFlow<Self::BreakTy> { match c.kind() { + ConstKind::Infer(InferConst::Var(_)) if self.infer_suggestable => {} + ConstKind::Infer(..) | ConstKind::Bound(..) | ConstKind::Placeholder(..) diff --git a/compiler/rustc_middle/src/ty/erase_regions.rs b/compiler/rustc_middle/src/ty/erase_regions.rs index f39fa363a16..3226950e79e 100644 --- a/compiler/rustc_middle/src/ty/erase_regions.rs +++ b/compiler/rustc_middle/src/ty/erase_regions.rs @@ -49,7 +49,7 @@ impl<'tcx> TypeFolder<'tcx> for RegionEraserVisitor<'tcx> { where T: TypeFoldable<'tcx>, { - let u = self.tcx.anonymize_late_bound_regions(t); + let u = self.tcx.anonymize_bound_vars(t); u.super_fold_with(self) } diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 49a518b101d..91246051316 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -660,12 +660,8 @@ impl<T> Trait<T> for X { | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }), ) ); - let impl_comparison = matches!( - cause_code, - ObligationCauseCode::CompareImplMethodObligation { .. } - | ObligationCauseCode::CompareImplTypeObligation { .. } - | ObligationCauseCode::CompareImplConstObligation - ); + let impl_comparison = + matches!(cause_code, ObligationCauseCode::CompareImplItemObligation { .. }); let assoc = self.associated_item(proj_ty.item_def_id); if !callable_scope || impl_comparison { // We do not want to suggest calling functions when the reason of the diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index 878f31af00f..5e96e278b9c 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -44,7 +44,8 @@ //! - u.fold_with(folder) //! ``` use crate::mir; -use crate::ty::{self, Binder, Ty, TyCtxt, TypeVisitable}; +use crate::ty::{self, Binder, BoundTy, Ty, TyCtxt, TypeVisitable}; +use rustc_data_structures::fx::FxIndexMap; use rustc_hir::def_id::DefId; use std::collections::BTreeMap; @@ -370,31 +371,55 @@ impl<'a, 'tcx> TypeFolder<'tcx> for RegionFolder<'a, 'tcx> { /////////////////////////////////////////////////////////////////////////// // Bound vars replacer +pub trait BoundVarReplacerDelegate<'tcx> { + fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx>; + fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx>; + fn replace_const(&mut self, bv: ty::BoundVar, ty: Ty<'tcx>) -> ty::Const<'tcx>; +} + +pub struct FnMutDelegate<R, T, C> { + pub regions: R, + pub types: T, + pub consts: C, +} +impl<'tcx, R, T, C> BoundVarReplacerDelegate<'tcx> for FnMutDelegate<R, T, C> +where + R: FnMut(ty::BoundRegion) -> ty::Region<'tcx>, + T: FnMut(ty::BoundTy) -> Ty<'tcx>, + C: FnMut(ty::BoundVar, Ty<'tcx>) -> ty::Const<'tcx>, +{ + fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx> { + (self.regions)(br) + } + fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx> { + (self.types)(bt) + } + fn replace_const(&mut self, bv: ty::BoundVar, ty: Ty<'tcx>) -> ty::Const<'tcx> { + (self.consts)(bv, ty) + } +} + /// Replaces the escaping bound vars (late bound regions or bound types) in a type. -struct BoundVarReplacer<'a, 'tcx> { +struct BoundVarReplacer<'tcx, D> { tcx: TyCtxt<'tcx>, /// As with `RegionFolder`, represents the index of a binder *just outside* /// the ones we have visited. current_index: ty::DebruijnIndex, - fld_r: &'a mut (dyn FnMut(ty::BoundRegion) -> ty::Region<'tcx> + 'a), - fld_t: &'a mut (dyn FnMut(ty::BoundTy) -> Ty<'tcx> + 'a), - fld_c: &'a mut (dyn FnMut(ty::BoundVar, Ty<'tcx>) -> ty::Const<'tcx> + 'a), + delegate: D, } -impl<'a, 'tcx> BoundVarReplacer<'a, 'tcx> { - fn new( - tcx: TyCtxt<'tcx>, - fld_r: &'a mut (dyn FnMut(ty::BoundRegion) -> ty::Region<'tcx> + 'a), - fld_t: &'a mut (dyn FnMut(ty::BoundTy) -> Ty<'tcx> + 'a), - fld_c: &'a mut (dyn FnMut(ty::BoundVar, Ty<'tcx>) -> ty::Const<'tcx> + 'a), - ) -> Self { - BoundVarReplacer { tcx, current_index: ty::INNERMOST, fld_r, fld_t, fld_c } +impl<'tcx, D: BoundVarReplacerDelegate<'tcx>> BoundVarReplacer<'tcx, D> { + fn new(tcx: TyCtxt<'tcx>, delegate: D) -> Self { + BoundVarReplacer { tcx, current_index: ty::INNERMOST, delegate } } } -impl<'a, 'tcx> TypeFolder<'tcx> for BoundVarReplacer<'a, 'tcx> { +impl<'tcx, D> TypeFolder<'tcx> for BoundVarReplacer<'tcx, D> +where + D: BoundVarReplacerDelegate<'tcx>, +{ fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { self.tcx } @@ -412,7 +437,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for BoundVarReplacer<'a, 'tcx> { fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { match *t.kind() { ty::Bound(debruijn, bound_ty) if debruijn == self.current_index => { - let ty = (self.fld_t)(bound_ty); + let ty = self.delegate.replace_ty(bound_ty); ty::fold::shift_vars(self.tcx, ty, self.current_index.as_u32()) } _ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self), @@ -423,14 +448,14 @@ impl<'a, 'tcx> TypeFolder<'tcx> for BoundVarReplacer<'a, 'tcx> { fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { match *r { ty::ReLateBound(debruijn, br) if debruijn == self.current_index => { - let region = (self.fld_r)(br); + let region = self.delegate.replace_region(br); if let ty::ReLateBound(debruijn1, br) = *region { // If the callback returns a late-bound region, // that region should always use the INNERMOST // debruijn index. Then we adjust it to the // correct depth. assert_eq!(debruijn1, ty::INNERMOST); - self.tcx.mk_region(ty::ReLateBound(debruijn, br)) + self.tcx.reuse_or_mk_region(region, ty::ReLateBound(debruijn, br)) } else { region } @@ -442,7 +467,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for BoundVarReplacer<'a, 'tcx> { fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { match ct.kind() { ty::ConstKind::Bound(debruijn, bound_const) if debruijn == self.current_index => { - let ct = (self.fld_c)(bound_const, ct.ty()); + let ct = self.delegate.replace_const(bound_const, ct.ty()); ty::fold::shift_vars(self.tcx, ct, self.current_index.as_u32()) } _ => ct.super_fold_with(self), @@ -486,19 +511,22 @@ impl<'tcx> TyCtxt<'tcx> { pub fn replace_late_bound_regions_uncached<T, F>( self, value: Binder<'tcx, T>, - mut fld_r: F, + replace_regions: F, ) -> T where F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>, T: TypeFoldable<'tcx>, { - let mut fld_t = |b| bug!("unexpected bound ty in binder: {b:?}"); - let mut fld_c = |b, ty| bug!("unexpected bound ct in binder: {b:?} {ty}"); let value = value.skip_binder(); if !value.has_escaping_bound_vars() { value } else { - let mut replacer = BoundVarReplacer::new(self, &mut fld_r, &mut fld_t, &mut fld_c); + let delegate = FnMutDelegate { + regions: replace_regions, + types: |b| bug!("unexpected bound ty in binder: {b:?}"), + consts: |b, ty| bug!("unexpected bound ct in binder: {b:?} {ty}"), + }; + let mut replacer = BoundVarReplacer::new(self, delegate); value.fold_with(&mut replacer) } } @@ -506,23 +534,15 @@ impl<'tcx> TyCtxt<'tcx> { /// Replaces all escaping bound vars. The `fld_r` closure replaces escaping /// bound regions; the `fld_t` closure replaces escaping bound types and the `fld_c` /// closure replaces escaping bound consts. - pub fn replace_escaping_bound_vars_uncached<T, F, G, H>( + pub fn replace_escaping_bound_vars_uncached<T: TypeFoldable<'tcx>>( self, value: T, - mut fld_r: F, - mut fld_t: G, - mut fld_c: H, - ) -> T - where - F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>, - G: FnMut(ty::BoundTy) -> Ty<'tcx>, - H: FnMut(ty::BoundVar, Ty<'tcx>) -> ty::Const<'tcx>, - T: TypeFoldable<'tcx>, - { + delegate: impl BoundVarReplacerDelegate<'tcx>, + ) -> T { if !value.has_escaping_bound_vars() { value } else { - let mut replacer = BoundVarReplacer::new(self, &mut fld_r, &mut fld_t, &mut fld_c); + let mut replacer = BoundVarReplacer::new(self, delegate); value.fold_with(&mut replacer) } } @@ -530,20 +550,12 @@ impl<'tcx> TyCtxt<'tcx> { /// Replaces all types or regions bound by the given `Binder`. The `fld_r` /// closure replaces bound regions, the `fld_t` closure replaces bound /// types, and `fld_c` replaces bound constants. - pub fn replace_bound_vars_uncached<T, F, G, H>( + pub fn replace_bound_vars_uncached<T: TypeFoldable<'tcx>>( self, value: Binder<'tcx, T>, - fld_r: F, - fld_t: G, - fld_c: H, - ) -> T - where - F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>, - G: FnMut(ty::BoundTy) -> Ty<'tcx>, - H: FnMut(ty::BoundVar, Ty<'tcx>) -> ty::Const<'tcx>, - T: TypeFoldable<'tcx>, - { - self.replace_escaping_bound_vars_uncached(value.skip_binder(), fld_r, fld_t, fld_c) + delegate: impl BoundVarReplacerDelegate<'tcx>, + ) -> T { + self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate) } /// Replaces any late-bound regions bound in `value` with @@ -568,34 +580,28 @@ impl<'tcx> TyCtxt<'tcx> { where T: TypeFoldable<'tcx>, { + let shift_bv = |bv: ty::BoundVar| ty::BoundVar::from_usize(bv.as_usize() + bound_vars); self.replace_escaping_bound_vars_uncached( value, - |r| { - self.mk_region(ty::ReLateBound( - ty::INNERMOST, - ty::BoundRegion { - var: ty::BoundVar::from_usize(r.var.as_usize() + bound_vars), - kind: r.kind, - }, - )) - }, - |t| { - self.mk_ty(ty::Bound( - ty::INNERMOST, - ty::BoundTy { - var: ty::BoundVar::from_usize(t.var.as_usize() + bound_vars), - kind: t.kind, - }, - )) - }, - |c, ty| { - self.mk_const(ty::ConstS { - kind: ty::ConstKind::Bound( + FnMutDelegate { + regions: |r: ty::BoundRegion| { + self.mk_region(ty::ReLateBound( ty::INNERMOST, - ty::BoundVar::from_usize(c.as_usize() + bound_vars), - ), - ty, - }) + ty::BoundRegion { var: shift_bv(r.var), kind: r.kind }, + )) + }, + types: |t: ty::BoundTy| { + self.mk_ty(ty::Bound( + ty::INNERMOST, + ty::BoundTy { var: shift_bv(t.var), kind: t.kind }, + )) + }, + consts: |c, ty: Ty<'tcx>| { + self.mk_const(ty::ConstS { + kind: ty::ConstKind::Bound(ty::INNERMOST, shift_bv(c)), + ty, + }) + }, }, ) } @@ -638,6 +644,51 @@ impl<'tcx> TyCtxt<'tcx> { ); Binder::bind_with_vars(inner, bound_vars) } + + /// Anonymize all bound variables in `value`, this is mostly used to improve caching. + pub fn anonymize_bound_vars<T>(self, value: Binder<'tcx, T>) -> Binder<'tcx, T> + where + T: TypeFoldable<'tcx>, + { + struct Anonymize<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + map: &'a mut FxIndexMap<ty::BoundVar, ty::BoundVariableKind>, + } + impl<'tcx> BoundVarReplacerDelegate<'tcx> for Anonymize<'_, 'tcx> { + fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx> { + let entry = self.map.entry(br.var); + let index = entry.index(); + let var = ty::BoundVar::from_usize(index); + let kind = entry + .or_insert_with(|| ty::BoundVariableKind::Region(ty::BrAnon(index as u32))) + .expect_region(); + let br = ty::BoundRegion { var, kind }; + self.tcx.mk_region(ty::ReLateBound(ty::INNERMOST, br)) + } + fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx> { + let entry = self.map.entry(bt.var); + let index = entry.index(); + let var = ty::BoundVar::from_usize(index); + let kind = entry + .or_insert_with(|| ty::BoundVariableKind::Ty(ty::BoundTyKind::Anon)) + .expect_ty(); + self.tcx.mk_ty(ty::Bound(ty::INNERMOST, BoundTy { var, kind })) + } + fn replace_const(&mut self, bv: ty::BoundVar, ty: Ty<'tcx>) -> ty::Const<'tcx> { + let entry = self.map.entry(bv); + let index = entry.index(); + let var = ty::BoundVar::from_usize(index); + let () = entry.or_insert_with(|| ty::BoundVariableKind::Const).expect_const(); + self.tcx.mk_const(ty::ConstS { ty, kind: ty::ConstKind::Bound(ty::INNERMOST, var) }) + } + } + + let mut map = Default::default(); + let delegate = Anonymize { tcx: self, map: &mut map }; + let inner = self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate); + let bound_vars = self.mk_bound_variable_kinds(map.into_values()); + Binder::bind_with_vars(inner, bound_vars) + } } /////////////////////////////////////////////////////////////////////////// diff --git a/compiler/rustc_middle/src/ty/impls_ty.rs b/compiler/rustc_middle/src/ty/impls_ty.rs index 88397a2bb56..cd00b26b8de 100644 --- a/compiler/rustc_middle/src/ty/impls_ty.rs +++ b/compiler/rustc_middle/src/ty/impls_ty.rs @@ -101,61 +101,21 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ty::subst::GenericArgKin } } -impl<'a> HashStable<StableHashingContext<'a>> for ty::EarlyBoundRegion { - #[inline] - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - self.def_id.hash_stable(hcx, hasher); - self.index.hash_stable(hcx, hasher); - self.name.hash_stable(hcx, hasher); - } -} - -impl<'a> HashStable<StableHashingContext<'a>> for ty::RegionVid { - #[inline] - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - self.index().hash_stable(hcx, hasher); - } -} - -impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ty::ConstVid<'tcx> { - #[inline] - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - self.index.hash_stable(hcx, hasher); - } -} - -impl<'tcx> HashStable<StableHashingContext<'tcx>> for ty::BoundVar { - #[inline] - fn hash_stable(&self, hcx: &mut StableHashingContext<'tcx>, hasher: &mut StableHasher) { - self.index().hash_stable(hcx, hasher); - } -} - -impl<'a, 'tcx, T> HashStable<StableHashingContext<'a>> for ty::Binder<'tcx, T> -where - T: HashStable<StableHashingContext<'a>>, -{ - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - self.as_ref().skip_binder().hash_stable(hcx, hasher); - self.bound_vars().hash_stable(hcx, hasher); - } -} - // AllocIds get resolved to whatever they point to (to be stable) impl<'a> HashStable<StableHashingContext<'a>> for mir::interpret::AllocId { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { ty::tls::with_opt(|tcx| { trace!("hashing {:?}", *self); let tcx = tcx.expect("can't hash AllocIds during hir lowering"); - tcx.get_global_alloc(*self).hash_stable(hcx, hasher); + tcx.try_get_global_alloc(*self).hash_stable(hcx, hasher); }); } } // `Relocations` with default type parameters is a sorted map. -impl<'a, Tag> HashStable<StableHashingContext<'a>> for mir::interpret::Relocations<Tag> +impl<'a, Prov> HashStable<StableHashingContext<'a>> for mir::interpret::Relocations<Prov> where - Tag: HashStable<StableHashingContext<'a>>, + Prov: HashStable<StableHashingContext<'a>>, { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.len().hash_stable(hcx, hasher); diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 4f9bbc135ec..33a46f809b0 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -49,7 +49,7 @@ pub enum InstanceDef<'tcx> { /// /// The generated shim will take `Self` via `*mut Self` - conceptually this is `&owned Self` - /// and dereference the argument to call the original function. - VtableShim(DefId), + VTableShim(DefId), /// `fn()` pointer where the function itself cannot be turned into a pointer. /// @@ -145,7 +145,7 @@ impl<'tcx> InstanceDef<'tcx> { pub fn def_id(self) -> DefId { match self { InstanceDef::Item(def) => def.did, - InstanceDef::VtableShim(def_id) + InstanceDef::VTableShim(def_id) | InstanceDef::ReifyShim(def_id) | InstanceDef::FnPtrShim(def_id, _) | InstanceDef::Virtual(def_id, _) @@ -161,7 +161,7 @@ impl<'tcx> InstanceDef<'tcx> { match self { ty::InstanceDef::Item(def) => Some(def.did), ty::InstanceDef::DropGlue(def_id, Some(_)) => Some(def_id), - InstanceDef::VtableShim(..) + InstanceDef::VTableShim(..) | InstanceDef::ReifyShim(..) | InstanceDef::FnPtrShim(..) | InstanceDef::Virtual(..) @@ -176,7 +176,7 @@ impl<'tcx> InstanceDef<'tcx> { pub fn with_opt_param(self) -> ty::WithOptConstParam<DefId> { match self { InstanceDef::Item(def) => def, - InstanceDef::VtableShim(def_id) + InstanceDef::VTableShim(def_id) | InstanceDef::ReifyShim(def_id) | InstanceDef::FnPtrShim(def_id, _) | InstanceDef::Virtual(def_id, _) @@ -273,7 +273,7 @@ impl<'tcx> InstanceDef<'tcx> { | InstanceDef::Intrinsic(..) | InstanceDef::ReifyShim(..) | InstanceDef::Virtual(..) - | InstanceDef::VtableShim(..) => true, + | InstanceDef::VTableShim(..) => true, } } } @@ -290,7 +290,7 @@ impl<'tcx> fmt::Display for Instance<'tcx> { match self.def { InstanceDef::Item(_) => Ok(()), - InstanceDef::VtableShim(_) => write!(f, " - shim(vtable)"), + InstanceDef::VTableShim(_) => write!(f, " - shim(vtable)"), InstanceDef::ReifyShim(_) => write!(f, " - shim(reify)"), InstanceDef::Intrinsic(_) => write!(f, " - intrinsic"), InstanceDef::Virtual(_, num) => write!(f, " - virtual#{}", num), @@ -434,7 +434,7 @@ impl<'tcx> Instance<'tcx> { && tcx.generics_of(def_id).has_self; if is_vtable_shim { debug!(" => associated item with unsizeable self: Self"); - Some(Instance { def: InstanceDef::VtableShim(def_id), substs }) + Some(Instance { def: InstanceDef::VTableShim(def_id), substs }) } else { Instance::resolve(tcx, param_env, def_id, substs).ok().flatten().map(|mut resolved| { match resolved.def { diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index c41a8318ec5..833edd22805 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -1231,11 +1231,15 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { .collect::<Result<IndexVec<VariantIdx, _>, _>>()?; let offset = st[i].fields().offset(field_index) + niche.offset; - let size = st[i].size(); + + // Align the total size to the largest alignment. + let size = st[i].size().align_to(align.abi); let abi = if st.iter().all(|v| v.abi().is_uninhabited()) { Abi::Uninhabited - } else { + } else if align == st[i].align() && size == st[i].size() { + // When the total alignment and size match, we can use the + // same ABI as the scalar variant with the reserved niche. match st[i].abi() { Abi::Scalar(_) => Abi::Scalar(niche_scalar), Abi::ScalarPair(first, second) => { @@ -1249,6 +1253,8 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { } _ => Abi::Aggregate { sized: true }, } + } else { + Abi::Aggregate { sized: true } }; let largest_niche = Niche::from_scalar(dl, offset, niche_scalar); @@ -2618,14 +2624,14 @@ where // Use conservative pointer kind if not optimizing. This saves us the // Freeze/Unpin queries, and can save time in the codegen backend (noalias // attributes in LLVM have compile-time cost even in unoptimized builds). - PointerKind::Shared + PointerKind::SharedMutable } else { match mt { hir::Mutability::Not => { if ty.is_freeze(tcx.at(DUMMY_SP), cx.param_env()) { PointerKind::Frozen } else { - PointerKind::Shared + PointerKind::SharedMutable } } hir::Mutability::Mut => { @@ -2636,7 +2642,7 @@ where if ty.is_unpin(tcx.at(DUMMY_SP), cx.param_env()) { PointerKind::UniqueBorrowed } else { - PointerKind::Shared + PointerKind::UniqueBorrowedPinned } } } @@ -2771,7 +2777,7 @@ impl<'tcx> ty::Instance<'tcx> { _ => unreachable!(), }; - if let ty::InstanceDef::VtableShim(..) = self.def { + if let ty::InstanceDef::VTableShim(..) = self.def { // Modify `fn(self, ...)` to `fn(self: *mut Self, ...)`. sig = sig.map_bound(|mut sig| { let mut inputs_and_output = sig.inputs_and_output.to_vec(); @@ -3255,10 +3261,13 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { // `Box` (`UniqueBorrowed`) are not necessarily dereferenceable // for the entire duration of the function as they can be deallocated - // at any time. Set their valid size to 0. + // at any time. Same for shared mutable references. If LLVM had a + // way to say "dereferenceable on entry" we could use it here. attrs.pointee_size = match kind { - PointerKind::UniqueOwned => Size::ZERO, - _ => pointee.size, + PointerKind::UniqueBorrowed + | PointerKind::UniqueBorrowedPinned + | PointerKind::Frozen => pointee.size, + PointerKind::SharedMutable | PointerKind::UniqueOwned => Size::ZERO, }; // `Box`, `&T`, and `&mut T` cannot be undef. @@ -3266,7 +3275,12 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { // this attribute doesn't make it UB for the pointed-to data to be undef. attrs.set(ArgAttribute::NoUndef); - // `Box` pointer parameters never alias because ownership is transferred + // The aliasing rules for `Box<T>` are still not decided, but currently we emit + // `noalias` for it. This can be turned off using an unstable flag. + // See https://github.com/rust-lang/unsafe-code-guidelines/issues/326 + let noalias_for_box = + self.tcx().sess.opts.unstable_opts.box_noalias.unwrap_or(true); + // `&mut` pointer parameters never alias other parameters, // or mutable global data // @@ -3280,8 +3294,10 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { // or not to actually emit the attribute. It can also be controlled with the // `-Zmutable-noalias` debugging option. let no_alias = match kind { - PointerKind::Shared | PointerKind::UniqueBorrowed => false, - PointerKind::UniqueOwned => true, + PointerKind::SharedMutable + | PointerKind::UniqueBorrowed + | PointerKind::UniqueBorrowedPinned => false, + PointerKind::UniqueOwned => noalias_for_box, PointerKind::Frozen => !is_return, }; if no_alias { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 53919826bf6..da9d51a29b1 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -575,6 +575,19 @@ impl<'tcx> Predicate<'tcx> { Some(tcx.mk_predicate(kind)) } + + pub fn without_const(mut self, tcx: TyCtxt<'tcx>) -> Self { + if let PredicateKind::Trait(TraitPredicate { trait_ref, constness, polarity }) = self.kind().skip_binder() + && constness != BoundConstness::NotConst + { + self = tcx.mk_predicate(self.kind().rebind(PredicateKind::Trait(TraitPredicate { + trait_ref, + constness: BoundConstness::NotConst, + polarity, + }))); + } + self + } } impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for Predicate<'tcx> { @@ -790,22 +803,15 @@ pub struct TraitPredicate<'tcx> { pub type PolyTraitPredicate<'tcx> = ty::Binder<'tcx, TraitPredicate<'tcx>>; impl<'tcx> TraitPredicate<'tcx> { - pub fn remap_constness(&mut self, tcx: TyCtxt<'tcx>, param_env: &mut ParamEnv<'tcx>) { - if std::intrinsics::unlikely(Some(self.trait_ref.def_id) == tcx.lang_items().drop_trait()) { - // remap without changing constness of this predicate. - // this is because `T: ~const Drop` has a different meaning to `T: Drop` - // FIXME(fee1-dead): remove this logic after beta bump - param_env.remap_constness_with(self.constness) - } else { - *param_env = param_env.with_constness(self.constness.and(param_env.constness())) - } + pub fn remap_constness(&mut self, param_env: &mut ParamEnv<'tcx>) { + *param_env = param_env.with_constness(self.constness.and(param_env.constness())) } /// Remap the constness of this predicate before emitting it for diagnostics. pub fn remap_constness_diag(&mut self, param_env: ParamEnv<'tcx>) { // this is different to `remap_constness` that callees want to print this predicate // in case of selection errors. `T: ~const Drop` bounds cannot end up here when the - // param_env is not const because we it is always satisfied in non-const contexts. + // param_env is not const because it is always satisfied in non-const contexts. if let hir::Constness::NotConst = param_env.constness() { self.constness = ty::BoundConstness::NotConst; } @@ -823,6 +829,14 @@ impl<'tcx> TraitPredicate<'tcx> { pub fn is_const_if_const(self) -> bool { self.constness == BoundConstness::ConstIfConst } + + pub fn is_constness_satisfied_by(self, constness: hir::Constness) -> bool { + match (self.constness, constness) { + (BoundConstness::NotConst, _) + | (BoundConstness::ConstIfConst, hir::Constness::Const) => true, + (BoundConstness::ConstIfConst, hir::Constness::NotConst) => false, + } + } } impl<'tcx> PolyTraitPredicate<'tcx> { @@ -1168,22 +1182,13 @@ impl<'tcx> OpaqueHiddenType<'tcx> { /// identified by both a universe, as well as a name residing within that universe. Distinct bound /// regions/types/consts within the same universe simply have an unknown relationship to one /// another. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, PartialOrd, Ord)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[derive(HashStable, TyEncodable, TyDecodable)] pub struct Placeholder<T> { pub universe: UniverseIndex, pub name: T, } -impl<'a, T> HashStable<StableHashingContext<'a>> for Placeholder<T> -where - T: HashStable<StableHashingContext<'a>>, -{ - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - self.universe.hash_stable(hcx, hasher); - self.name.hash_stable(hcx, hasher); - } -} - pub type PlaceholderRegion = Placeholder<BoundRegionKind>; pub type PlaceholderType = Placeholder<BoundVar>; @@ -1567,6 +1572,7 @@ impl<'tcx> PolyTraitRef<'tcx> { } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TypeFoldable, TypeVisitable)] +#[derive(HashStable)] pub struct ParamEnvAnd<'tcx, T> { pub param_env: ParamEnv<'tcx>, pub value: T, @@ -1584,18 +1590,6 @@ impl<'tcx, T> ParamEnvAnd<'tcx, T> { } } -impl<'a, 'tcx, T> HashStable<StableHashingContext<'a>> for ParamEnvAnd<'tcx, T> -where - T: HashStable<StableHashingContext<'a>>, -{ - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - let ParamEnvAnd { ref param_env, ref value } = *self; - - param_env.hash_stable(hcx, hasher); - value.hash_stable(hcx, hasher); - } -} - #[derive(Copy, Clone, Debug, HashStable, Encodable, Decodable)] pub struct Destructor { /// The `DefId` of the destructor method @@ -2135,7 +2129,7 @@ impl<'tcx> TyCtxt<'tcx> { } } } - ty::InstanceDef::VtableShim(..) + ty::InstanceDef::VTableShim(..) | ty::InstanceDef::ReifyShim(..) | ty::InstanceDef::Intrinsic(..) | ty::InstanceDef::FnPtrShim(..) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 96e84bc8f0a..7f2e81a71a9 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1269,7 +1269,7 @@ pub trait PrettyPrinter<'tcx>: if let ty::Array(elem, len) = inner.kind() { if let ty::Uint(ty::UintTy::U8) = elem.kind() { if let ty::ConstKind::Value(ty::ValTree::Leaf(int)) = len.kind() { - match self.tcx().get_global_alloc(alloc_id) { + match self.tcx().try_get_global_alloc(alloc_id) { Some(GlobalAlloc::Memory(alloc)) => { let len = int.assert_bits(self.tcx().data_layout.pointer_size); let range = @@ -1282,11 +1282,12 @@ pub trait PrettyPrinter<'tcx>: p!("<too short allocation>") } } - // FIXME: for statics and functions, we could in principle print more detail. + // FIXME: for statics, vtables, and functions, we could in principle print more detail. Some(GlobalAlloc::Static(def_id)) => { p!(write("<static({:?})>", def_id)) } Some(GlobalAlloc::Function(_)) => p!("<function>"), + Some(GlobalAlloc::VTable(..)) => p!("<vtable>"), None => p!("<dangling pointer>"), } return Ok(self); @@ -1297,7 +1298,8 @@ pub trait PrettyPrinter<'tcx>: ty::FnPtr(_) => { // FIXME: We should probably have a helper method to share code with the "Byte strings" // printing above (which also has to handle pointers to all sorts of things). - if let Some(GlobalAlloc::Function(instance)) = self.tcx().get_global_alloc(alloc_id) + if let Some(GlobalAlloc::Function(instance)) = + self.tcx().try_get_global_alloc(alloc_id) { self = self.typed_value( |this| this.print_value_path(instance.def_id(), instance.substs), @@ -1377,9 +1379,9 @@ pub trait PrettyPrinter<'tcx>: /// This is overridden for MIR printing because we only want to hide alloc ids from users, not /// from MIR where it is actually useful. - fn pretty_print_const_pointer<Tag: Provenance>( + fn pretty_print_const_pointer<Prov: Provenance>( mut self, - _: Pointer<Tag>, + _: Pointer<Prov>, ty: Ty<'tcx>, print_ty: bool, ) -> Result<Self::Const, Self::Error> { @@ -1450,7 +1452,7 @@ pub trait PrettyPrinter<'tcx>: } }, (ty::ValTree::Branch(_), ty::Array(t, _)) if *t == u8_type => { - let bytes = valtree.try_to_raw_bytes(self.tcx(), *t).unwrap_or_else(|| { + let bytes = valtree.try_to_raw_bytes(self.tcx(), ty).unwrap_or_else(|| { bug!("expected to convert valtree to raw bytes for type {:?}", t) }); p!("*"); @@ -1727,7 +1729,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { } fn print_const(self, ct: ty::Const<'tcx>) -> Result<Self::Const, Self::Error> { - self.pretty_print_const(ct, true) + self.pretty_print_const(ct, false) } fn path_crate(mut self, cnum: CrateNum) -> Result<Self::Path, Self::Error> { @@ -1952,9 +1954,9 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> { } } - fn pretty_print_const_pointer<Tag: Provenance>( + fn pretty_print_const_pointer<Prov: Provenance>( self, - p: Pointer<Tag>, + p: Pointer<Prov>, ty: Ty<'tcx>, print_ty: bool, ) -> Result<Self::Const, Self::Error> { @@ -2554,7 +2556,7 @@ define_print_and_forward_display! { ty::TraitPredicate<'tcx> { p!(print(self.trait_ref.self_ty()), ": "); - if let ty::BoundConstness::ConstIfConst = self.constness { + if let ty::BoundConstness::ConstIfConst = self.constness && cx.tcx().features().const_trait_impl { p!("~const "); } p!(print(self.trait_ref.print_only_trait_path())) diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 391a0a20c96..7660a2f3af6 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -224,6 +224,7 @@ TrivialTypeTraversalAndLiftImpls! { // general `Region`. crate::ty::BoundRegionKind, crate::ty::AssocItem, + crate::ty::AssocKind, crate::ty::Placeholder<crate::ty::BoundRegionKind>, crate::ty::ClosureKind, crate::ty::FreeRegion, @@ -624,7 +625,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::InstanceDef<'a> { fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> { match self { ty::InstanceDef::Item(def_id) => Some(ty::InstanceDef::Item(def_id)), - ty::InstanceDef::VtableShim(def_id) => Some(ty::InstanceDef::VtableShim(def_id)), + ty::InstanceDef::VTableShim(def_id) => Some(ty::InstanceDef::VTableShim(def_id)), ty::InstanceDef::ReifyShim(def_id) => Some(ty::InstanceDef::ReifyShim(def_id)), ty::InstanceDef::Intrinsic(def_id) => Some(ty::InstanceDef::Intrinsic(def_id)), ty::InstanceDef::FnPtrShim(def_id, ty) => { @@ -927,7 +928,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> { substs: self.substs.try_fold_with(folder)?, def: match self.def { Item(def) => Item(def.try_fold_with(folder)?), - VtableShim(did) => VtableShim(did.try_fold_with(folder)?), + VTableShim(did) => VTableShim(did.try_fold_with(folder)?), ReifyShim(did) => ReifyShim(did.try_fold_with(folder)?), Intrinsic(did) => Intrinsic(did.try_fold_with(folder)?), FnPtrShim(did, ty) => { @@ -954,7 +955,7 @@ impl<'tcx> TypeVisitable<'tcx> for ty::instance::Instance<'tcx> { self.substs.visit_with(visitor)?; match self.def { Item(def) => def.visit_with(visitor), - VtableShim(did) | ReifyShim(did) | Intrinsic(did) | Virtual(did, _) => { + VTableShim(did) | ReifyShim(did) | Intrinsic(did) | Virtual(did, _) => { did.visit_with(visitor) } FnPtrShim(did, ty) | CloneShim(did, ty) => { @@ -1122,6 +1123,7 @@ impl<'tcx> TypeVisitable<'tcx> for ty::Predicate<'tcx> { visitor.visit_predicate(*self) } + #[inline] fn has_vars_bound_at_or_above(&self, binder: ty::DebruijnIndex) -> bool { self.outer_exclusive_binder() > binder } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 9f622ad6cd2..411d5c55829 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -976,6 +976,29 @@ pub enum BoundVariableKind { Const, } +impl BoundVariableKind { + pub fn expect_region(self) -> BoundRegionKind { + match self { + BoundVariableKind::Region(lt) => lt, + _ => bug!("expected a region, but found another kind"), + } + } + + pub fn expect_ty(self) -> BoundTyKind { + match self { + BoundVariableKind::Ty(ty) => ty, + _ => bug!("expected a type, but found another kind"), + } + } + + pub fn expect_const(self) { + match self { + BoundVariableKind::Const => (), + _ => bug!("expected a const, but found another kind"), + } + } +} + /// Binder is a binder for higher-ranked lifetimes or types. It is part of the /// compiler's representation for things like `for<'a> Fn(&'a isize)` /// (which would be represented by the type `PolyTraitRef == @@ -986,6 +1009,7 @@ pub enum BoundVariableKind { /// /// `Decodable` and `Encodable` are implemented for `Binder<T>` using the `impl_binder_encode_decode!` macro. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[derive(HashStable)] pub struct Binder<'tcx, T>(T, &'tcx List<BoundVariableKind>); impl<'tcx, T> Binder<'tcx, T> @@ -1332,6 +1356,7 @@ impl<'tcx> fmt::Debug for Region<'tcx> { } #[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, PartialOrd, Ord)] +#[derive(HashStable)] pub struct EarlyBoundRegion { pub def_id: DefId, pub index: u32, @@ -1345,7 +1370,8 @@ impl fmt::Debug for EarlyBoundRegion { } /// A **`const`** **v**ariable **ID**. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(HashStable, TyEncodable, TyDecodable)] pub struct ConstVid<'tcx> { pub index: u32, pub phantom: PhantomData<&'tcx ()>, @@ -1353,6 +1379,7 @@ pub struct ConstVid<'tcx> { rustc_index::newtype_index! { /// A **region** (lifetime) **v**ariable **ID**. + #[derive(HashStable)] pub struct RegionVid { DEBUG_FORMAT = custom, } @@ -1365,6 +1392,7 @@ impl Atom for RegionVid { } rustc_index::newtype_index! { + #[derive(HashStable)] pub struct BoundVar { .. } } diff --git a/compiler/rustc_middle/src/ty/subst.rs b/compiler/rustc_middle/src/ty/subst.rs index 3a524d7b0f3..6262aa18075 100644 --- a/compiler/rustc_middle/src/ty/subst.rs +++ b/compiler/rustc_middle/src/ty/subst.rs @@ -11,7 +11,6 @@ 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::DUMMY_SP; use smallvec::SmallVec; use core::intrinsics; @@ -165,6 +164,14 @@ impl<'tcx> GenericArg<'tcx> { } } + /// Unpack the `GenericArg` as a region when it is known certainly to be a region. + pub fn expect_region(self) -> ty::Region<'tcx> { + match self.unpack() { + GenericArgKind::Lifetime(lt) => lt, + _ => bug!("expected a region, but found another kind"), + } + } + /// Unpack the `GenericArg` as a type when it is known certainly to be a type. /// This is true in cases where `Substs` is used in places where the kinds are known /// to be limited (e.g. in tuples, where the only parameters are type parameters). @@ -525,6 +532,7 @@ struct SubstFolder<'a, 'tcx> { } impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> { + #[inline] fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { self.tcx } @@ -540,6 +548,16 @@ impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> { } fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + #[cold] + #[inline(never)] + fn region_param_out_of_range(data: ty::EarlyBoundRegion) -> ! { + bug!( + "Region parameter out of range when substituting in region {} (index={})", + data.name, + data.index + ) + } + // Note: This routine only handles regions that are bound on // type declarations and other outer declarations, not those // bound in *fn types*. Region substitution of the bound @@ -550,14 +568,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> { let rk = self.substs.get(data.index as usize).map(|k| k.unpack()); match rk { Some(GenericArgKind::Lifetime(lt)) => self.shift_region_through_binders(lt), - _ => { - let msg = format!( - "Region parameter out of range \ - when substituting in region {} (index={})", - data.name, data.index - ); - span_bug!(DUMMY_SP, "{}", msg); - } + _ => region_param_out_of_range(data), } } _ => r, @@ -595,67 +606,80 @@ impl<'a, 'tcx> SubstFolder<'a, 'tcx> { let opt_ty = self.substs.get(p.index as usize).map(|k| k.unpack()); let ty = match opt_ty { Some(GenericArgKind::Type(ty)) => ty, - Some(kind) => { - span_bug!( - DUMMY_SP, - "expected type for `{:?}` ({:?}/{}) but found {:?} \ - when substituting, substs={:?}", - p, - source_ty, - p.index, - kind, - self.substs, - ); - } - None => { - span_bug!( - DUMMY_SP, - "type parameter `{:?}` ({:?}/{}) out of range \ - when substituting, substs={:?}", - p, - source_ty, - p.index, - self.substs, - ); - } + Some(kind) => self.type_param_expected(p, source_ty, kind), + None => self.type_param_out_of_range(p, source_ty), }; self.shift_vars_through_binders(ty) } + #[cold] + #[inline(never)] + fn type_param_expected(&self, p: ty::ParamTy, ty: Ty<'tcx>, kind: GenericArgKind<'tcx>) -> ! { + bug!( + "expected type for `{:?}` ({:?}/{}) but found {:?} when substituting, substs={:?}", + p, + ty, + p.index, + kind, + self.substs, + ) + } + + #[cold] + #[inline(never)] + fn type_param_out_of_range(&self, p: ty::ParamTy, ty: Ty<'tcx>) -> ! { + bug!( + "type parameter `{:?}` ({:?}/{}) out of range when substituting, substs={:?}", + p, + ty, + p.index, + self.substs, + ) + } + fn const_for_param(&self, p: ParamConst, source_ct: ty::Const<'tcx>) -> ty::Const<'tcx> { // Look up the const in the substitutions. It really should be in there. let opt_ct = self.substs.get(p.index as usize).map(|k| k.unpack()); let ct = match opt_ct { Some(GenericArgKind::Const(ct)) => ct, - Some(kind) => { - span_bug!( - DUMMY_SP, - "expected const for `{:?}` ({:?}/{}) but found {:?} \ - when substituting substs={:?}", - p, - source_ct, - p.index, - kind, - self.substs, - ); - } - None => { - span_bug!( - DUMMY_SP, - "const parameter `{:?}` ({:?}/{}) out of range \ - when substituting substs={:?}", - p, - source_ct, - p.index, - self.substs, - ); - } + Some(kind) => self.const_param_expected(p, source_ct, kind), + None => self.const_param_out_of_range(p, source_ct), }; self.shift_vars_through_binders(ct) } + #[cold] + #[inline(never)] + fn const_param_expected( + &self, + p: ty::ParamConst, + ct: ty::Const<'tcx>, + kind: GenericArgKind<'tcx>, + ) -> ! { + bug!( + "expected const for `{:?}` ({:?}/{}) but found {:?} when substituting substs={:?}", + p, + ct, + p.index, + kind, + self.substs, + ) + } + + #[cold] + #[inline(never)] + fn const_param_out_of_range(&self, p: ty::ParamConst, ct: ty::Const<'tcx>) -> ! { + bug!( + "const parameter `{:?}` ({:?}/{}) out of range when substituting substs={:?}", + p, + ct, + p.index, + self.substs, + ) + } + /// It is sometimes necessary to adjust the De Bruijn indices during substitution. This occurs /// when we are substituting a type with escaping bound vars into a context where we have /// passed through binders. That's quite a mouthful. Let's see an example: diff --git a/compiler/rustc_mir_build/src/build/block.rs b/compiler/rustc_mir_build/src/build/block.rs index cb8be51a085..6875600129a 100644 --- a/compiler/rustc_mir_build/src/build/block.rs +++ b/compiler/rustc_mir_build/src/build/block.rs @@ -132,6 +132,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { initializer_span, else_block, visibility_scope, + *remainder_scope, remainder_span, pattern, ) 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 6abf419dd49..e88f9dc1f08 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -7,7 +7,6 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::hir::place::Projection as HirProjection; use rustc_middle::hir::place::ProjectionKind as HirProjectionKind; use rustc_middle::middle::region; -use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::mir::AssertKind::BoundsCheck; use rustc_middle::mir::*; use rustc_middle::thir::*; @@ -72,7 +71,7 @@ pub(crate) enum PlaceBase { /// This is used internally when building a place for an expression like `a.b.c`. The fields `b` /// and `c` can be progressively pushed onto the place builder that is created when converting `a`. #[derive(Clone, Debug, PartialEq)] -pub(in crate::build) struct PlaceBuilder<'tcx> { +pub(crate) struct PlaceBuilder<'tcx> { base: PlaceBase, projection: Vec<PlaceElem<'tcx>>, } @@ -105,8 +104,6 @@ fn convert_to_hir_projections_and_truncate_for_capture<'tcx>( variant = Some(*idx); continue; } - // These do not affect anything, they just make sure we know the right type. - ProjectionElem::OpaqueCast(_) => continue, ProjectionElem::Index(..) | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { @@ -204,10 +201,10 @@ fn find_capture_matching_projections<'a, 'tcx>( /// `PlaceBuilder` now starts from `PlaceBase::Local`. /// /// Returns a Result with the error being the PlaceBuilder (`from_builder`) that was not found. -#[instrument(level = "trace", skip(cx))] -fn to_upvars_resolved_place_builder<'tcx>( +fn to_upvars_resolved_place_builder<'a, 'tcx>( from_builder: PlaceBuilder<'tcx>, - cx: &Builder<'_, 'tcx>, + tcx: TyCtxt<'tcx>, + typeck_results: &'a ty::TypeckResults<'tcx>, ) -> Result<PlaceBuilder<'tcx>, PlaceBuilder<'tcx>> { match from_builder.base { PlaceBase::Local(_) => Ok(from_builder), @@ -222,13 +219,13 @@ fn to_upvars_resolved_place_builder<'tcx>( let Some((capture_index, capture)) = find_capture_matching_projections( - cx.typeck_results, + typeck_results, var_hir_id, closure_def_id, &from_builder.projection, ) else { - let closure_span = cx.tcx.def_span(closure_def_id); - if !enable_precise_capture(cx.tcx, closure_span) { + let closure_span = tcx.def_span(closure_def_id); + if !enable_precise_capture(tcx, closure_span) { bug!( "No associated capture found for {:?}[{:#?}] even though \ capture_disjoint_fields isn't enabled", @@ -245,8 +242,8 @@ fn to_upvars_resolved_place_builder<'tcx>( }; // We won't be building MIR if the closure wasn't local - let closure_hir_id = cx.tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local()); - let closure_ty = cx.typeck_results.node_type(closure_hir_id); + let closure_hir_id = tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local()); + let closure_ty = typeck_results.node_type(closure_hir_id); let substs = match closure_ty.kind() { ty::Closure(_, substs) => ty::UpvarSubsts::Closure(substs), @@ -273,14 +270,12 @@ fn to_upvars_resolved_place_builder<'tcx>( // We used some of the projections to build the capture itself, // now we apply the remaining to the upvar resolved place. - trace!(?capture.place, ?from_builder.projection); let remaining_projections = strip_prefix( capture.place.base_ty, from_builder.projection, &capture.place.projections, ); upvar_resolved_place_builder.projection.extend(remaining_projections); - trace!(?upvar_resolved_place_builder); Ok(upvar_resolved_place_builder) } @@ -299,21 +294,16 @@ fn strip_prefix<'tcx>( prefix_projections: &[HirProjection<'tcx>], ) -> impl Iterator<Item = PlaceElem<'tcx>> { let mut iter = projections.into_iter(); - let mut next = || match iter.next()? { - // Filter out opaque casts, they are unnecessary in the prefix. - ProjectionElem::OpaqueCast(..) => iter.next(), - other => Some(other), - }; for projection in prefix_projections { match projection.kind { HirProjectionKind::Deref => { - assert!(matches!(next(), Some(ProjectionElem::Deref))); + assert!(matches!(iter.next(), Some(ProjectionElem::Deref))); } HirProjectionKind::Field(..) => { if base_ty.is_enum() { - assert!(matches!(next(), Some(ProjectionElem::Downcast(..)))); + assert!(matches!(iter.next(), Some(ProjectionElem::Downcast(..)))); } - assert!(matches!(next(), Some(ProjectionElem::Field(..)))); + assert!(matches!(iter.next(), Some(ProjectionElem::Field(..)))); } HirProjectionKind::Index | HirProjectionKind::Subslice => { bug!("unexpected projection kind: {:?}", projection); @@ -325,32 +315,24 @@ fn strip_prefix<'tcx>( } impl<'tcx> PlaceBuilder<'tcx> { - pub(crate) fn into_place(self, cx: &Builder<'_, 'tcx>) -> Place<'tcx> { + pub(crate) fn into_place<'a>( + self, + tcx: TyCtxt<'tcx>, + typeck_results: &'a ty::TypeckResults<'tcx>, + ) -> Place<'tcx> { if let PlaceBase::Local(local) = self.base { - let mut projections = vec![]; - let mut ty = PlaceTy::from_ty(cx.local_decls[local].ty); - for projection in self.projection { - // Only preserve those opaque casts that actually go from an opaque type - // to another type. - if let ProjectionElem::OpaqueCast(t) = projection { - if let ty::Opaque(..) = ty.ty.kind() { - if t != ty.ty { - projections.push(ProjectionElem::OpaqueCast(t)); - } - } - } else { - projections.push(projection); - } - ty = ty.projection_ty(cx.tcx, projection); - } - Place { local, projection: cx.tcx.intern_place_elems(&projections) } + Place { local, projection: tcx.intern_place_elems(&self.projection) } } else { - self.expect_upvars_resolved(cx).into_place(cx) + self.expect_upvars_resolved(tcx, typeck_results).into_place(tcx, typeck_results) } } - fn expect_upvars_resolved(self, cx: &Builder<'_, 'tcx>) -> PlaceBuilder<'tcx> { - to_upvars_resolved_place_builder(self, cx).unwrap() + fn expect_upvars_resolved<'a>( + self, + tcx: TyCtxt<'tcx>, + typeck_results: &'a ty::TypeckResults<'tcx>, + ) -> PlaceBuilder<'tcx> { + to_upvars_resolved_place_builder(self, tcx, typeck_results).unwrap() } /// Attempts to resolve the `PlaceBuilder`. @@ -364,11 +346,12 @@ impl<'tcx> PlaceBuilder<'tcx> { /// not captured. This can happen because the final mir that will be /// generated doesn't require a read for this place. Failures will only /// happen inside closures. - pub(crate) fn try_upvars_resolved( + pub(crate) fn try_upvars_resolved<'a>( self, - cx: &Builder<'_, 'tcx>, + tcx: TyCtxt<'tcx>, + typeck_results: &'a ty::TypeckResults<'tcx>, ) -> Result<PlaceBuilder<'tcx>, PlaceBuilder<'tcx>> { - to_upvars_resolved_place_builder(self, cx) + to_upvars_resolved_place_builder(self, tcx, typeck_results) } pub(crate) fn base(&self) -> PlaceBase { @@ -428,7 +411,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { expr: &Expr<'tcx>, ) -> BlockAnd<Place<'tcx>> { let place_builder = unpack!(block = self.as_place_builder(block, expr)); - block.and(place_builder.into_place(self)) + block.and(place_builder.into_place(self.tcx, self.typeck_results)) } /// This is used when constructing a compound `Place`, so that we can avoid creating @@ -452,7 +435,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { expr: &Expr<'tcx>, ) -> BlockAnd<Place<'tcx>> { let place_builder = unpack!(block = self.as_read_only_place_builder(block, expr)); - block.and(place_builder.into_place(self)) + block.and(place_builder.into_place(self.tcx, self.typeck_results)) } /// This is used when constructing a compound `Place`, so that we can avoid creating @@ -547,7 +530,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { inferred_ty: expr.ty, }); - let place = place_builder.clone().into_place(this); + let place = place_builder.clone().into_place(this.tcx, this.typeck_results); this.cfg.push( block, Statement { @@ -699,7 +682,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if is_outermost_index { self.read_fake_borrows(block, fake_borrow_temps, source_info) } else { - base_place = base_place.expect_upvars_resolved(self); + base_place = base_place.expect_upvars_resolved(self.tcx, self.typeck_results); self.add_fake_borrows_of_base( &base_place, block, @@ -727,7 +710,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let lt = self.temp(bool_ty, expr_span); // len = len(slice) - self.cfg.push_assign(block, source_info, len, Rvalue::Len(slice.into_place(self))); + self.cfg.push_assign( + block, + source_info, + len, + Rvalue::Len(slice.into_place(self.tcx, self.typeck_results)), + ); // lt = idx < len self.cfg.push_assign( block, @@ -807,7 +795,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } ProjectionElem::Field(..) | ProjectionElem::Downcast(..) - | ProjectionElem::OpaqueCast(..) | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => (), } 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 93f76333165..15f2d17c4e0 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -321,8 +321,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let place_builder = unpack!(block = this.as_place_builder(block, &this.thir[*thir_place])); - if let Ok(place_builder_resolved) = place_builder.try_upvars_resolved(this) { - let mir_place = place_builder_resolved.into_place(this); + if let Ok(place_builder_resolved) = + place_builder.try_upvars_resolved(this.tcx, this.typeck_results) + { + let mir_place = + place_builder_resolved.into_place(this.tcx, this.typeck_results); this.cfg.push_fake_read( block, this.source_info(this.tcx.hir().span(*hir_id)), @@ -613,7 +616,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // by the parent itself. The mutability of the current capture // is same as that of the capture in the parent closure. PlaceBase::Upvar { .. } => { - let enclosing_upvars_resolved = arg_place_builder.clone().into_place(this); + let enclosing_upvars_resolved = + arg_place_builder.clone().into_place(this.tcx, this.typeck_results); match enclosing_upvars_resolved.as_ref() { PlaceRef { @@ -650,7 +654,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false }, }; - let arg_place = arg_place_builder.into_place(this); + let arg_place = arg_place_builder.into_place(this.tcx, this.typeck_results); this.cfg.push_assign( block, 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 dac8862647a..724b72f8769 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_temp.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_temp.rs @@ -23,7 +23,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ensure_sufficient_stack(|| self.as_temp_inner(block, temp_lifetime, expr, mutability)) } - #[instrument(skip(self), level = "debug")] fn as_temp_inner( &mut self, mut block: BasicBlock, @@ -31,6 +30,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { expr: &Expr<'tcx>, mutability: Mutability, ) -> BlockAnd<Local> { + debug!( + "as_temp(block={:?}, temp_lifetime={:?}, expr={:?}, mutability={:?})", + block, temp_lifetime, expr, mutability + ); let this = self; let expr_span = expr.span; diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index d1ef515b3d2..017d43d10a9 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -15,13 +15,14 @@ use std::iter; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Compile `expr`, storing the result into `destination`, which /// is assumed to be uninitialized. - #[instrument(level = "debug", skip(self))] pub(crate) fn expr_into_dest( &mut self, destination: Place<'tcx>, mut block: BasicBlock, expr: &Expr<'tcx>, ) -> BlockAnd<()> { + debug!("expr_into_dest(destination={:?}, block={:?}, expr={:?})", destination, block, expr); + // since we frequently have to reference `self` from within a // closure, where `self` would be shadowed, it's easier to // just use the name `this` uniformly @@ -365,7 +366,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { None => { let place_builder = place_builder.clone(); this.consume_by_copy_or_move( - place_builder.field(n, *ty).into_place(this), + place_builder + .field(n, *ty) + .into_place(this.tcx, this.typeck_results), ) } }) diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 86ef666eac8..58b1564cc5d 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -220,8 +220,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let cause_matched_place = FakeReadCause::ForMatchedPlace(None); let source_info = self.source_info(scrutinee_span); - if let Ok(scrutinee_builder) = scrutinee_place_builder.clone().try_upvars_resolved(self) { - let scrutinee_place = scrutinee_builder.into_place(self); + if let Ok(scrutinee_builder) = + scrutinee_place_builder.clone().try_upvars_resolved(self.tcx, self.typeck_results) + { + let scrutinee_place = scrutinee_builder.into_place(self.tcx, self.typeck_results); self.cfg.push_fake_read(block, source_info, cause_matched_place, scrutinee_place); } @@ -346,10 +348,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // ``` let mut opt_scrutinee_place: Option<(Option<&Place<'tcx>>, Span)> = None; let scrutinee_place: Place<'tcx>; - if let Ok(scrutinee_builder) = - scrutinee_place_builder.clone().try_upvars_resolved(this) + if let Ok(scrutinee_builder) = scrutinee_place_builder + .clone() + .try_upvars_resolved(this.tcx, this.typeck_results) { - scrutinee_place = scrutinee_builder.into_place(this); + scrutinee_place = + scrutinee_builder.into_place(this.tcx, this.typeck_results); opt_scrutinee_place = Some((Some(&scrutinee_place), scrutinee_span)); } let scope = this.declare_bindings( @@ -598,6 +602,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { while let Some(next) = { for binding in &candidate_ref.bindings { let local = self.var_local_id(binding.var_id, OutsideGuard); + + let Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var( + VarBindingForm { opt_match_place: Some((ref mut match_place, _)), .. }, + )))) = self.local_decls[local].local_info else { + bug!("Let binding to non-user variable.") + }; // `try_upvars_resolved` may fail if it is unable to resolve the given // `PlaceBuilder` inside a closure. In this case, we don't want to include // a scrutinee place. `scrutinee_place_builder` will fail for destructured @@ -612,15 +622,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // let (v1, v2) = foo; // }; // ``` - if let Ok(match_pair_resolved) = initializer.clone().try_upvars_resolved(self) { - let place = match_pair_resolved.into_place(self); - - let Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var( - VarBindingForm { opt_match_place: Some((ref mut match_place, _)), .. }, - )))) = self.local_decls[local].local_info else { - bug!("Let binding to non-user variable.") - }; - + if let Ok(match_pair_resolved) = + initializer.clone().try_upvars_resolved(self.tcx, self.typeck_results) + { + let place = match_pair_resolved.into_place(self.tcx, self.typeck_results); *match_place = Some(place); } } @@ -649,7 +654,6 @@ 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. - #[instrument(skip(self), level = "debug")] pub(crate) fn declare_bindings( &mut self, mut visibility_scope: Option<SourceScope>, @@ -658,6 +662,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { has_guard: ArmHasGuard, opt_match_place: Option<(Option<&Place<'tcx>>, Span)>, ) -> Option<SourceScope> { + debug!("declare_bindings: pattern={:?}", pattern); self.visit_primary_bindings( &pattern, UserTypeProjections::none(), @@ -867,7 +872,7 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> { Candidate { span: pattern.span, has_guard, - match_pairs: smallvec![MatchPair::new(place, pattern)], + match_pairs: smallvec![MatchPair { place, pattern }], bindings: Vec::new(), ascriptions: Vec::new(), subcandidates: Vec::new(), @@ -1043,7 +1048,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// if `x.0` matches `false` (for the third arm). In the (impossible at /// runtime) case when `x.0` is now `true`, we branch to /// `otherwise_block`. - #[instrument(skip(self, fake_borrows), level = "debug")] fn match_candidates<'pat>( &mut self, span: Span, @@ -1053,6 +1057,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { candidates: &mut [&mut Candidate<'pat, 'tcx>], fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>, ) { + debug!( + "matched_candidate(span={:?}, candidates={:?}, start_block={:?}, otherwise_block={:?})", + span, candidates, start_block, otherwise_block, + ); + // Start by simplifying candidates. Once this process is complete, all // the match pairs which remain require some form of test, whether it // be a switch or pattern comparison. @@ -1371,10 +1380,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ) } - #[instrument( - skip(self, otherwise, or_span, place, fake_borrows, candidate, pats), - level = "debug" - )] fn test_or_pattern<'pat>( &mut self, candidate: &mut Candidate<'pat, 'tcx>, @@ -1384,7 +1389,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { place: PlaceBuilder<'tcx>, fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>, ) { - debug!("candidate={:#?}\npats={:#?}", candidate, pats); + debug!("test_or_pattern:\ncandidate={:#?}\npats={:#?}", candidate, pats); let mut or_candidates: Vec<_> = pats .iter() .map(|pat| Candidate::new(place.clone(), pat, candidate.has_guard)) @@ -1600,9 +1605,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Insert a Shallow borrow of any places that is switched on. if let Some(fb) = fake_borrows && let Ok(match_place_resolved) = - match_place.clone().try_upvars_resolved(self) + match_place.clone().try_upvars_resolved(self.tcx, self.typeck_results) { - let resolved_place = match_place_resolved.into_place(self); + let resolved_place = match_place_resolved.into_place(self.tcx, self.typeck_results); fb.insert(resolved_place); } @@ -1629,14 +1634,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { candidates = rest; } // at least the first candidate ought to be tested - assert!( - total_candidate_count > candidates.len(), - "{}, {:#?}", - total_candidate_count, - candidates - ); - debug!("tested_candidates: {}", total_candidate_count - candidates.len()); - debug!("untested_candidates: {}", candidates.len()); + assert!(total_candidate_count > candidates.len()); + debug!("test_candidates: tested_candidates: {}", total_candidate_count - candidates.len()); + debug!("test_candidates: untested_candidates: {}", candidates.len()); // HACK(matthewjasper) This is a closure so that we can let the test // create its blocks before the rest of the match. This currently @@ -1794,8 +1794,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); let mut opt_expr_place: Option<(Option<&Place<'tcx>>, Span)> = None; let expr_place: Place<'tcx>; - if let Ok(expr_builder) = expr_place_builder.try_upvars_resolved(self) { - expr_place = expr_builder.into_place(self); + if let Ok(expr_builder) = + expr_place_builder.try_upvars_resolved(self.tcx, self.typeck_results) + { + expr_place = expr_builder.into_place(self.tcx, self.typeck_results); opt_expr_place = Some((Some(&expr_place), expr_span)); } let otherwise_post_guard_block = otherwise_candidate.pre_binding_block.unwrap(); @@ -2193,7 +2195,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// first local is a binding for occurrences of `var` in the guard, which /// will have type `&T`. The second local is a binding for occurrences of /// `var` in the arm body, which will have type `T`. - #[instrument(skip(self), level = "debug")] fn declare_binding( &mut self, source_info: SourceInfo, @@ -2208,12 +2209,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { opt_match_place: Option<(Option<Place<'tcx>>, Span)>, pat_span: Span, ) { + debug!( + "declare_binding(var_id={:?}, name={:?}, mode={:?}, var_ty={:?}, \ + visibility_scope={:?}, source_info={:?})", + var_id, name, mode, var_ty, visibility_scope, source_info + ); + let tcx = self.tcx; let debug_source_info = SourceInfo { span: source_info.span, scope: visibility_scope }; let binding_mode = match mode { BindingMode::ByValue => ty::BindingMode::BindByValue(mutability), BindingMode::ByRef(_) => ty::BindingMode::BindByReference(mutability), }; + debug!("declare_binding: user_ty={:?}", user_ty); let local = LocalDecl::<'tcx> { mutability, ty: var_ty, @@ -2263,7 +2271,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } else { LocalsForNode::One(for_arm_body) }; - debug!(?locals); + debug!("declare_binding: vars={:?}", locals); self.var_indices.insert(var_id, locals); } @@ -2274,49 +2282,55 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { initializer_span: Span, else_block: &Block, visibility_scope: Option<SourceScope>, + remainder_scope: region::Scope, remainder_span: Span, pattern: &Pat<'tcx>, ) -> BlockAnd<()> { - let scrutinee = unpack!(block = self.lower_scrutinee(block, init, initializer_span)); - let pat = Pat { ty: init.ty, span: else_block.span, kind: Box::new(PatKind::Wild) }; - let mut wildcard = Candidate::new(scrutinee.clone(), &pat, false); - self.declare_bindings( - visibility_scope, - remainder_span, - pattern, - ArmHasGuard(false), - Some((None, initializer_span)), - ); - let mut candidate = Candidate::new(scrutinee.clone(), pattern, false); - let fake_borrow_temps = self.lower_match_tree( - block, - initializer_span, - pattern.span, - false, - &mut [&mut candidate, &mut wildcard], - ); - // This block is for the matching case - let matching = self.bind_pattern( - self.source_info(pattern.span), - candidate, - None, - &fake_borrow_temps, - initializer_span, - None, - None, - None, - ); - // This block is for the failure case - let failure = self.bind_pattern( - self.source_info(else_block.span), - wildcard, - None, - &fake_borrow_temps, - initializer_span, - None, - None, - None, - ); + let (matching, failure) = self.in_if_then_scope(remainder_scope, |this| { + let scrutinee = unpack!(block = this.lower_scrutinee(block, init, initializer_span)); + let pat = Pat { ty: init.ty, span: else_block.span, kind: Box::new(PatKind::Wild) }; + let mut wildcard = Candidate::new(scrutinee.clone(), &pat, false); + this.declare_bindings( + visibility_scope, + remainder_span, + pattern, + ArmHasGuard(false), + Some((None, initializer_span)), + ); + let mut candidate = Candidate::new(scrutinee.clone(), pattern, false); + let fake_borrow_temps = this.lower_match_tree( + block, + initializer_span, + pattern.span, + false, + &mut [&mut candidate, &mut wildcard], + ); + // This block is for the matching case + let matching = this.bind_pattern( + this.source_info(pattern.span), + candidate, + None, + &fake_borrow_temps, + initializer_span, + None, + None, + None, + ); + // This block is for the failure case + let failure = this.bind_pattern( + this.source_info(else_block.span), + wildcard, + None, + &fake_borrow_temps, + initializer_span, + None, + None, + None, + ); + this.break_for_else(failure, remainder_scope, this.source_info(initializer_span)); + matching.unit() + }); + // This place is not really used because this destination place // should never be used to take values at the end of the failure // block. diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/build/matches/simplify.rs index 6fa817da28a..c6298904140 100644 --- a/compiler/rustc_mir_build/src/build/matches/simplify.rs +++ b/compiler/rustc_mir_build/src/build/matches/simplify.rs @@ -37,13 +37,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// /// only generates a single switch. If this happens this method returns /// `true`. - #[instrument(skip(self, candidate), level = "debug")] pub(super) fn simplify_candidate<'pat>( &mut self, candidate: &mut Candidate<'pat, 'tcx>, ) -> bool { // repeatedly simplify match pairs until fixed point is reached - debug!("{:#?}", candidate); + debug!(?candidate, "simplify_candidate"); // existing_bindings and new_bindings exists to keep the semantics in order. // Reversing the binding order for bindings after `@` changes the binding order in places @@ -156,10 +155,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { 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) { + if let Ok(place_resolved) = + match_pair.place.clone().try_upvars_resolved(self.tcx, self.typeck_results) + { candidate.ascriptions.push(Ascription { annotation: annotation.clone(), - source: place_resolved.into_place(self), + source: place_resolved.into_place(self.tcx, self.typeck_results), variance, }); } @@ -183,10 +184,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ref subpattern, is_primary: _, } => { - if let Ok(place_resolved) = match_pair.place.clone().try_upvars_resolved(self) { + if let Ok(place_resolved) = + match_pair.place.clone().try_upvars_resolved(self.tcx, self.typeck_results) + { candidate.bindings.push(Binding { span: match_pair.pattern.span, - source: place_resolved.into_place(self), + source: place_resolved.into_place(self.tcx, self.typeck_results), var_id: var, binding_mode: mode, }); diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index 63acd731db7..598da80c574 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -144,7 +144,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - #[instrument(skip(self, make_target_blocks, place_builder), level = "debug")] pub(super) fn perform_test( &mut self, match_start_span: Span, @@ -154,9 +153,21 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { test: &Test<'tcx>, make_target_blocks: impl FnOnce(&mut Self) -> Vec<BasicBlock>, ) { - let place = place_builder.into_place(self); - let place_ty = place.ty(&self.local_decls, self.tcx); - debug!(?place, ?place_ty,); + let place: Place<'tcx>; + if let Ok(test_place_builder) = + place_builder.try_upvars_resolved(self.tcx, self.typeck_results) + { + place = test_place_builder.into_place(self.tcx, self.typeck_results); + } else { + return; + } + debug!( + "perform_test({:?}, {:?}: {:?}, {:?})", + block, + place, + place.ty(&self.local_decls, self.tcx), + test + ); let source_info = self.source_info(test.span); match test.kind { @@ -724,7 +735,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // So, if we have a match-pattern like `x @ Enum::Variant(P1, P2)`, // we want to create a set of derived match-patterns like // `(x as Variant).0 @ P1` and `(x as Variant).1 @ P1`. - let downcast_place = match_pair.place.downcast(adt_def, variant_index); // `(x as Variant)` + let elem = + ProjectionElem::Downcast(Some(adt_def.variant(variant_index).name), variant_index); + let downcast_place = match_pair.place.project(elem); // `(x as Variant)` let consequent_match_pairs = subpatterns.iter().map(|subpattern| { // e.g., `(x as Variant).0` let place = downcast_place.clone().field(subpattern.field, subpattern.pattern.ty); diff --git a/compiler/rustc_mir_build/src/build/matches/util.rs b/compiler/rustc_mir_build/src/build/matches/util.rs index 4a7edc517f4..9a1e98d3bb1 100644 --- a/compiler/rustc_mir_build/src/build/matches/util.rs +++ b/compiler/rustc_mir_build/src/build/matches/util.rs @@ -31,15 +31,21 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { suffix: &'pat [Pat<'tcx>], ) { let tcx = self.tcx; - let (min_length, exact_size) = - if let Ok(place_resolved) = place.clone().try_upvars_resolved(self) { - match place_resolved.into_place(self).ty(&self.local_decls, tcx).ty.kind() { - ty::Array(_, length) => (length.eval_usize(tcx, self.param_env), true), - _ => ((prefix.len() + suffix.len()).try_into().unwrap(), false), - } - } else { - ((prefix.len() + suffix.len()).try_into().unwrap(), false) - }; + let (min_length, exact_size) = if let Ok(place_resolved) = + place.clone().try_upvars_resolved(tcx, self.typeck_results) + { + match place_resolved + .into_place(tcx, self.typeck_results) + .ty(&self.local_decls, tcx) + .ty + .kind() + { + ty::Array(_, length) => (length.eval_usize(tcx, self.param_env), true), + _ => ((prefix.len() + suffix.len()).try_into().unwrap(), false), + } + } else { + ((prefix.len() + suffix.len()).try_into().unwrap(), false) + }; match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| { let elem = @@ -94,14 +100,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } impl<'pat, 'tcx> MatchPair<'pat, 'tcx> { - pub(in crate::build) fn new( + pub(crate) fn new( place: PlaceBuilder<'tcx>, pattern: &'pat Pat<'tcx>, ) -> MatchPair<'pat, 'tcx> { - // Force the place type to the pattern's type. - // FIXME(oli-obk): only do this when we don't already know the place type. - // FIXME(oli-obk): can we use this to simplify slice/array pattern hacks? - let place = place.project(ProjectionElem::OpaqueCast(pattern.ty)); MatchPair { place, pattern } } } diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index ab37c480285..b2fd9f25bdd 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -553,7 +553,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Convenience wrapper that pushes a scope and then executes `f` /// to build its contents, popping the scope afterwards. - #[instrument(skip(self, f), level = "debug")] pub(crate) fn in_scope<F, R>( &mut self, region_scope: (region::Scope, SourceInfo), @@ -563,6 +562,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { where F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd<R>, { + debug!("in_scope(region_scope={:?})", region_scope); let source_scope = self.source_scope; let tcx = self.tcx; if let LintLevel::Explicit(current_hir_id) = lint_level { @@ -589,7 +589,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let rv = unpack!(block = f(self)); unpack!(block = self.pop_scope(region_scope, block)); self.source_scope = source_scope; - debug!(?block); + debug!("in_scope: exiting region_scope={:?} block={:?}", region_scope, block); block.and(rv) } @@ -690,7 +690,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } drops.add_entry(block, drop_idx); - // `build_drop_tree` doesn't have access to our source_info, so we + // `build_drop_trees` doesn't have access to our source_info, so we // create a dummy terminator now. `TerminatorKind::Resume` is used // because MIR type checking will panic if it hasn't been overwritten. self.cfg.terminate(block, source_info, TerminatorKind::Resume); @@ -722,7 +722,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } drops.add_entry(block, drop_idx); - // `build_drop_tree` doesn't have access to our source_info, so we + // `build_drop_trees` doesn't have access to our source_info, so we // create a dummy terminator now. `TerminatorKind::Resume` is used // because MIR type checking will panic if it hasn't been overwritten. self.cfg.terminate(block, source_info, TerminatorKind::Resume); diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 1f0d0ce04aa..55ad83f8975 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -626,7 +626,7 @@ pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalD if tcx.is_closure(def.did.to_def_id()) { let hir = tcx.hir(); let owner = hir.enclosing_body_owner(hir.local_def_id_to_hir_id(def.did)); - tcx.ensure().thir_check_unsafety(hir.local_def_id(owner)); + tcx.ensure().thir_check_unsafety(owner); return; } diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 05da33caa91..4eb3607e9cc 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -48,8 +48,6 @@ impl<'tcx> Cx<'tcx> { _ => None, }; - trace!(?expr.ty); - // Now apply adjustments, if any. for adjustment in self.typeck_results.expr_adjustments(hir_expr) { trace!(?expr, ?adjustment); @@ -58,8 +56,6 @@ impl<'tcx> Cx<'tcx> { self.apply_adjustment(hir_expr, expr, adjustment, adjustment_span.unwrap_or(span)); } - trace!(?expr.ty, "after adjustments"); - // Next, wrap this up in the expr's scope. expr = Expr { temp_lifetime, diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index 35a0afd6813..f7351a4caa9 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -21,7 +21,7 @@ pub(crate) fn thir_body<'tcx>( owner_def: ty::WithOptConstParam<LocalDefId>, ) -> Result<(&'tcx Steal<Thir<'tcx>>, ExprId), ErrorGuaranteed> { let hir = tcx.hir(); - let body = hir.body(hir.body_owned_by(hir.local_def_id_to_hir_id(owner_def.did))); + let body = hir.body(hir.body_owned_by(owner_def.did)); let mut cx = Cx::new(tcx, owner_def); if let Some(reported) = cx.typeck_results.tainted_by_errors { return Err(reported); 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 5bd1fad0bcb..063c076474e 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -26,7 +26,7 @@ use rustc_span::{BytePos, Span}; 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)), + Some(def_id) => tcx.hir().body_owned_by(def_id), }; let pattern_arena = TypedArena::default(); @@ -951,7 +951,7 @@ fn adt_defined_here<'p, 'tcx>( let mut span: MultiSpan = if spans.is_empty() { def_span.into() } else { spans.clone().into() }; - span.push_span_label(def_span, String::new()); + span.push_span_label(def_span, ""); for pat in spans { span.push_span_label(pat, "not covered"); } 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 e32e0b11ba4..d6dd0f01794 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 @@ -120,37 +120,35 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { } fn search_for_structural_match_violation(&self, ty: Ty<'tcx>) -> Option<String> { - traits::search_for_structural_match_violation(self.span, self.tcx(), ty, true).map( - |non_sm_ty| { - with_no_trimmed_paths!(match non_sm_ty.kind { - traits::NonStructuralMatchTyKind::Adt(adt) => self.adt_derive_msg(adt), - traits::NonStructuralMatchTyKind::Dynamic => { - "trait objects cannot be used in patterns".to_string() - } - traits::NonStructuralMatchTyKind::Opaque => { - "opaque types cannot be used in patterns".to_string() - } - traits::NonStructuralMatchTyKind::Closure => { - "closures cannot be used in patterns".to_string() - } - traits::NonStructuralMatchTyKind::Generator => { - "generators cannot be used in patterns".to_string() - } - traits::NonStructuralMatchTyKind::Float => { - "floating-point numbers cannot be used in patterns".to_string() - } - traits::NonStructuralMatchTyKind::Param => { - bug!("use of a constant whose type is a parameter inside a pattern") - } - traits::NonStructuralMatchTyKind::Projection => { - bug!("use of a constant whose type is a projection inside a pattern") - } - traits::NonStructuralMatchTyKind::Foreign => { - bug!("use of a value of a foreign type inside a pattern") - } - }) - }, - ) + traits::search_for_structural_match_violation(self.span, self.tcx(), ty).map(|non_sm_ty| { + with_no_trimmed_paths!(match non_sm_ty.kind() { + ty::Adt(adt, _) => self.adt_derive_msg(*adt), + ty::Dynamic(..) => { + "trait objects cannot be used in patterns".to_string() + } + ty::Opaque(..) => { + "opaque types cannot be used in patterns".to_string() + } + ty::Closure(..) => { + "closures cannot be used in patterns".to_string() + } + ty::Generator(..) | ty::GeneratorWitness(..) => { + "generators cannot be used in patterns".to_string() + } + ty::Float(..) => { + "floating-point numbers cannot be used in patterns".to_string() + } + ty::FnPtr(..) => { + "function pointers cannot be used in patterns".to_string() + } + ty::RawPtr(..) => { + "raw pointers cannot be used in patterns".to_string() + } + _ => { + bug!("use of a value of `{non_sm_ty}` inside a pattern") + } + }) + }) } fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool { 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 c0fd19cf71c..60db98073a3 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -1202,32 +1202,35 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { /// Creates a new list of wildcard fields for a given constructor. The result must have a /// length of `constructor.arity()`. - #[instrument(level = "trace")] - pub(super) fn wildcards(pcx: PatCtxt<'_, 'p, 'tcx>, constructor: &Constructor<'tcx>) -> Self { + pub(super) fn wildcards( + cx: &MatchCheckCtxt<'p, 'tcx>, + ty: Ty<'tcx>, + constructor: &Constructor<'tcx>, + ) -> Self { let ret = match constructor { - Single | Variant(_) => match pcx.ty.kind() { - ty::Tuple(fs) => Fields::wildcards_from_tys(pcx.cx, fs.iter()), - ty::Ref(_, rty, _) => Fields::wildcards_from_tys(pcx.cx, once(*rty)), + Single | Variant(_) => match ty.kind() { + ty::Tuple(fs) => Fields::wildcards_from_tys(cx, fs.iter()), + ty::Ref(_, rty, _) => Fields::wildcards_from_tys(cx, once(*rty)), ty::Adt(adt, substs) => { if adt.is_box() { // The only legal patterns of type `Box` (outside `std`) are `_` and box // patterns. If we're here we can assume this is a box pattern. - Fields::wildcards_from_tys(pcx.cx, once(substs.type_at(0))) + Fields::wildcards_from_tys(cx, once(substs.type_at(0))) } else { let variant = &adt.variant(constructor.variant_index_for_adt(*adt)); - let tys = Fields::list_variant_nonhidden_fields(pcx.cx, pcx.ty, variant) + let tys = Fields::list_variant_nonhidden_fields(cx, ty, variant) .map(|(_, ty)| ty); - Fields::wildcards_from_tys(pcx.cx, tys) + Fields::wildcards_from_tys(cx, tys) } } - _ => bug!("Unexpected type for `Single` constructor: {:?}", pcx), + _ => bug!("Unexpected type for `Single` constructor: {:?}", ty), }, - Slice(slice) => match *pcx.ty.kind() { + Slice(slice) => match *ty.kind() { ty::Slice(ty) | ty::Array(ty, _) => { let arity = slice.arity(); - Fields::wildcards_from_tys(pcx.cx, (0..arity).map(|_| ty)) + Fields::wildcards_from_tys(cx, (0..arity).map(|_| ty)) } - _ => bug!("bad slice pattern {:?} {:?}", constructor, pcx), + _ => bug!("bad slice pattern {:?} {:?}", constructor, ty), }, Str(..) | FloatRange(..) @@ -1240,7 +1243,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { bug!("called `Fields::wildcards` on an `Or` ctor") } }; - debug!(?ret); + debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret); ret } @@ -1283,7 +1286,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { /// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern /// `Some(_)`. pub(super) fn wild_from_ctor(pcx: PatCtxt<'_, 'p, 'tcx>, ctor: Constructor<'tcx>) -> Self { - let fields = Fields::wildcards(pcx, &ctor); + let fields = Fields::wildcards(pcx.cx, pcx.ty, &ctor); DeconstructedPat::new(ctor, fields, pcx.ty, DUMMY_SP) } @@ -1550,13 +1553,13 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { /// `other_ctor` can be different from `self.ctor`, but must be covered by it. pub(super) fn specialize<'a>( &'a self, - pcx: PatCtxt<'_, 'p, 'tcx>, + cx: &MatchCheckCtxt<'p, 'tcx>, other_ctor: &Constructor<'tcx>, ) -> SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]> { match (&self.ctor, other_ctor) { (Wildcard, _) => { // We return a wildcard for each field of `other_ctor`. - Fields::wildcards(pcx, other_ctor).iter_patterns().collect() + Fields::wildcards(cx, self.ty, other_ctor).iter_patterns().collect() } (Slice(self_slice), Slice(other_slice)) if self_slice.arity() != other_slice.arity() => @@ -1575,7 +1578,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { let prefix = &self.fields.fields[..prefix]; let suffix = &self.fields.fields[self_slice.arity() - suffix..]; let wildcard: &_ = - pcx.cx.pattern_arena.alloc(DeconstructedPat::wildcard(inner_ty)); + cx.pattern_arena.alloc(DeconstructedPat::wildcard(inner_ty)); let extra_wildcards = other_slice.arity() - self_slice.arity(); let extra_wildcards = (0..extra_wildcards).map(|_| wildcard); prefix.iter().chain(extra_wildcards).chain(suffix).collect() diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 7d22f7b69d8..a13748a2d47 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -196,7 +196,6 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } } - #[instrument(skip(self), level = "debug")] fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Pat<'tcx> { let mut ty = self.typeck_results.node_type(pat.hir_id); diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index f27ec22a8de..9e7a267ecbd 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -411,12 +411,12 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { /// This is roughly the inverse of `Constructor::apply`. fn pop_head_constructor( &self, - pcx: PatCtxt<'_, 'p, 'tcx>, + cx: &MatchCheckCtxt<'p, 'tcx>, ctor: &Constructor<'tcx>, ) -> PatStack<'p, 'tcx> { // We pop the head pattern and push the new fields extracted from the arguments of // `self.head()`. - let mut new_fields: SmallVec<[_; 2]> = self.head().specialize(pcx, ctor); + let mut new_fields: SmallVec<[_; 2]> = self.head().specialize(cx, ctor); new_fields.extend_from_slice(&self.pats[1..]); PatStack::from_vec(new_fields) } @@ -475,7 +475,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { let mut matrix = Matrix::empty(); for row in &self.patterns { if ctor.is_covered_by(pcx, row.head().ctor()) { - let new_row = row.pop_head_constructor(pcx, ctor); + let new_row = row.pop_head_constructor(pcx.cx, ctor); matrix.push(new_row); } } @@ -786,7 +786,7 @@ fn is_useful<'p, 'tcx>( is_under_guard: bool, is_top_level: bool, ) -> Usefulness<'p, 'tcx> { - debug!(?matrix, ?v); + debug!("matrix,v={:?}{:?}", matrix, v); let Matrix { patterns: rows, .. } = matrix; // The base case. We are pattern-matching on () and the return value is @@ -806,6 +806,11 @@ fn is_useful<'p, 'tcx>( debug_assert!(rows.iter().all(|r| r.len() == v.len())); + let ty = v.head().ty(); + let is_non_exhaustive = cx.is_foreign_non_exhaustive_enum(ty); + debug!("v.head: {:?}, v.span: {:?}", v.head(), v.head().span()); + let pcx = PatCtxt { cx, ty, span: v.head().span(), is_top_level, is_non_exhaustive }; + // If the first pattern is an or-pattern, expand it. let mut ret = Usefulness::new_not_useful(witness_preference); if v.head().is_or_pat() { @@ -827,19 +832,6 @@ fn is_useful<'p, 'tcx>( } } } else { - let mut ty = v.head().ty(); - - // Opaque types can't get destructured/split, but the patterns can - // actually hint at hidden types, so we use the patterns' types instead. - if let ty::Opaque(..) = v.head().ty().kind() { - if let Some(row) = rows.first() { - ty = row.head().ty(); - } - } - let is_non_exhaustive = cx.is_foreign_non_exhaustive_enum(ty); - debug!("v.head: {:?}, v.span: {:?}", v.head(), v.head().span()); - let pcx = PatCtxt { cx, ty, span: v.head().span(), is_top_level, is_non_exhaustive }; - let v_ctor = v.head().ctor(); debug!(?v_ctor); if let Constructor::IntRange(ctor_range) = &v_ctor { @@ -861,7 +853,7 @@ fn is_useful<'p, 'tcx>( debug!("specialize({:?})", ctor); // We cache the result of `Fields::wildcards` because it is used a lot. let spec_matrix = start_matrix.specialize_constructor(pcx, &ctor); - let v = v.pop_head_constructor(pcx, &ctor); + let v = v.pop_head_constructor(cx, &ctor); let usefulness = ensure_sufficient_stack(|| { is_useful(cx, &spec_matrix, &v, witness_preference, hir_id, is_under_guard, false) }); diff --git a/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs b/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs index 7806e8f45d3..28936274baa 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs @@ -48,7 +48,6 @@ impl<'tcx> Lift for PlaceElem<'tcx> { match *self { ProjectionElem::Deref => ProjectionElem::Deref, ProjectionElem::Field(f, ty) => ProjectionElem::Field(f, ty.lift()), - ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(ty.lift()), ProjectionElem::Index(ref i) => ProjectionElem::Index(i.lift()), ProjectionElem::Subslice { from, to, from_end } => { ProjectionElem::Subslice { from, to, from_end } diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 19aa71d7bc7..116e5c1f3ce 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -1,3 +1,4 @@ +use crate::move_paths::FxHashMap; use crate::un_derefer::UnDerefer; use rustc_index::vec::IndexVec; use rustc_middle::mir::tcx::RvalueInitializationState; @@ -206,10 +207,13 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { } } +pub type MoveDat<'tcx> = Result< + (FxHashMap<Local, Place<'tcx>>, MoveData<'tcx>), + (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>), +>; + impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { - fn finalize( - self, - ) -> Result<MoveData<'tcx>, (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>)> { + fn finalize(self) -> MoveDat<'tcx> { debug!("{}", { debug!("moves for {:?}:", self.body.span); for (j, mo) in self.data.moves.iter_enumerated() { @@ -222,7 +226,11 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { "done dumping moves" }); - if !self.errors.is_empty() { Err((self.data, self.errors)) } else { Ok(self.data) } + if self.errors.is_empty() { + Ok((self.un_derefer.derefer_sidetable, self.data)) + } else { + Err((self.data, self.errors)) + } } } @@ -230,7 +238,7 @@ pub(super) fn gather_moves<'tcx>( body: &Body<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, -) -> Result<MoveData<'tcx>, (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>)> { +) -> MoveDat<'tcx> { let mut builder = MoveDataBuilder::new(body, tcx, param_env); builder.gather_args(); diff --git a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs index 1af789b4885..a951c5b0b1c 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs @@ -1,3 +1,4 @@ +use crate::move_paths::builder::MoveDat; use rustc_data_structures::fx::FxHashMap; use rustc_index::vec::IndexVec; use rustc_middle::mir::*; @@ -386,7 +387,7 @@ impl<'tcx> MoveData<'tcx> { body: &Body<'tcx>, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, - ) -> Result<Self, (Self, Vec<(Place<'tcx>, MoveError<'tcx>)>)> { + ) -> MoveDat<'tcx> { builder::gather_moves(body, tcx, param_env) } diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs index e1df482786f..f2471f37a52 100644 --- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs +++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs @@ -30,7 +30,7 @@ impl<'tcx> MirPass<'tcx> for SanityCheck { } let param_env = tcx.param_env(def_id); - let move_data = MoveData::gather_moves(body, tcx, param_env).unwrap(); + let (_, move_data) = MoveData::gather_moves(body, tcx, param_env).unwrap(); let mdpe = MoveDataParamEnv { move_data, param_env }; if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_init).is_some() { diff --git a/compiler/rustc_mir_dataflow/src/un_derefer.rs b/compiler/rustc_mir_dataflow/src/un_derefer.rs index ec2e516f7ac..7e6e25cc603 100644 --- a/compiler/rustc_mir_dataflow/src/un_derefer.rs +++ b/compiler/rustc_mir_dataflow/src/un_derefer.rs @@ -1,4 +1,4 @@ -use rustc_data_structures::stable_map::FxHashMap; +use rustc_data_structures::fx::FxHashMap; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; @@ -9,6 +9,7 @@ pub struct UnDerefer<'tcx> { } impl<'tcx> UnDerefer<'tcx> { + #[inline] pub fn derefer(&self, place: PlaceRef<'tcx>, body: &Body<'tcx>) -> Option<Place<'tcx>> { let reffed = self.derefer_sidetable.get(&place.local)?; @@ -18,19 +19,4 @@ impl<'tcx> UnDerefer<'tcx> { } Some(new_place) } - - pub fn ref_finder(&mut self, body: &Body<'tcx>) { - for (_bb, data) in body.basic_blocks().iter_enumerated() { - for stmt in data.statements.iter() { - match stmt.kind { - StatementKind::Assign(box (place, Rvalue::CopyForDeref(reffed))) => { - if body.local_decls[place.local].is_deref_temp() { - self.derefer_sidetable.insert(place.local, reffed); - } - } - _ => (), - } - } - } - } } diff --git a/compiler/rustc_mir_transform/src/add_retag.rs b/compiler/rustc_mir_transform/src/add_retag.rs index 86327ade94b..9c5896c4e4a 100644 --- a/compiler/rustc_mir_transform/src/add_retag.rs +++ b/compiler/rustc_mir_transform/src/add_retag.rs @@ -15,23 +15,9 @@ pub struct AddRetag; /// (Concurrent accesses by other threads are no problem as these are anyway non-atomic /// copies. Data races are UB.) fn is_stable(place: PlaceRef<'_>) -> bool { - place.projection.iter().all(|elem| { - match elem { - // Which place this evaluates to can change with any memory write, - // so cannot assume this to be stable. - ProjectionElem::Deref => false, - // Array indices are interesting, but MIR building generates a *fresh* - // temporary for every array access, so the index cannot be changed as - // a side-effect. - ProjectionElem::Index { .. } | - // The rest is completely boring, they just offset by a constant. - ProjectionElem::Field { .. } | - ProjectionElem::ConstantIndex { .. } | - ProjectionElem::Subslice { .. } | - ProjectionElem::OpaqueCast { .. } | - ProjectionElem::Downcast { .. } => true, - } - }) + // Which place this evaluates to can change with any memory write, + // so cannot assume deref to be stable. + !place.has_deref() } /// Determine whether this type may contain a reference (or box), and thus needs retagging. @@ -92,11 +78,8 @@ impl<'tcx> MirPass<'tcx> for AddRetag { }; let place_base_raw = |place: &Place<'tcx>| { // If this is a `Deref`, get the type of what we are deref'ing. - let deref_base = - place.projection.iter().rposition(|p| matches!(p, ProjectionElem::Deref)); - if let Some(deref_base) = deref_base { - let base_proj = &place.projection[..deref_base]; - let ty = Place::ty_from(place.local, base_proj, &*local_decls, tcx).ty; + if place.has_deref() { + let ty = &local_decls[place.local].ty; ty.is_unsafe_ptr() } else { // Not a deref, and thus not raw. diff --git a/compiler/rustc_mir_transform/src/check_packed_ref.rs b/compiler/rustc_mir_transform/src/check_packed_ref.rs index 2eb38941f1a..3b7ba3f9a67 100644 --- a/compiler/rustc_mir_transform/src/check_packed_ref.rs +++ b/compiler/rustc_mir_transform/src/check_packed_ref.rs @@ -36,13 +36,16 @@ fn unsafe_derive_on_repr_packed(tcx: TyCtxt<'_>, def_id: LocalDefId) { tcx.struct_span_lint_hir(UNALIGNED_REFERENCES, lint_hir_id, tcx.def_span(def_id), |lint| { // FIXME: when we make this a hard error, this should have its // own error code. - let message = if tcx.generics_of(def_id).own_requires_monomorphization() { - "`#[derive]` can't be used on a `#[repr(packed)]` struct with \ - type or const parameters (error E0133)" + let extra = if tcx.generics_of(def_id).own_requires_monomorphization() { + "with type or const parameters" } else { - "`#[derive]` can't be used on a `#[repr(packed)]` struct that \ - does not derive Copy (error E0133)" + "that does not derive `Copy`" }; + let message = format!( + "`{}` can't be derived on this `#[repr(packed)]` struct {}", + tcx.item_name(tcx.trait_id_of_impl(def_id.to_def_id()).expect("derived trait name")), + extra + ); lint.build(message).emit(); }); } diff --git a/compiler/rustc_mir_transform/src/check_unsafety.rs b/compiler/rustc_mir_transform/src/check_unsafety.rs index a2ad96cfc16..13ae8a1cd3e 100644 --- a/compiler/rustc_mir_transform/src/check_unsafety.rs +++ b/compiler/rustc_mir_transform/src/check_unsafety.rs @@ -464,15 +464,15 @@ fn check_unused_unsafe( def_id: LocalDefId, used_unsafe_blocks: &FxHashMap<HirId, UsedUnsafeBlockData>, ) -> Vec<(HirId, UnusedUnsafe)> { - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - let body_id = tcx.hir().maybe_body_owned_by(hir_id); + let body_id = tcx.hir().maybe_body_owned_by(def_id); let Some(body_id) = body_id else { debug!("check_unused_unsafe({:?}) - no body found", def_id); return vec![]; }; - let body = tcx.hir().body(body_id); + let body = tcx.hir().body(body_id); + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); let context = match tcx.hir().fn_sig_by_hir_id(hir_id) { Some(sig) if sig.header.unsafety == hir::Unsafety::Unsafe => Context::UnsafeFn(hir_id), _ => Context::Safe, diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs index 2fd026b1bca..85ad6b8f2fe 100644 --- a/compiler/rustc_mir_transform/src/const_prop.rs +++ b/compiler/rustc_mir_transform/src/const_prop.rs @@ -155,18 +155,18 @@ impl<'tcx> MirPass<'tcx> for ConstProp { } } -struct ConstPropMachine<'mir, 'tcx> { +pub struct ConstPropMachine<'mir, 'tcx> { /// The virtual call stack. stack: Vec<Frame<'mir, 'tcx>>, /// `OnlyInsideOwnBlock` locals that were written in the current block get erased at the end. - written_only_inside_own_block_locals: FxHashSet<Local>, + pub written_only_inside_own_block_locals: FxHashSet<Local>, /// Locals that need to be cleared after every block terminates. - only_propagate_inside_block_locals: BitSet<Local>, - can_const_prop: IndexVec<Local, ConstPropMode>, + pub only_propagate_inside_block_locals: BitSet<Local>, + pub can_const_prop: IndexVec<Local, ConstPropMode>, } impl ConstPropMachine<'_, '_> { - fn new( + pub fn new( only_propagate_inside_block_locals: BitSet<Local>, can_const_prop: IndexVec<Local, ConstPropMode>, ) -> Self { @@ -234,9 +234,9 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> } fn access_local<'a>( - frame: &'a Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>, + frame: &'a Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>, local: Local, - ) -> InterpResult<'tcx, &'a interpret::Operand<Self::PointerTag>> { + ) -> InterpResult<'tcx, &'a interpret::Operand<Self::Provenance>> { let l = &frame.locals[local]; if matches!( @@ -255,7 +255,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> ecx: &'a mut InterpCx<'mir, 'tcx, Self>, frame: usize, local: Local, - ) -> InterpResult<'tcx, &'a mut interpret::Operand<Self::PointerTag>> { + ) -> InterpResult<'tcx, &'a mut interpret::Operand<Self::Provenance>> { if ecx.machine.can_const_prop[local] == ConstPropMode::NoPropagation { throw_machine_stop_str!("tried to write to a local that is marked as not propagatable") } @@ -274,7 +274,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> _tcx: TyCtxt<'tcx>, _machine: &Self, _alloc_id: AllocId, - alloc: ConstAllocation<'tcx, Self::PointerTag, Self::AllocExtra>, + alloc: ConstAllocation<'tcx, Self::Provenance, Self::AllocExtra>, _static_def_id: Option<DefId>, is_write: bool, ) -> InterpResult<'tcx> { @@ -309,14 +309,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> #[inline(always)] fn stack<'a>( ecx: &'a InterpCx<'mir, 'tcx, Self>, - ) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>] { + ) -> &'a [Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>] { &ecx.machine.stack } #[inline(always)] fn stack_mut<'a>( ecx: &'a mut InterpCx<'mir, 'tcx, Self>, - ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>> { + ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>> { &mut ecx.machine.stack } } @@ -816,7 +816,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { /// The mode that `ConstProp` is allowed to run in for a given `Local`. #[derive(Clone, Copy, Debug, PartialEq)] -enum ConstPropMode { +pub enum ConstPropMode { /// The `Local` can be propagated into and reads of this `Local` can also be propagated. FullConstProp, /// The `Local` can only be propagated into and from its own block. @@ -828,7 +828,7 @@ enum ConstPropMode { NoPropagation, } -struct CanConstProp { +pub struct CanConstProp { can_const_prop: IndexVec<Local, ConstPropMode>, // False at the beginning. Once set, no more assignments are allowed to that local. found_assignment: BitSet<Local>, @@ -838,7 +838,7 @@ struct CanConstProp { impl CanConstProp { /// Returns true if `local` can be propagated - fn check<'tcx>( + pub fn check<'tcx>( tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, body: &Body<'tcx>, diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs index 9c843f11c1e..3ae6a88a140 100644 --- a/compiler/rustc_mir_transform/src/const_prop_lint.rs +++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs @@ -1,19 +1,24 @@ //! Propagates constants for early reporting of statically known //! assertion failures -use std::cell::Cell; - -use rustc_ast::Mutability; -use rustc_data_structures::fx::FxHashSet; +use crate::const_prop::CanConstProp; +use crate::const_prop::ConstPropMachine; +use crate::const_prop::ConstPropMode; +use crate::MirLint; +use rustc_const_eval::const_eval::ConstEvalErr; +use rustc_const_eval::interpret::{ + self, InterpCx, InterpResult, LocalState, LocalValue, MemoryKind, OpTy, Scalar, + ScalarMaybeUninit, StackPopCleanup, +}; use rustc_hir::def::DefKind; use rustc_hir::HirId; use rustc_index::bit_set::BitSet; use rustc_index::vec::IndexVec; -use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; +use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::{ - AssertKind, BasicBlock, BinOp, Body, Constant, ConstantKind, Local, LocalDecl, LocalKind, - Location, Operand, Place, Rvalue, SourceInfo, SourceScope, SourceScopeData, Statement, - StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE, + AssertKind, BinOp, Body, Constant, ConstantKind, Local, LocalDecl, Location, Operand, Place, + Rvalue, SourceInfo, SourceScope, SourceScopeData, Statement, StatementKind, Terminator, + TerminatorKind, UnOp, RETURN_PLACE, }; use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::subst::{InternalSubsts, Subst}; @@ -22,42 +27,15 @@ use rustc_middle::ty::{ TypeVisitable, }; use rustc_session::lint; -use rustc_span::{def_id::DefId, Span}; +use rustc_span::Span; use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout}; -use rustc_target::spec::abi::Abi as CallAbi; use rustc_trait_selection::traits; - -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, MemoryKind, OpTy, PlaceTy, Pointer, Scalar, ScalarMaybeUninit, - StackPopCleanup, StackPopUnwind, -}; +use std::cell::Cell; /// The maximum number of bytes that we'll allocate space for a local or the return value. /// Needed for #66397, because otherwise we eval into large places and that can cause OOM or just /// Severely regress performance. const MAX_ALLOC_LIMIT: u64 = 1024; - -/// Macro for machine-specific `InterpError` without allocation. -/// (These will never be shown to the user, but they help diagnose ICEs.) -macro_rules! throw_machine_stop_str { - ($($tt:tt)*) => {{ - // We make a new local type for it. The type itself does not carry any information, - // but its vtable (for the `MachineStopType` trait) does. - struct Zst; - // Printing this type shows the desired string. - impl std::fmt::Display for Zst { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, $($tt)*) - } - } - impl rustc_middle::mir::interpret::MachineStopType for Zst {} - throw_machine_stop!(Zst) - }}; -} - pub struct ConstProp; impl<'tcx> MirLint<'tcx> for ConstProp { @@ -151,172 +129,6 @@ impl<'tcx> MirLint<'tcx> for ConstProp { } } -struct ConstPropMachine<'mir, 'tcx> { - /// The virtual call stack. - stack: Vec<Frame<'mir, 'tcx>>, - /// `OnlyInsideOwnBlock` locals that were written in the current block get erased at the end. - written_only_inside_own_block_locals: FxHashSet<Local>, - /// Locals that need to be cleared after every block terminates. - only_propagate_inside_block_locals: BitSet<Local>, - can_const_prop: IndexVec<Local, ConstPropMode>, -} - -impl ConstPropMachine<'_, '_> { - fn new( - only_propagate_inside_block_locals: BitSet<Local>, - can_const_prop: IndexVec<Local, ConstPropMode>, - ) -> Self { - Self { - stack: Vec::new(), - written_only_inside_own_block_locals: Default::default(), - only_propagate_inside_block_locals, - can_const_prop, - } - } -} - -impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> { - compile_time_machine!(<'mir, 'tcx>); - const PANIC_ON_ALLOC_FAIL: bool = true; // all allocations are small (see `MAX_ALLOC_LIMIT`) - - type MemoryKind = !; - - fn load_mir( - _ecx: &InterpCx<'mir, 'tcx, Self>, - _instance: ty::InstanceDef<'tcx>, - ) -> InterpResult<'tcx, &'tcx Body<'tcx>> { - throw_machine_stop_str!("calling functions isn't supported in ConstProp") - } - - fn find_mir_or_eval_fn( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _instance: ty::Instance<'tcx>, - _abi: CallAbi, - _args: &[OpTy<'tcx>], - _destination: &PlaceTy<'tcx>, - _target: Option<BasicBlock>, - _unwind: StackPopUnwind, - ) -> InterpResult<'tcx, Option<(&'mir Body<'tcx>, ty::Instance<'tcx>)>> { - Ok(None) - } - - fn call_intrinsic( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _instance: ty::Instance<'tcx>, - _args: &[OpTy<'tcx>], - _destination: &PlaceTy<'tcx>, - _target: Option<BasicBlock>, - _unwind: StackPopUnwind, - ) -> InterpResult<'tcx> { - throw_machine_stop_str!("calling intrinsics isn't supported in ConstProp") - } - - fn assert_panic( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _msg: &rustc_middle::mir::AssertMessage<'tcx>, - _unwind: Option<rustc_middle::mir::BasicBlock>, - ) -> InterpResult<'tcx> { - bug!("panics terminators are not evaluated in ConstProp") - } - - fn binary_ptr_op( - _ecx: &InterpCx<'mir, 'tcx, Self>, - _bin_op: BinOp, - _left: &ImmTy<'tcx>, - _right: &ImmTy<'tcx>, - ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> { - // We can't do this because aliasing of memory can differ between const eval and llvm - throw_machine_stop_str!("pointer arithmetic or comparisons aren't supported in ConstProp") - } - - fn access_local<'a>( - frame: &'a Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>, - local: Local, - ) -> InterpResult<'tcx, &'a interpret::Operand<Self::PointerTag>> { - let l = &frame.locals[local]; - - if matches!( - l.value, - LocalValue::Live(interpret::Operand::Immediate(interpret::Immediate::Uninit)) - ) { - // For us "uninit" means "we don't know its value, might be initiailized or not". - // So stop here. - throw_machine_stop_str!("tried to access a local with unknown value") - } - - l.access() - } - - fn access_local_mut<'a>( - ecx: &'a mut InterpCx<'mir, 'tcx, Self>, - frame: usize, - local: Local, - ) -> InterpResult<'tcx, &'a mut interpret::Operand<Self::PointerTag>> { - if ecx.machine.can_const_prop[local] == ConstPropMode::NoPropagation { - throw_machine_stop_str!("tried to write to a local that is marked as not propagatable") - } - if frame == 0 && ecx.machine.only_propagate_inside_block_locals.contains(local) { - trace!( - "mutating local {:?} which is restricted to its block. \ - Will remove it from const-prop after block is finished.", - local - ); - ecx.machine.written_only_inside_own_block_locals.insert(local); - } - ecx.machine.stack[frame].locals[local].access_mut() - } - - fn before_access_global( - _tcx: TyCtxt<'tcx>, - _machine: &Self, - _alloc_id: AllocId, - alloc: ConstAllocation<'tcx, Self::PointerTag, Self::AllocExtra>, - _static_def_id: Option<DefId>, - is_write: bool, - ) -> InterpResult<'tcx> { - if is_write { - throw_machine_stop_str!("can't write to global"); - } - // If the static allocation is mutable, then we can't const prop it as its content - // might be different at runtime. - if alloc.inner().mutability == Mutability::Mut { - throw_machine_stop_str!("can't access mutable globals in ConstProp"); - } - - Ok(()) - } - - #[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>, - ) -> InterpResult<'tcx, Frame<'mir, 'tcx>> { - Ok(frame) - } - - #[inline(always)] - fn stack<'a>( - ecx: &'a InterpCx<'mir, 'tcx, Self>, - ) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>] { - &ecx.machine.stack - } - - #[inline(always)] - fn stack_mut<'a>( - ecx: &'a mut InterpCx<'mir, 'tcx, Self>, - ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>> { - &mut ecx.machine.stack - } -} - /// Finds optimization opportunities on the MIR. struct ConstPropagator<'mir, 'tcx> { ecx: InterpCx<'mir, 'tcx, ConstPropMachine<'mir, 'tcx>>, @@ -711,139 +523,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } } -/// The mode that `ConstProp` is allowed to run in for a given `Local`. -#[derive(Clone, Copy, Debug, PartialEq)] -enum ConstPropMode { - /// The `Local` can be propagated into and reads of this `Local` can also be propagated. - FullConstProp, - /// The `Local` can only be propagated into and from its own block. - OnlyInsideOwnBlock, - /// The `Local` can be propagated into but reads cannot be propagated. - OnlyPropagateInto, - /// The `Local` cannot be part of propagation at all. Any statement - /// referencing it either for reading or writing will not get propagated. - NoPropagation, -} - -struct CanConstProp { - can_const_prop: IndexVec<Local, ConstPropMode>, - // False at the beginning. Once set, no more assignments are allowed to that local. - found_assignment: BitSet<Local>, - // Cache of locals' information - local_kinds: IndexVec<Local, LocalKind>, -} - -impl CanConstProp { - /// Returns true if `local` can be propagated - fn check<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ParamEnv<'tcx>, - body: &Body<'tcx>, - ) -> IndexVec<Local, ConstPropMode> { - let mut cpv = CanConstProp { - can_const_prop: IndexVec::from_elem(ConstPropMode::FullConstProp, &body.local_decls), - found_assignment: BitSet::new_empty(body.local_decls.len()), - local_kinds: IndexVec::from_fn_n( - |local| body.local_kind(local), - body.local_decls.len(), - ), - }; - for (local, val) in cpv.can_const_prop.iter_enumerated_mut() { - let ty = body.local_decls[local].ty; - match tcx.layout_of(param_env.and(ty)) { - Ok(layout) if layout.size < Size::from_bytes(MAX_ALLOC_LIMIT) => {} - // Either the layout fails to compute, then we can't use this local anyway - // or the local is too large, then we don't want to. - _ => { - *val = ConstPropMode::NoPropagation; - continue; - } - } - // Cannot use args at all - // Cannot use locals because if x < y { y - x } else { x - y } would - // lint for x != y - // FIXME(oli-obk): lint variables until they are used in a condition - // FIXME(oli-obk): lint if return value is constant - if cpv.local_kinds[local] == LocalKind::Arg { - *val = ConstPropMode::OnlyPropagateInto; - trace!( - "local {:?} can't be const propagated because it's a function argument", - local - ); - } else if cpv.local_kinds[local] == LocalKind::Var { - *val = ConstPropMode::OnlyInsideOwnBlock; - trace!( - "local {:?} will only be propagated inside its block, because it's a user variable", - local - ); - } - } - cpv.visit_body(&body); - cpv.can_const_prop - } -} - -impl Visitor<'_> for CanConstProp { - fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) { - use rustc_middle::mir::visit::PlaceContext::*; - match context { - // Projections are fine, because `&mut foo.x` will be caught by - // `MutatingUseContext::Borrow` elsewhere. - MutatingUse(MutatingUseContext::Projection) - // These are just stores, where the storing is not propagatable, but there may be later - // mutations of the same local via `Store` - | MutatingUse(MutatingUseContext::Call) - | MutatingUse(MutatingUseContext::AsmOutput) - | MutatingUse(MutatingUseContext::Deinit) - // Actual store that can possibly even propagate a value - | MutatingUse(MutatingUseContext::SetDiscriminant) - | MutatingUse(MutatingUseContext::Store) => { - if !self.found_assignment.insert(local) { - match &mut self.can_const_prop[local] { - // If the local can only get propagated in its own block, then we don't have - // to worry about multiple assignments, as we'll nuke the const state at the - // end of the block anyway, and inside the block we overwrite previous - // states as applicable. - ConstPropMode::OnlyInsideOwnBlock => {} - ConstPropMode::NoPropagation => {} - ConstPropMode::OnlyPropagateInto => {} - other @ ConstPropMode::FullConstProp => { - trace!( - "local {:?} can't be propagated because of multiple assignments. Previous state: {:?}", - local, other, - ); - *other = ConstPropMode::OnlyInsideOwnBlock; - } - } - } - } - // Reading constants is allowed an arbitrary number of times - NonMutatingUse(NonMutatingUseContext::Copy) - | NonMutatingUse(NonMutatingUseContext::Move) - | NonMutatingUse(NonMutatingUseContext::Inspect) - | NonMutatingUse(NonMutatingUseContext::Projection) - | NonUse(_) => {} - - // These could be propagated with a smarter analysis or just some careful thinking about - // whether they'd be fine right now. - MutatingUse(MutatingUseContext::Yield) - | MutatingUse(MutatingUseContext::Drop) - | MutatingUse(MutatingUseContext::Retag) - // These can't ever be propagated under any scheme, as we can't reason about indirect - // mutation. - | NonMutatingUse(NonMutatingUseContext::SharedBorrow) - | NonMutatingUse(NonMutatingUseContext::ShallowBorrow) - | NonMutatingUse(NonMutatingUseContext::UniqueBorrow) - | NonMutatingUse(NonMutatingUseContext::AddressOf) - | MutatingUse(MutatingUseContext::Borrow) - | MutatingUse(MutatingUseContext::AddressOf) => { - trace!("local {:?} can't be propagaged because it's used: {:?}", local, context); - self.can_const_prop[local] = ConstPropMode::NoPropagation; - } - } - } -} - impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> { fn visit_body(&mut self, body: &Body<'tcx>) { for (bb, data) in body.basic_blocks().iter_enumerated() { diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs index 71ab6dee1b6..9c1fcbaa69d 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drops.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs @@ -28,20 +28,19 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { debug!("elaborate_drops({:?} @ {:?})", body.source, body.span); - let mut un_derefer = UnDerefer { tcx: tcx, derefer_sidetable: Default::default() }; - un_derefer.ref_finder(body); let def_id = body.source.def_id(); let param_env = tcx.param_env_reveal_all_normalized(def_id); - let move_data = match MoveData::gather_moves(body, tcx, param_env) { + let (side_table, move_data) = match MoveData::gather_moves(body, tcx, param_env) { Ok(move_data) => move_data, Err((move_data, _)) => { tcx.sess.delay_span_bug( body.span, "No `move_errors` should be allowed in MIR borrowck", ); - move_data + (Default::default(), move_data) } }; + let un_derefer = UnDerefer { tcx: tcx, derefer_sidetable: side_table }; let elaborate_patch = { let body = &*body; let env = MoveDataParamEnv { move_data, param_env }; diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index dc5d5cee879..1e46b0a0e81 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -246,7 +246,7 @@ impl<'tcx> Inliner<'tcx> { // not get any optimizations run on it. Any subsequent inlining may cause cycles, but we // do not need to catch this here, we can wait until the inliner decides to continue // inlining a second time. - InstanceDef::VtableShim(_) + InstanceDef::VTableShim(_) | InstanceDef::ReifyShim(_) | InstanceDef::FnPtrShim(..) | InstanceDef::ClosureOnceShim { .. } diff --git a/compiler/rustc_mir_transform/src/inline/cycle.rs b/compiler/rustc_mir_transform/src/inline/cycle.rs index a3a35f95071..7810218fd67 100644 --- a/compiler/rustc_mir_transform/src/inline/cycle.rs +++ b/compiler/rustc_mir_transform/src/inline/cycle.rs @@ -79,7 +79,7 @@ pub(crate) fn mir_callgraph_reachable<'tcx>( // These have MIR and if that MIR is inlined, substituted and then inlining is run // again, a function item can end up getting inlined. Thus we'll be able to cause // a cycle that way - InstanceDef::VtableShim(_) + InstanceDef::VTableShim(_) | InstanceDef::ReifyShim(_) | InstanceDef::FnPtrShim(..) | InstanceDef::ClosureOnceShim { .. } diff --git a/compiler/rustc_mir_transform/src/lower_slice_len.rs b/compiler/rustc_mir_transform/src/lower_slice_len.rs index 47848cfa497..2f02d00ec9f 100644 --- a/compiler/rustc_mir_transform/src/lower_slice_len.rs +++ b/compiler/rustc_mir_transform/src/lower_slice_len.rs @@ -11,7 +11,7 @@ pub struct LowerSliceLenCalls; impl<'tcx> MirPass<'tcx> for LowerSliceLenCalls { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - sess.opts.mir_opt_level() > 0 + sess.mir_opt_level() > 0 } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs index c48aa9a90ef..96b715402e7 100644 --- a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs +++ b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs @@ -21,7 +21,7 @@ pub struct RemoveUninitDrops; impl<'tcx> MirPass<'tcx> for RemoveUninitDrops { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let param_env = tcx.param_env(body.source.def_id()); - let Ok(move_data) = MoveData::gather_moves(body, tcx, param_env) else { + let Ok((_,move_data)) = MoveData::gather_moves(body, tcx, param_env) else { // We could continue if there are move errors, but there's not much point since our // init data isn't complete. return; diff --git a/compiler/rustc_mir_transform/src/reveal_all.rs b/compiler/rustc_mir_transform/src/reveal_all.rs index 8ea550fa123..4919ad40098 100644 --- a/compiler/rustc_mir_transform/src/reveal_all.rs +++ b/compiler/rustc_mir_transform/src/reveal_all.rs @@ -9,7 +9,7 @@ pub struct RevealAll; impl<'tcx> MirPass<'tcx> for RevealAll { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - sess.opts.mir_opt_level() >= 3 || super::inline::Inline.is_enabled(sess) + sess.mir_opt_level() >= 3 || super::inline::Inline.is_enabled(sess) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index eaa61d8614d..3620e94bec7 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -32,7 +32,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' let mut result = match instance { ty::InstanceDef::Item(..) => bug!("item {:?} passed to make_shim", instance), - ty::InstanceDef::VtableShim(def_id) => { + ty::InstanceDef::VTableShim(def_id) => { build_call_shim(tcx, instance, Some(Adjustment::Deref), CallKind::Direct(def_id)) } ty::InstanceDef::FnPtrShim(def_id, ty) => { @@ -113,7 +113,7 @@ enum Adjustment { /// We get passed `&[mut] self` and call the target with `*self`. /// /// This either copies `self` (if `Self: Copy`, eg. for function items), or moves out of it - /// (for `VtableShim`, which effectively is passed `&own Self`). + /// (for `VTableShim`, which effectively is passed `&own Self`). Deref, /// We get passed `self: Self` and call the target with `&mut self`. @@ -569,7 +569,7 @@ fn build_call_shim<'tcx>( // FIXME(eddyb) avoid having this snippet both here and in // `Instance::fn_sig` (introduce `InstanceDef::fn_sig`?). - if let ty::InstanceDef::VtableShim(..) = instance { + if let ty::InstanceDef::VTableShim(..) = instance { // Modify fn(self, ...) to fn(self: *mut Self, ...) let mut inputs_and_output = sig.inputs_and_output.to_vec(); let self_arg = &mut inputs_and_output[0]; diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 980af984362..180f4c7dcd6 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -28,6 +28,7 @@ //! return. use crate::MirPass; +use rustc_data_structures::fx::FxHashSet; use rustc_index::vec::{Idx, IndexVec}; use rustc_middle::mir::coverage::*; use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; @@ -267,7 +268,8 @@ pub fn remove_dead_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { return; } - let basic_blocks = body.basic_blocks_mut(); + let basic_blocks = body.basic_blocks.as_mut(); + let source_scopes = &body.source_scopes; let mut replacements: Vec<_> = (0..num_blocks).map(BasicBlock::new).collect(); let mut used_blocks = 0; for alive_index in reachable.iter() { @@ -282,7 +284,7 @@ pub fn remove_dead_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { } if tcx.sess.instrument_coverage() { - save_unreachable_coverage(basic_blocks, used_blocks); + save_unreachable_coverage(basic_blocks, source_scopes, used_blocks); } basic_blocks.raw.truncate(used_blocks); @@ -311,56 +313,72 @@ pub fn remove_dead_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { /// `Unreachable` coverage statements. These are non-executable statements whose /// code regions are still recorded in the coverage map, representing regions /// with `0` executions. +/// +/// If there are no live `Counter` `Coverage` statements remaining, we remove +/// `Coverage` statements along with the dead blocks. Since at least one +/// counter per function is required by LLVM (and necessary, to add the +/// `function_hash` to the counter's call to the LLVM intrinsic +/// `instrprof.increment()`). +/// +/// The `generator::StateTransform` MIR pass and MIR inlining can create +/// atypical conditions, where all live `Counter`s are dropped from the MIR. +/// +/// With MIR inlining we can have coverage counters belonging to different +/// instances in a single body, so the strategy described above is applied to +/// coverage counters from each instance individually. fn save_unreachable_coverage( basic_blocks: &mut IndexVec<BasicBlock, BasicBlockData<'_>>, + source_scopes: &IndexVec<SourceScope, SourceScopeData<'_>>, first_dead_block: usize, ) { - let has_live_counters = basic_blocks.raw[0..first_dead_block].iter().any(|live_block| { - live_block.statements.iter().any(|statement| { - if let StatementKind::Coverage(coverage) = &statement.kind { - matches!(coverage.kind, CoverageKind::Counter { .. }) - } else { - false + // Identify instances that still have some live coverage counters left. + let mut live = FxHashSet::default(); + for basic_block in &basic_blocks.raw[0..first_dead_block] { + for statement in &basic_block.statements { + let StatementKind::Coverage(coverage) = &statement.kind else { continue }; + let CoverageKind::Counter { .. } = coverage.kind else { continue }; + let instance = statement.source_info.scope.inlined_instance(source_scopes); + live.insert(instance); + } + } + + for block in &mut basic_blocks.raw[..first_dead_block] { + for statement in &mut block.statements { + let StatementKind::Coverage(_) = &statement.kind else { continue }; + let instance = statement.source_info.scope.inlined_instance(source_scopes); + if !live.contains(&instance) { + statement.make_nop(); } - }) - }); - if !has_live_counters { - // If there are no live `Counter` `Coverage` statements anymore, don't - // move dead coverage to the `START_BLOCK`. Just allow the dead - // `Coverage` statements to be dropped with the dead blocks. - // - // The `generator::StateTransform` MIR pass can create atypical - // conditions, where all live `Counter`s are dropped from the MIR. - // - // At least one Counter per function is required by LLVM (and necessary, - // to add the `function_hash` to the counter's call to the LLVM - // intrinsic `instrprof.increment()`). + } + } + + if live.is_empty() { return; } - // Retain coverage info for dead blocks, so coverage reports will still - // report `0` executions for the uncovered code regions. - let mut dropped_coverage = Vec::new(); - for dead_block in basic_blocks.raw[first_dead_block..].iter() { - for statement in dead_block.statements.iter() { - if let StatementKind::Coverage(coverage) = &statement.kind { - if let Some(code_region) = &coverage.code_region { - dropped_coverage.push((statement.source_info, code_region.clone())); - } + // Retain coverage for instances that still have some live counters left. + let mut retained_coverage = Vec::new(); + for dead_block in &basic_blocks.raw[first_dead_block..] { + for statement in &dead_block.statements { + let StatementKind::Coverage(coverage) = &statement.kind else { continue }; + let Some(code_region) = &coverage.code_region else { continue }; + let instance = statement.source_info.scope.inlined_instance(source_scopes); + if live.contains(&instance) { + retained_coverage.push((statement.source_info, code_region.clone())); } } } let start_block = &mut basic_blocks[START_BLOCK]; - for (source_info, code_region) in dropped_coverage { - start_block.statements.push(Statement { + start_block.statements.extend(retained_coverage.into_iter().map( + |(source_info, code_region)| Statement { source_info, kind: StatementKind::Coverage(Box::new(Coverage { kind: CoverageKind::Unreachable, code_region: Some(code_region), })), - }) - } + }, + )); } pub struct SimplifyLocals; diff --git a/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs b/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs index bd196f11879..30be64f5b2f 100644 --- a/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs +++ b/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs @@ -1,7 +1,7 @@ //! A pass that eliminates branches on uninhabited enum variants. use crate::MirPass; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_middle::mir::{ BasicBlockData, Body, Local, Operand, Rvalue, StatementKind, SwitchTargets, Terminator, TerminatorKind, diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index e3cfb034e40..68b65658c72 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -25,7 +25,7 @@ //! codegen unit: //! //! - Constants -//! - Vtables +//! - VTables //! - Object Shims //! //! @@ -992,7 +992,7 @@ fn visit_instance_use<'tcx>( } } ty::InstanceDef::DropGlue(_, Some(_)) - | ty::InstanceDef::VtableShim(..) + | ty::InstanceDef::VTableShim(..) | ty::InstanceDef::ReifyShim(..) | ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::Item(..) @@ -1427,6 +1427,10 @@ fn collect_miri<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIte output.push(create_fn_mono_item(tcx, fn_instance, DUMMY_SP)); } } + GlobalAlloc::VTable(ty, trait_ref) => { + let alloc_id = tcx.vtable_allocation((ty, trait_ref)); + collect_miri(tcx, alloc_id, output) + } } } diff --git a/compiler/rustc_monomorphize/src/partitioning/default.rs b/compiler/rustc_monomorphize/src/partitioning/default.rs index d18b1c26c17..15276569c32 100644 --- a/compiler/rustc_monomorphize/src/partitioning/default.rs +++ b/compiler/rustc_monomorphize/src/partitioning/default.rs @@ -271,7 +271,7 @@ fn characteristic_def_id_of_mono_item<'tcx>( MonoItem::Fn(instance) => { let def_id = match instance.def { ty::InstanceDef::Item(def) => def.did, - ty::InstanceDef::VtableShim(..) + ty::InstanceDef::VTableShim(..) | ty::InstanceDef::ReifyShim(..) | ty::InstanceDef::FnPtrShim(..) | ty::InstanceDef::ClosureOnceShim { .. } @@ -425,7 +425,7 @@ fn mono_item_visibility<'tcx>( InstanceDef::DropGlue(def_id, Some(_)) => def_id, // These are all compiler glue and such, never exported, always hidden. - InstanceDef::VtableShim(..) + InstanceDef::VTableShim(..) | InstanceDef::ReifyShim(..) | InstanceDef::FnPtrShim(..) | InstanceDef::Virtual(..) diff --git a/compiler/rustc_monomorphize/src/partitioning/mod.rs b/compiler/rustc_monomorphize/src/partitioning/mod.rs index 36243803f99..ff2d3869328 100644 --- a/compiler/rustc_monomorphize/src/partitioning/mod.rs +++ b/compiler/rustc_monomorphize/src/partitioning/mod.rs @@ -98,6 +98,7 @@ mod merging; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync; use rustc_hir::def_id::DefIdSet; +use rustc_middle::mir; use rustc_middle::mir::mono::MonoItem; use rustc_middle::mir::mono::{CodegenUnit, Linkage}; use rustc_middle::ty::print::with_no_trimmed_paths; @@ -479,9 +480,14 @@ fn codegened_and_inlined_items<'tcx>(tcx: TyCtxt<'tcx>, (): ()) -> &'tcx DefIdSe if !visited.insert(did) { continue; } - for scope in &tcx.instance_mir(instance.def).source_scopes { - if let Some((ref inlined, _)) = scope.inlined { - result.insert(inlined.def_id()); + let body = tcx.instance_mir(instance.def); + for block in body.basic_blocks() { + for statement in &block.statements { + let mir::StatementKind::Coverage(_) = statement.kind else { continue }; + let scope = statement.source_info.scope; + if let Some(inlined) = scope.inlined_instance(&body.source_scopes) { + result.insert(inlined.def_id()); + } } } } diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs index ef84f95ec83..0816bc8deb6 100644 --- a/compiler/rustc_parse/src/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -1,11 +1,7 @@ use super::{StringReader, UnmatchedBrace}; use rustc_ast::token::{self, Delimiter, Token}; -use rustc_ast::tokenstream::{ - DelimSpan, - Spacing::{self, *}, - TokenStream, TokenTree, TreeAndSpacing, -}; +use rustc_ast::tokenstream::{DelimSpan, Spacing, TokenStream, TokenTree}; use rustc_ast_pretty::pprust::token_to_string; use rustc_data_structures::fx::FxHashMap; use rustc_errors::PResult; @@ -77,7 +73,7 @@ impl<'a> TokenTreesReader<'a> { } } - fn parse_token_tree(&mut self) -> PResult<'a, TreeAndSpacing> { + fn parse_token_tree(&mut self) -> PResult<'a, TokenTree> { let sm = self.string_reader.sess.source_map(); match self.token.kind { @@ -223,7 +219,7 @@ impl<'a> TokenTreesReader<'a> { _ => {} } - Ok(TokenTree::Delimited(delim_span, delim, tts).into()) + Ok(TokenTree::Delimited(delim_span, delim, tts)) } token::CloseDelim(delim) => { // An unexpected closing delimiter (i.e., there is no @@ -258,12 +254,12 @@ impl<'a> TokenTreesReader<'a> { Err(err) } _ => { - let tt = TokenTree::Token(self.token.take()); + let tok = self.token.take(); let mut spacing = self.bump(); if !self.token.is_op() { - spacing = Alone; + spacing = Spacing::Alone; } - Ok((tt, spacing)) + Ok(TokenTree::Token(tok, spacing)) } } } @@ -277,20 +273,20 @@ impl<'a> TokenTreesReader<'a> { #[derive(Default)] struct TokenStreamBuilder { - buf: Vec<TreeAndSpacing>, + buf: Vec<TokenTree>, } impl TokenStreamBuilder { - fn push(&mut self, (tree, joint): TreeAndSpacing) { - if let Some((TokenTree::Token(prev_token), Joint)) = self.buf.last() - && let TokenTree::Token(token) = &tree + fn push(&mut self, tree: TokenTree) { + if let Some(TokenTree::Token(prev_token, Spacing::Joint)) = self.buf.last() + && let TokenTree::Token(token, joint) = &tree && let Some(glued) = prev_token.glue(token) { self.buf.pop(); - self.buf.push((TokenTree::Token(glued), joint)); + self.buf.push(TokenTree::Token(glued, *joint)); return; } - self.buf.push((tree, joint)) + self.buf.push(tree); } fn into_token_stream(self) -> TokenStream { diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index d56e3773dc7..261adbfc346 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -117,12 +117,12 @@ pub fn new_parser_from_file<'a>(sess: &'a ParseSess, path: &Path, sp: Option<Spa source_file_to_parser(sess, file_to_source_file(sess, path, sp)) } -/// Given a `source_file` and config, returns a parser. +/// Given a session and a `source_file`, returns a parser. fn source_file_to_parser(sess: &ParseSess, source_file: Lrc<SourceFile>) -> Parser<'_> { panictry_buffer!(&sess.span_diagnostic, maybe_source_file_to_parser(sess, source_file)) } -/// Given a `source_file` and config, return a parser. Returns any buffered errors from lexing the +/// Given a session and a `source_file`, return a parser. Returns any buffered errors from lexing the /// initial token stream. fn maybe_source_file_to_parser( sess: &ParseSess, diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 63055c56c5c..09329f18c67 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -601,6 +601,17 @@ impl<'a> Parser<'a> { self.last_unexpected_token_span = Some(self.token.span); let mut err = self.struct_span_err(self.token.span, &msg_exp); + if let TokenKind::Ident(symbol, _) = &self.prev_token.kind { + if symbol.as_str() == "public" { + err.span_suggestion_short( + self.prev_token.span, + "write `pub` instead of `public` to make the item public", + "pub", + appl, + ); + } + } + // Add suggestion for a missing closing angle bracket if '>' is included in expected_tokens // there are unclosed angle brackets if self.unmatched_angle_bracket_count > 0 diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 8e1b279d9b6..c0f661f7dbb 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -827,11 +827,12 @@ impl<'a> Parser<'a> { cast_expr: P<Expr>, ) -> PResult<'a, P<Expr>> { let span = cast_expr.span; - let maybe_ascription_span = if let ExprKind::Type(ascripted_expr, _) = &cast_expr.kind { - Some(ascripted_expr.span.shrink_to_hi().with_hi(span.hi())) - } else { - None - }; + let (cast_kind, maybe_ascription_span) = + if let ExprKind::Type(ascripted_expr, _) = &cast_expr.kind { + ("type ascription", Some(ascripted_expr.span.shrink_to_hi().with_hi(span.hi()))) + } else { + ("cast", None) + }; // Save the memory location of expr before parsing any following postfix operators. // This will be compared with the memory location of the output expression. @@ -844,7 +845,7 @@ impl<'a> Parser<'a> { // If the resulting expression is not a cast, or has a different memory location, it is an illegal postfix operator. if !matches!(with_postfix.kind, ExprKind::Cast(_, _) | ExprKind::Type(_, _)) || changed { let msg = format!( - "casts cannot be followed by {}", + "{cast_kind} cannot be followed by {}", match with_postfix.kind { ExprKind::Index(_, _) => "indexing", ExprKind::Try(_) => "`?`", @@ -2356,10 +2357,7 @@ impl<'a> Parser<'a> { let is_in_a_let_chains_context_but_nested_in_other_expr = self.let_expr_allowed && !matches!( self.prev_token.kind, - TokenKind::AndAnd - | TokenKind::CloseDelim(Delimiter::Brace) - | TokenKind::Ident(kw::If, _) - | TokenKind::Ident(kw::While, _) + TokenKind::AndAnd | TokenKind::Ident(kw::If, _) | TokenKind::Ident(kw::While, _) ); if !self.let_expr_allowed || is_in_a_let_chains_context_but_nested_in_other_expr { self.struct_span_err(self.token.span, "expected expression, found `let` statement") diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 87bc0d9762e..2c1e5807aa7 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1664,8 +1664,8 @@ impl<'a> Parser<'a> { let body = self.parse_token_tree(); // `MacBody` // Convert `MacParams MacBody` into `{ MacParams => MacBody }`. let bspan = body.span(); - let arrow = TokenTree::token(token::FatArrow, pspan.between(bspan)); // `=>` - let tokens = TokenStream::new(vec![params.into(), arrow.into(), body.into()]); + let arrow = TokenTree::token_alone(token::FatArrow, pspan.between(bspan)); // `=>` + let tokens = TokenStream::new(vec![params, arrow, body]); let dspan = DelimSpan::from_pair(pspan.shrink_to_lo(), bspan.shrink_to_hi()); P(MacArgs::Delimited(dspan, MacDelimiter::Brace, tokens)) } else { @@ -1677,7 +1677,6 @@ impl<'a> Parser<'a> { } /// Is this a possibly malformed start of a `macro_rules! foo` item definition? - fn is_macro_rules_item(&mut self) -> IsMacroRulesItem { if self.check_keyword(kw::MacroRules) { let macro_rules_span = self.token.span; diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 67e6402c0ae..1ac8b224248 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -268,13 +268,13 @@ impl TokenCursor { // FIXME: we currently don't return `Delimiter` open/close delims. To fix #67062 we will // need to, whereupon the `delim != Delimiter::Invisible` conditions below can be // removed. - if let Some((tree, spacing)) = self.frame.tree_cursor.next_with_spacing_ref() { + if let Some(tree) = self.frame.tree_cursor.next_ref() { match tree { - &TokenTree::Token(ref token) => match (desugar_doc_comments, token) { + &TokenTree::Token(ref token, spacing) => match (desugar_doc_comments, token) { (true, &Token { kind: token::DocComment(_, attr_style, data), span }) => { return self.desugar(attr_style, data, span); } - _ => return (token.clone(), *spacing), + _ => return (token.clone(), spacing), }, &TokenTree::Delimited(sp, delim, ref tts) => { // Set `open_delim` to true here because we deal with it immediately. @@ -318,12 +318,14 @@ impl TokenCursor { delim_span, Delimiter::Bracket, [ - TokenTree::token(token::Ident(sym::doc, false), span), - TokenTree::token(token::Eq, span), - TokenTree::token(TokenKind::lit(token::StrRaw(num_of_hashes), data, None), span), + TokenTree::token_alone(token::Ident(sym::doc, false), span), + TokenTree::token_alone(token::Eq, span), + TokenTree::token_alone( + TokenKind::lit(token::StrRaw(num_of_hashes), data, None), + span, + ), ] - .iter() - .cloned() + .into_iter() .collect::<TokenStream>(), ); @@ -332,14 +334,16 @@ impl TokenCursor { TokenCursorFrame::new( None, if attr_style == AttrStyle::Inner { - [TokenTree::token(token::Pound, span), TokenTree::token(token::Not, span), body] - .iter() - .cloned() - .collect::<TokenStream>() + [ + TokenTree::token_alone(token::Pound, span), + TokenTree::token_alone(token::Not, span), + body, + ] + .into_iter() + .collect::<TokenStream>() } else { - [TokenTree::token(token::Pound, span), body] - .iter() - .cloned() + [TokenTree::token_alone(token::Pound, span), body] + .into_iter() .collect::<TokenStream>() }, ), @@ -1042,7 +1046,7 @@ impl<'a> Parser<'a> { if all_normal { return match frame.tree_cursor.look_ahead(dist - 1) { Some(tree) => match tree { - TokenTree::Token(token) => looker(token), + TokenTree::Token(token, _) => looker(token), TokenTree::Delimited(dspan, delim, _) => { looker(&Token::new(token::OpenDelim(*delim), dspan.open)) } @@ -1226,7 +1230,7 @@ impl<'a> Parser<'a> { token::CloseDelim(_) | token::Eof => unreachable!(), _ => { self.bump(); - TokenTree::Token(self.prev_token.clone()) + TokenTree::Token(self.prev_token.clone(), Spacing::Alone) } } } @@ -1245,7 +1249,7 @@ impl<'a> Parser<'a> { loop { match self.token.kind { token::Eof | token::CloseDelim(..) => break, - _ => result.push(self.parse_token_tree().into()), + _ => result.push(self.parse_token_tree()), } } TokenStream::new(result) diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index f6fa19030ac..5deb17b8651 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -104,8 +104,8 @@ pub struct FormatSpec<'a> { pub enum Position<'a> { /// The argument is implied to be located at an index ArgumentImplicitlyIs(usize), - /// The argument is located at a specific index given in the format - ArgumentIs(usize), + /// The argument is located at a specific index given in the format, + ArgumentIs(usize, Option<InnerSpan>), /// The argument has a name. ArgumentNamed(&'a str, InnerSpan), } @@ -113,7 +113,7 @@ pub enum Position<'a> { impl Position<'_> { pub fn index(&self) -> Option<usize> { match self { - ArgumentIs(i) | ArgumentImplicitlyIs(i) => Some(*i), + ArgumentIs(i, ..) | ArgumentImplicitlyIs(i) => Some(*i), _ => None, } } @@ -502,8 +502,15 @@ impl<'a> Parser<'a> { /// Returns `Some(parsed_position)` if the position is not implicitly /// consuming a macro argument, `None` if it's the case. fn position(&mut self) -> Option<Position<'a>> { + let start_position = self.cur.peek().map(|item| item.0); if let Some(i) = self.integer() { - Some(ArgumentIs(i)) + let inner_span = start_position.and_then(|start| { + self.cur + .peek() + .cloned() + .and_then(|item| Some(self.to_span_index(start).to(self.to_span_index(item.0)))) + }); + Some(ArgumentIs(i, inner_span)) } else { match self.cur.peek() { Some(&(start, c)) if rustc_lexer::is_id_start(c) => { @@ -572,9 +579,14 @@ impl<'a> Parser<'a> { // '0' flag and then an ill-formatted format string with just a '$' // and no count, but this is better if we instead interpret this as // no '0' flag and '0$' as the width instead. - if self.consume('$') { + if let Some(end) = self.consume_pos('$') { spec.width = CountIsParam(0); + + if let Some((pos, _)) = self.cur.peek().cloned() { + spec.width_span = Some(self.to_span_index(pos - 2).to(self.to_span_index(pos))); + } havewidth = true; + spec.width_span = Some(self.to_span_index(end - 1).to(self.to_span_index(end + 1))); } else { spec.flags |= 1 << (FlagSignAwareZeroPad as u32); } @@ -585,6 +597,7 @@ impl<'a> Parser<'a> { spec.width = w; spec.width_span = sp; } + if let Some(start) = self.consume_pos('.') { if let Some(end) = self.consume_pos('*') { // Resolve `CountIsNextParam`. diff --git a/compiler/rustc_parse_format/src/tests.rs b/compiler/rustc_parse_format/src/tests.rs index c9667922ee7..a98f816644b 100644 --- a/compiler/rustc_parse_format/src/tests.rs +++ b/compiler/rustc_parse_format/src/tests.rs @@ -62,18 +62,30 @@ fn format_nothing() { } #[test] fn format_position() { - same("{3}", &[NextArgument(Argument { position: ArgumentIs(3), format: fmtdflt() })]); + same( + "{3}", + &[NextArgument(Argument { + position: ArgumentIs(3, Some(InnerSpan { start: 2, end: 3 })), + format: fmtdflt(), + })], + ); } #[test] fn format_position_nothing_else() { - same("{3:}", &[NextArgument(Argument { position: ArgumentIs(3), format: fmtdflt() })]); + same( + "{3:}", + &[NextArgument(Argument { + position: ArgumentIs(3, Some(InnerSpan { start: 2, end: 3 })), + format: fmtdflt(), + })], + ); } #[test] fn format_type() { same( "{3:x}", &[NextArgument(Argument { - position: ArgumentIs(3), + position: ArgumentIs(3, Some(InnerSpan { start: 2, end: 3 })), format: FormatSpec { fill: None, align: AlignUnknown, @@ -93,7 +105,7 @@ fn format_align_fill() { same( "{3:>}", &[NextArgument(Argument { - position: ArgumentIs(3), + position: ArgumentIs(3, Some(InnerSpan { start: 2, end: 3 })), format: FormatSpec { fill: None, align: AlignRight, @@ -110,7 +122,7 @@ fn format_align_fill() { same( "{3:0<}", &[NextArgument(Argument { - position: ArgumentIs(3), + position: ArgumentIs(3, Some(InnerSpan { start: 2, end: 3 })), format: FormatSpec { fill: Some('0'), align: AlignLeft, @@ -127,7 +139,7 @@ fn format_align_fill() { same( "{3:*<abcd}", &[NextArgument(Argument { - position: ArgumentIs(3), + position: ArgumentIs(3, Some(InnerSpan { start: 2, end: 3 })), format: FormatSpec { fill: Some('*'), align: AlignLeft, @@ -179,6 +191,23 @@ fn format_counts() { })], ); same( + "{1:0$.10x}", + &[NextArgument(Argument { + position: ArgumentIs(1, Some(InnerSpan { start: 2, end: 3 })), + format: FormatSpec { + fill: None, + align: AlignUnknown, + flags: 0, + precision: CountIs(10), + width: CountIsParam(0), + precision_span: None, + width_span: Some(InnerSpan::new(4, 6)), + ty: "x", + ty_span: None, + }, + })], + ); + same( "{:.*x}", &[NextArgument(Argument { position: ArgumentImplicitlyIs(1), @@ -274,7 +303,7 @@ fn format_mixture() { &[ String("abcd "), NextArgument(Argument { - position: ArgumentIs(3), + position: ArgumentIs(3, Some(InnerSpan { start: 7, end: 8 })), format: FormatSpec { fill: None, align: AlignUnknown, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index d96e7d3efe8..fde12b9eee6 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -7,7 +7,7 @@ use crate::errors; use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{fluent, pluralize, struct_span_err, Applicability, MultiSpan}; +use rustc_errors::{fluent, struct_span_err, Applicability, MultiSpan}; use rustc_expand::base::resolve_path; use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; use rustc_hir as hir; @@ -121,6 +121,10 @@ impl CheckAttrVisitor<'_> { sym::rustc_lint_diagnostics => { self.check_rustc_lint_diagnostics(&attr, span, target) } + sym::rustc_lint_opt_ty => self.check_rustc_lint_opt_ty(&attr, span, target), + sym::rustc_lint_opt_deny_field_access => { + self.check_rustc_lint_opt_deny_field_access(&attr, span, target) + } sym::rustc_clean | sym::rustc_dirty | sym::rustc_if_this_changed @@ -706,14 +710,20 @@ impl CheckAttrVisitor<'_> { true } - fn check_doc_tuple_variadic(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool { + fn check_doc_fake_variadic(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool { match self.tcx.hir().find(hir_id).and_then(|node| match node { hir::Node::Item(item) => Some(&item.kind), _ => None, }) { Some(ItemKind::Impl(ref i)) => { - if !matches!(&i.self_ty.kind, hir::TyKind::Tup([_])) { - self.tcx.sess.emit_err(errors::DocTupleVariadicNotFirst { span: meta.span() }); + let is_valid = matches!(&i.self_ty.kind, hir::TyKind::Tup([_])) + || if let hir::TyKind::BareFn(bare_fn_ty) = &i.self_ty.kind { + bare_fn_ty.decl.inputs.len() == 1 + } else { + false + }; + if !is_valid { + self.tcx.sess.emit_err(errors::DocFakeVariadicNotValid { span: meta.span() }); return false; } } @@ -887,9 +897,9 @@ impl CheckAttrVisitor<'_> { is_valid = false } - sym::tuple_variadic - if !self.check_attr_not_crate_level(meta, hir_id, "tuple_variadic") - || !self.check_doc_tuple_variadic(meta, hir_id) => + sym::fake_variadic + if !self.check_attr_not_crate_level(meta, hir_id, "fake_variadic") + || !self.check_doc_fake_variadic(meta, hir_id) => { is_valid = false } @@ -939,7 +949,7 @@ impl CheckAttrVisitor<'_> { | sym::notable_trait | sym::passes | sym::plugins - | sym::tuple_variadic => {} + | sym::fake_variadic => {} sym::test => { if !self.check_test_attr(meta, hir_id) { @@ -1174,30 +1184,22 @@ impl CheckAttrVisitor<'_> { _ => { // FIXME: #[cold] was previously allowed on non-functions/statics and some crates // used this, so only emit a warning. - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - let mut diag = - lint.build("attribute should be applied to a foreign function or static"); - diag.warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", + let attr_span = matches!(target, Target::ForeignMod).then_some(attr.span); + if let Some(s) = attr.value_str() { + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::LinkName { span, attr_span, value: s.as_str() }, ); - - // See issue #47725 - if let Target::ForeignMod = target { - if let Some(value) = attr.value_str() { - diag.span_help( - attr.span, - &format!(r#"try `#[link(name = "{value}")]` instead"#), - ); - } else { - diag.span_help(attr.span, r#"try `#[link(name = "...")]` instead"#); - } - } - - diag.span_label(span, "not a foreign function or static"); - diag.emit(); - }); + } else { + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::LinkName { span, attr_span, value: "..." }, + ); + }; } } } @@ -1215,14 +1217,7 @@ impl CheckAttrVisitor<'_> { true } _ => { - self.tcx - .sess - .struct_span_err( - attr.span, - "attribute should be applied to an `extern crate` item", - ) - .span_label(span, "not an `extern crate` item") - .emit(); + self.tcx.sess.emit_err(errors::NoLink { attr_span: attr.span, span }); false } } @@ -1252,14 +1247,7 @@ impl CheckAttrVisitor<'_> { true } _ => { - self.tcx - .sess - .struct_span_err( - attr.span, - "attribute should be applied to a free function, impl method or static", - ) - .span_label(span, "not a free function, impl method or static") - .emit(); + self.tcx.sess.emit_err(errors::ExportName { attr_span: attr.span, span }); false } } @@ -1272,11 +1260,10 @@ impl CheckAttrVisitor<'_> { target: Target, ) -> bool { if target != Target::Struct { - self.tcx - .sess - .struct_span_err(attr.span, "attribute should be applied to a struct") - .span_label(span, "not a struct") - .emit(); + self.tcx.sess.emit_err(errors::RustcLayoutScalarValidRangeNotStruct { + attr_span: attr.span, + span, + }); return false; } @@ -1287,10 +1274,7 @@ impl CheckAttrVisitor<'_> { if matches!(&list[..], &[NestedMetaItem::Literal(Lit { kind: LitKind::Int(..), .. })]) { true } else { - self.tcx - .sess - .struct_span_err(attr.span, "expected exactly one integer literal argument") - .emit(); + self.tcx.sess.emit_err(errors::RustcLayoutScalarValidRangeArg { attr_span: attr.span }); false } } @@ -1305,11 +1289,10 @@ impl CheckAttrVisitor<'_> { ) -> bool { let is_function = matches!(target, Target::Fn); if !is_function { - self.tcx - .sess - .struct_span_err(attr.span, "attribute should be applied to a function") - .span_label(span, "not a function") - .emit(); + self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn { + attr_span: attr.span, + defn_span: span, + }); return false; } @@ -1329,29 +1312,20 @@ impl CheckAttrVisitor<'_> { match param.kind { hir::GenericParamKind::Const { .. } => {} _ => { - self.tcx - .sess - .struct_span_err( - attr.span, - "#[rustc_legacy_const_generics] functions must \ - only have const generics", - ) - .span_label(param.span, "non-const generic parameter") - .emit(); + self.tcx.sess.emit_err(errors::RustcLegacyConstGenericsOnly { + attr_span: attr.span, + param_span: param.span, + }); return false; } } } if list.len() != generics.params.len() { - self.tcx - .sess - .struct_span_err( - attr.span, - "#[rustc_legacy_const_generics] must have one index for each generic parameter", - ) - .span_label(generics.span, "generic parameters") - .emit(); + self.tcx.sess.emit_err(errors::RustcLegacyConstGenericsIndex { + attr_span: attr.span, + generics_span: generics.span, + }); return false; } @@ -1361,19 +1335,10 @@ impl CheckAttrVisitor<'_> { if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) { if *val >= arg_count { let span = meta.span(); - self.tcx - .sess - .struct_span_err(span, "index exceeds number of arguments") - .span_label( - span, - format!( - "there {} only {} argument{}", - pluralize!("is", arg_count), - arg_count, - pluralize!(arg_count) - ), - ) - .emit(); + self.tcx.sess.emit_err(errors::RustcLegacyConstGenericsIndexExceed { + span, + arg_count: arg_count as usize, + }); return false; } } else { @@ -1382,10 +1347,7 @@ impl CheckAttrVisitor<'_> { } if !invalid_args.is_empty() { - self.tcx - .sess - .struct_span_err(invalid_args, "arguments should be non-negative integers") - .emit(); + self.tcx.sess.emit_err(errors::RustcLegacyConstGenericsIndexNegative { invalid_args }); false } else { true @@ -1397,11 +1359,10 @@ impl CheckAttrVisitor<'_> { fn check_applied_to_fn_or_method(&self, attr: &Attribute, span: Span, target: Target) -> bool { let is_function = matches!(target, Target::Fn | Target::Method(..)); if !is_function { - self.tcx - .sess - .struct_span_err(attr.span, "attribute should be applied to a function") - .span_label(span, "not a function") - .emit(); + self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn { + attr_span: attr.span, + defn_span: span, + }); false } else { true @@ -1425,16 +1386,42 @@ impl CheckAttrVisitor<'_> { self.check_applied_to_fn_or_method(attr, span, target) } + /// Checks that the `#[rustc_lint_opt_ty]` attribute is only applied to a struct. + fn check_rustc_lint_opt_ty(&self, attr: &Attribute, span: Span, target: Target) -> bool { + match target { + Target::Struct => true, + _ => { + self.tcx.sess.emit_err(errors::RustcLintOptTy { attr_span: attr.span, span }); + false + } + } + } + + /// Checks that the `#[rustc_lint_opt_deny_field_access]` attribute is only applied to a field. + fn check_rustc_lint_opt_deny_field_access( + &self, + attr: &Attribute, + span: Span, + target: Target, + ) -> bool { + match target { + Target::Field => true, + _ => { + self.tcx + .sess + .emit_err(errors::RustcLintOptDenyFieldAccess { attr_span: attr.span, span }); + false + } + } + } + /// Checks that the dep-graph debugging attributes are only present when the query-dep-graph /// option is passed to the compiler. fn check_rustc_dirty_clean(&self, attr: &Attribute) -> bool { if self.tcx.sess.opts.unstable_opts.query_dep_graph { true } else { - self.tcx - .sess - .struct_span_err(attr.span, "attribute requires -Z query-dep-graph to be enabled") - .emit(); + self.tcx.sess.emit_err(errors::RustcDirtyClean { span: attr.span }); false } } @@ -1453,16 +1440,12 @@ impl CheckAttrVisitor<'_> { _ => { // FIXME: #[link_section] was previously allowed on non-functions/statics and some // crates used this, so only emit a warning. - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("attribute should be applied to a function or static") - .warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", - ) - .span_label(span, "not a function or static") - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::LinkSection { span }, + ); } } } @@ -1488,41 +1471,22 @@ impl CheckAttrVisitor<'_> { Target::ForeignStatic => "static", _ => unreachable!(), }; - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build(&format!( - "`#[no_mangle]` has no effect on a foreign {foreign_item_kind}" - )) - .warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", - ) - .span_label(span, format!("foreign {foreign_item_kind}")) - .note("symbol names in extern blocks are not mangled") - .span_suggestion( - attr.span, - "remove this attribute", - "", - Applicability::MachineApplicable, - ) - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::NoMangleForeign { span, attr_span: attr.span, foreign_item_kind }, + ); } _ => { // FIXME: #[no_mangle] was previously allowed on non-functions/statics and some // crates used this, so only emit a warning. - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build( - "attribute should be applied to a free function, impl method or static", - ) - .warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", - ) - .span_label(span, "not a free function, impl method or static") - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::NoMangle { span }, + ); } } } @@ -1555,13 +1519,7 @@ impl CheckAttrVisitor<'_> { for hint in &hints { if !hint.is_meta_item() { - struct_span_err!( - self.tcx.sess, - hint.span(), - E0565, - "meta item in `repr` must be an identifier" - ) - .emit(); + self.tcx.sess.emit_err(errors::ReprIdent { span: hint.span() }); continue; } @@ -1682,15 +1640,11 @@ impl CheckAttrVisitor<'_> { return false; })) { - self.tcx.struct_span_lint_hir( + self.tcx.emit_spanned_lint( CONFLICTING_REPR_HINTS, hir_id, hint_spans.collect::<Vec<Span>>(), - |lint| { - lint.build("conflicting representation hints") - .code(rustc_errors::error_code!(E0566)) - .emit(); - }, + errors::ReprConflicting, ); } } @@ -1700,9 +1654,7 @@ impl CheckAttrVisitor<'_> { let mut used_compiler_span = None; for attr in attrs.iter().filter(|attr| attr.has_name(sym::used)) { if target != Target::Static { - self.tcx - .sess - .span_err(attr.span, "attribute must be applied to a `static` variable"); + self.tcx.sess.emit_err(errors::UsedStatic { span: attr.span }); } let inner = attr.meta_item_list(); match inner.as_deref() { @@ -1728,14 +1680,9 @@ impl CheckAttrVisitor<'_> { } } if let (Some(linker_span), Some(compiler_span)) = (used_linker_span, used_compiler_span) { - let spans = vec![linker_span, compiler_span]; self.tcx .sess - .struct_span_err( - spans, - "`used(compiler)` and `used(linker)` can't be used together", - ) - .emit(); + .emit_err(errors::UsedCompilerLinker { spans: vec![linker_span, compiler_span] }); } } @@ -1777,9 +1724,7 @@ impl CheckAttrVisitor<'_> { _ => { self.tcx .sess - .struct_span_err(attr.span, "attribute should be applied to a macro") - .span_label(span, "not a macro") - .emit(); + .emit_err(errors::AllowInternalUnstable { attr_span: attr.span, span }); false } } @@ -1790,29 +1735,26 @@ impl CheckAttrVisitor<'_> { match target { Target::Mod => {} _ => { - self.tcx - .sess - .struct_span_err(attr.span, "attribute should be applied to a module") - .emit(); + self.tcx.sess.emit_err(errors::DebugVisualizerPlacement { span: attr.span }); return false; } } let Some(hints) = attr.meta_item_list() else { - self.emit_debugger_visualizer_err(attr.span); + self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: attr.span }); return false; }; let hint = match hints.len() { 1 => &hints[0], _ => { - self.emit_debugger_visualizer_err(attr.span); + self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: attr.span }); return false; } }; let Some(meta_item) = hint.meta_item() else { - self.emit_debugger_visualizer_err(attr.span); + self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: attr.span }); return false; }; @@ -1820,7 +1762,7 @@ impl CheckAttrVisitor<'_> { (sym::natvis_file, Some(value)) => value, (sym::gdb_script_file, Some(value)) => value, (_, _) => { - self.emit_debugger_visualizer_err(meta_item.span); + self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: meta_item.span }); return false; } }; @@ -1849,16 +1791,6 @@ impl CheckAttrVisitor<'_> { } } - fn emit_debugger_visualizer_err(&self, span: Span) { - self.tcx - .sess - .struct_span_err(span, "invalid argument") - .note(r#"expected: `natvis_file = "..."`"#) - .note(r#"OR"#) - .note(r#"expected: `gdb_script_file = "..."`"#) - .emit(); - } - /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros. /// (Allows proc_macro functions) fn check_rustc_allow_const_fn_unstable( @@ -1885,9 +1817,7 @@ impl CheckAttrVisitor<'_> { _ => { self.tcx .sess - .struct_span_err(attr.span, "attribute should be applied to `const fn`") - .span_label(span, "not a `const fn`") - .emit(); + .emit_err(errors::RustcAllowConstFnUnstable { attr_span: attr.span, span }); false } } @@ -1904,9 +1834,7 @@ impl CheckAttrVisitor<'_> { _ => { self.tcx .sess - .struct_span_err(attr.span, "attribute should be applied functions or statics") - .span_label(span, "not a function or static") - .emit(); + .emit_err(errors::RustcStdInternalSymbol { attr_span: attr.span, span }); false } } @@ -1917,10 +1845,7 @@ impl CheckAttrVisitor<'_> { match target { Target::Trait => true, _ => { - self.tcx - .sess - .struct_span_err(attr.span, "attribute should be applied to a trait") - .emit(); + self.tcx.sess.emit_err(errors::ConstTrait { attr_span: attr.span }); false } } @@ -1929,10 +1854,7 @@ impl CheckAttrVisitor<'_> { fn check_stability_promotable(&self, attr: &Attribute, _span: Span, target: Target) -> bool { match target { Target::Expression => { - self.tcx - .sess - .struct_span_err(attr.span, "attribute cannot be applied to an expression") - .emit(); + self.tcx.sess.emit_err(errors::StabilityPromotable { attr_span: attr.span }); false } _ => true, @@ -1942,9 +1864,12 @@ impl CheckAttrVisitor<'_> { fn check_deprecated(&self, hir_id: HirId, attr: &Attribute, _span: Span, target: Target) { match target { Target::Closure | Target::Expression | Target::Statement | Target::Arm => { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("attribute is ignored here").emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::Deprecated, + ); } _ => {} } @@ -1955,29 +1880,30 @@ impl CheckAttrVisitor<'_> { match target { Target::ExternCrate | Target::Mod => {} _ => { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build(&format!( - "`#[{name}]` only has an effect on `extern crate` and modules" - )) - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::MacroUse { name }, + ); } } } fn check_macro_export(&self, hir_id: HirId, attr: &Attribute, target: Target) { if target != Target::MacroDef { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("`#[macro_export]` only has an effect on macro definitions").emit(); - }); + self.tcx.emit_spanned_lint(UNUSED_ATTRIBUTES, hir_id, attr.span, errors::MacroExport); } } fn check_plugin_registrar(&self, hir_id: HirId, attr: &Attribute, target: Target) { if target != Target::Fn { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("`#[plugin_registrar]` only has an effect on functions").emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::PluginRegistrar, + ); } } @@ -1996,10 +1922,7 @@ impl CheckAttrVisitor<'_> { | sym::target_feature ) && attr.meta_item_list().map_or(false, |list| list.is_empty()) { - format!( - "attribute `{}` with an empty list has no effect", - attr.name_or_empty() - ) + errors::UnusedNote::EmptyList { name: attr.name_or_empty() } } else if matches!( attr.name_or_empty(), sym::allow | sym::warn | sym::deny | sym::forbid | sym::expect @@ -2009,27 +1932,19 @@ impl CheckAttrVisitor<'_> { && let MetaItemKind::NameValue(_) = &item.kind && item.path == sym::reason { - format!( - "attribute `{}` without any lints has no effect", - attr.name_or_empty() - ) + errors::UnusedNote::NoLints { name: attr.name_or_empty() } } else if attr.name_or_empty() == sym::default_method_body_is_const { - format!("`default_method_body_is_const` has been replaced with `#[const_trait]` on traits") + errors::UnusedNote::DefaultMethodBodyConst } else { return; }; - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("unused attribute") - .span_suggestion( - attr.span, - "remove this attribute", - "", - Applicability::MachineApplicable, - ) - .note(¬e) - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::Unused { attr_span: attr.span, note }, + ); } } @@ -2200,14 +2115,7 @@ fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>) for attr in attrs { if attr.has_name(sym::inline) { - struct_span_err!( - tcx.sess, - attr.span, - E0518, - "attribute should be applied to function or closure", - ) - .span_label(attr.span, "not a function or closure") - .emit(); + tcx.sess.emit_err(errors::NonExportedMacroInvalidAttrs { attr_span: attr.span }); } } } @@ -2247,23 +2155,20 @@ fn check_duplicates( } else { (attr.span, *entry.get()) }; - tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, this, |lint| { - let mut db = lint.build("unused attribute"); - db.span_note(other, "attribute also specified here").span_suggestion( + tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + this, + errors::UnusedDuplicate { this, - "remove this attribute", - "", - Applicability::MachineApplicable, - ); - if matches!(duplicates, FutureWarnFollowing | FutureWarnPreceding) { - db.warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", - ); - } - db.emit(); - }); + other, + warning: matches!( + duplicates, + FutureWarnFollowing | FutureWarnPreceding + ) + .then_some(()), + }, + ); } Entry::Vacant(entry) => { entry.insert(attr.span); @@ -2278,19 +2183,11 @@ fn check_duplicates( } else { (attr.span, *entry.get()) }; - tcx.sess - .struct_span_err( - this, - &format!("multiple `{}` attributes", attr.name_or_empty()), - ) - .span_note(other, "attribute also specified here") - .span_suggestion( - this, - "remove this attribute", - "", - Applicability::MachineApplicable, - ) - .emit(); + tcx.sess.emit_err(errors::UnusedMultiple { + this, + other, + name: attr.name_or_empty(), + }); } Entry::Vacant(entry) => { entry.insert(attr.span); diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index f8e8720ab54..5feb0e2956b 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1,5 +1,5 @@ use rustc_errors::{Applicability, MultiSpan}; -use rustc_macros::{LintDiagnostic, SessionDiagnostic}; +use rustc_macros::{LintDiagnostic, SessionDiagnostic, SessionSubdiagnostic}; use rustc_span::{Span, Symbol}; #[derive(LintDiagnostic)] @@ -212,8 +212,8 @@ pub struct DocKeywordInvalidIdent { } #[derive(SessionDiagnostic)] -#[error(passes::doc_tuple_variadic_not_first)] -pub struct DocTupleVariadicNotFirst { +#[error(passes::doc_fake_variadic_not_valid)] +pub struct DocFakeVariadicNotValid { #[primary_span] pub span: Span, } @@ -360,3 +360,286 @@ pub struct Link { #[label] pub span: Option<Span>, } + +#[derive(LintDiagnostic)] +#[lint(passes::link_name)] +#[warn_] +pub struct LinkName<'a> { + #[help] + pub attr_span: Option<Span>, + #[label] + pub span: Span, + pub value: &'a str, +} + +#[derive(SessionDiagnostic)] +#[error(passes::no_link)] +pub struct NoLink { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::export_name)] +pub struct ExportName { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::rustc_layout_scalar_valid_range_not_struct)] +pub struct RustcLayoutScalarValidRangeNotStruct { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::rustc_layout_scalar_valid_range_arg)] +pub struct RustcLayoutScalarValidRangeArg { + #[primary_span] + pub attr_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::rustc_legacy_const_generics_only)] +pub struct RustcLegacyConstGenericsOnly { + #[primary_span] + pub attr_span: Span, + #[label] + pub param_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::rustc_legacy_const_generics_index)] +pub struct RustcLegacyConstGenericsIndex { + #[primary_span] + pub attr_span: Span, + #[label] + pub generics_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::rustc_legacy_const_generics_index_exceed)] +pub struct RustcLegacyConstGenericsIndexExceed { + #[primary_span] + #[label] + pub span: Span, + pub arg_count: usize, +} + +#[derive(SessionDiagnostic)] +#[error(passes::rustc_legacy_const_generics_index_negative)] +pub struct RustcLegacyConstGenericsIndexNegative { + #[primary_span] + pub invalid_args: Vec<Span>, +} + +#[derive(SessionDiagnostic)] +#[error(passes::rustc_dirty_clean)] +pub struct RustcDirtyClean { + #[primary_span] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[lint(passes::link_section)] +#[warn_] +pub struct LinkSection { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[lint(passes::no_mangle_foreign)] +#[warn_] +#[note] +pub struct NoMangleForeign { + #[label] + pub span: Span, + #[suggestion(applicability = "machine-applicable")] + pub attr_span: Span, + pub foreign_item_kind: &'static str, +} + +#[derive(LintDiagnostic)] +#[lint(passes::no_mangle)] +#[warn_] +pub struct NoMangle { + #[label] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::repr_ident, code = "E0565")] +pub struct ReprIdent { + #[primary_span] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[lint(passes::repr_conflicting, code = "E0566")] +pub struct ReprConflicting; + +#[derive(SessionDiagnostic)] +#[error(passes::used_static)] +pub struct UsedStatic { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::used_compiler_linker)] +pub struct UsedCompilerLinker { + #[primary_span] + pub spans: Vec<Span>, +} + +#[derive(SessionDiagnostic)] +#[error(passes::allow_internal_unstable)] +pub struct AllowInternalUnstable { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::debug_visualizer_placement)] +pub struct DebugVisualizerPlacement { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::debug_visualizer_invalid)] +#[note(passes::note_1)] +#[note(passes::note_2)] +#[note(passes::note_3)] +pub struct DebugVisualizerInvalid { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::rustc_allow_const_fn_unstable)] +pub struct RustcAllowConstFnUnstable { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::rustc_std_internal_symbol)] +pub struct RustcStdInternalSymbol { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::const_trait)] +pub struct ConstTrait { + #[primary_span] + pub attr_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::stability_promotable)] +pub struct StabilityPromotable { + #[primary_span] + pub attr_span: Span, +} + +#[derive(LintDiagnostic)] +#[lint(passes::deprecated)] +pub struct Deprecated; + +#[derive(LintDiagnostic)] +#[lint(passes::macro_use)] +pub struct MacroUse { + pub name: Symbol, +} + +#[derive(LintDiagnostic)] +#[lint(passes::macro_export)] +pub struct MacroExport; + +#[derive(LintDiagnostic)] +#[lint(passes::plugin_registrar)] +pub struct PluginRegistrar; + +#[derive(SessionSubdiagnostic)] +pub enum UnusedNote { + #[note(passes::unused_empty_lints_note)] + EmptyList { name: Symbol }, + #[note(passes::unused_no_lints_note)] + NoLints { name: Symbol }, + #[note(passes::unused_default_method_body_const_note)] + DefaultMethodBodyConst, +} + +#[derive(LintDiagnostic)] +#[lint(passes::unused)] +pub struct Unused { + #[suggestion(applicability = "machine-applicable")] + pub attr_span: Span, + #[subdiagnostic] + pub note: UnusedNote, +} + +#[derive(SessionDiagnostic)] +#[error(passes::non_exported_macro_invalid_attrs, code = "E0518")] +pub struct NonExportedMacroInvalidAttrs { + #[primary_span] + #[label] + pub attr_span: Span, +} + +#[derive(LintDiagnostic)] +#[lint(passes::unused_duplicate)] +pub struct UnusedDuplicate { + #[primary_span] + #[suggestion(code = "", applicability = "machine-applicable")] + pub this: Span, + #[note] + pub other: Span, + #[warn_] + pub warning: Option<()>, +} + +#[derive(SessionDiagnostic)] +#[error(passes::unused_multiple)] +pub struct UnusedMultiple { + #[primary_span] + #[suggestion(code = "", applicability = "machine-applicable")] + pub this: Span, + #[note] + pub other: Span, + pub name: Symbol, +} + +#[derive(SessionDiagnostic)] +#[error(passes::rustc_lint_opt_ty)] +pub struct RustcLintOptTy { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::rustc_lint_opt_deny_field_access)] +pub struct RustcLintOptDenyFieldAccess { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs index 26bfa4737a7..e05994f13e4 100644 --- a/compiler/rustc_passes/src/lib_features.rs +++ b/compiler/rustc_passes/src/lib_features.rs @@ -1,8 +1,8 @@ -// Detecting lib features (i.e., features that are not lang features). -// -// These are declared using stability attributes (e.g., `#[stable (..)]` -// and `#[unstable (..)]`), but are not declared in one single location -// (unlike lang features), which means we need to collect them instead. +//! Detecting lib features (i.e., features that are not lang features). +//! +//! These are declared using stability attributes (e.g., `#[stable (..)]` and `#[unstable (..)]`), +//! but are not declared in one single location (unlike lang features), which means we need to +//! collect them instead. use rustc_ast::{Attribute, MetaItemKind}; use rustc_errors::struct_span_err; @@ -71,11 +71,11 @@ impl<'tcx> LibFeatureCollector<'tcx> { fn collect_feature(&mut self, feature: Symbol, since: Option<Symbol>, span: Span) { let already_in_stable = self.lib_features.stable.contains_key(&feature); - let already_in_unstable = self.lib_features.unstable.contains(&feature); + let already_in_unstable = self.lib_features.unstable.contains_key(&feature); match (since, already_in_stable, already_in_unstable) { (Some(since), _, false) => { - if let Some(prev_since) = self.lib_features.stable.get(&feature) { + if let Some((prev_since, _)) = self.lib_features.stable.get(&feature) { if *prev_since != since { self.span_feature_error( span, @@ -89,10 +89,10 @@ impl<'tcx> LibFeatureCollector<'tcx> { } } - self.lib_features.stable.insert(feature, since); + self.lib_features.stable.insert(feature, (since, span)); } (None, false, _) => { - self.lib_features.unstable.insert(feature); + self.lib_features.unstable.insert(feature, span); } (Some(_), _, true) | (None, true, _) => { self.span_feature_error( diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 4e091c5b70d..ca6a2ac3db3 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -2,9 +2,9 @@ //! propagating default levels lexically from parent to children ast nodes. use attr::StabilityLevel; -use rustc_attr::{self as attr, ConstStability, Stability}; -use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; -use rustc_errors::struct_span_err; +use rustc_attr::{self as attr, ConstStability, Stability, Unstable, UnstableReason}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; +use rustc_errors::{struct_span_err, Applicability}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID}; @@ -29,13 +29,13 @@ use std::num::NonZeroU32; #[derive(PartialEq)] enum AnnotationKind { - // Annotation is required if not inherited from unstable parents + /// Annotation is required if not inherited from unstable parents. Required, - // Annotation is useless, reject it + /// Annotation is useless, reject it. Prohibited, - // Deprecation annotation is useless, reject it. (Stability attribute is still required.) + /// Deprecation annotation is useless, reject it. (Stability attribute is still required.) DeprecationProhibited, - // Annotation itself is useless, but it can be propagated to children + /// Annotation itself is useless, but it can be propagated to children. Container, } @@ -83,7 +83,7 @@ impl InheritStability { } } -// A private tree-walker for producing an Index. +/// A private tree-walker for producing an `Index`. struct Annotator<'a, 'tcx> { tcx: TyCtxt<'tcx>, index: &'a mut Index, @@ -94,9 +94,9 @@ struct Annotator<'a, 'tcx> { } impl<'a, 'tcx> Annotator<'a, 'tcx> { - // Determine the stability for a node based on its attributes and inherited - // stability. The stability is recorded in the index and used as the parent. - // If the node is a function, `fn_sig` is its signature + /// Determine the stability for a node based on its attributes and inherited stability. The + /// stability is recorded in the index and used as the parent. If the node is a function, + /// `fn_sig` is its signature. fn annotate<F>( &mut self, def_id: LocalDefId, @@ -265,6 +265,10 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } } + if let Stability { level: Unstable { implied_by: Some(implied_by), .. }, feature } = stab { + self.index.implications.insert(implied_by, feature); + } + self.index.stab_map.insert(def_id, stab); stab }); @@ -610,6 +614,7 @@ fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index { stab_map: Default::default(), const_stab_map: Default::default(), depr_map: Default::default(), + implications: Default::default(), }; { @@ -629,14 +634,12 @@ fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index { // while maintaining the invariant that all sysroot crates are unstable // by default and are unable to be used. if tcx.sess.opts.unstable_opts.force_unstable_if_unmarked { - let reason = "this crate is being loaded from the sysroot, an \ - unstable location; did you mean to load this crate \ - from crates.io via `Cargo.toml` instead?"; let stability = Stability { level: attr::StabilityLevel::Unstable { - reason: Some(Symbol::intern(reason)), + reason: UnstableReason::Default, issue: NonZeroU32::new(27812), is_soft: false, + implied_by: None, }, feature: sym::rustc_private, }; @@ -667,6 +670,7 @@ pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { check_mod_unstable_api_usage, stability_index, + stability_implications: |tcx, _| tcx.stability().implications.clone(), lookup_stability: |tcx, id| tcx.stability().local_stability(id.expect_local()), lookup_const_stability: |tcx, id| tcx.stability().local_const_stability(id.expect_local()), lookup_deprecation_entry: |tcx, id| { @@ -945,32 +949,51 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { remaining_lib_features.remove(&sym::libc); remaining_lib_features.remove(&sym::test); - let check_features = |remaining_lib_features: &mut FxIndexMap<_, _>, defined_features: &[_]| { - for &(feature, since) in defined_features { - if let Some(since) = since { - if let Some(span) = remaining_lib_features.get(&feature) { - // Warn if the user has enabled an already-stable lib feature. - unnecessary_stable_feature_lint(tcx, *span, feature, since); - } - } - remaining_lib_features.remove(&feature); - if remaining_lib_features.is_empty() { - break; - } - } - }; - // We always collect the lib features declared in the current crate, even if there are // no unknown features, because the collection also does feature attribute validation. - let local_defined_features = tcx.lib_features(()).to_vec(); - if !remaining_lib_features.is_empty() { - check_features(&mut remaining_lib_features, &local_defined_features); + let local_defined_features = tcx.lib_features(()); + let mut all_lib_features: FxHashMap<_, _> = + local_defined_features.to_vec().iter().map(|el| *el).collect(); + let mut implications = tcx.stability_implications(rustc_hir::def_id::LOCAL_CRATE).clone(); + for &cnum in tcx.crates(()) { + implications.extend(tcx.stability_implications(cnum)); + all_lib_features.extend(tcx.defined_lib_features(cnum).iter().map(|el| *el)); + } - for &cnum in tcx.crates(()) { + // Check that every feature referenced by an `implied_by` exists (for features defined in the + // local crate). + for (implied_by, feature) in tcx.stability_implications(rustc_hir::def_id::LOCAL_CRATE) { + // Only `implied_by` needs to be checked, `feature` is guaranteed to exist. + if !all_lib_features.contains_key(implied_by) { + let span = local_defined_features + .stable + .get(feature) + .map(|(_, span)| span) + .or_else(|| local_defined_features.unstable.get(feature)) + .expect("feature that implied another does not exist"); + tcx.sess + .struct_span_err( + *span, + format!("feature `{implied_by}` implying `{feature}` does not exist"), + ) + .emit(); + } + } + + if !remaining_lib_features.is_empty() { + for (feature, since) in all_lib_features.iter() { + if let Some(since) = since && let Some(span) = remaining_lib_features.get(&feature) { + // Warn if the user has enabled an already-stable lib feature. + if let Some(implies) = implications.get(&feature) { + unnecessary_partially_stable_feature_lint(tcx, *span, *feature, *implies, *since); + } else { + unnecessary_stable_feature_lint(tcx, *span, *feature, *since); + } + } + remaining_lib_features.remove(&feature); if remaining_lib_features.is_empty() { break; } - check_features(&mut remaining_lib_features, tcx.defined_lib_features(cnum)); } } @@ -982,12 +1005,41 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { // don't lint about unused features. We should re-enable this one day! } +fn unnecessary_partially_stable_feature_lint( + tcx: TyCtxt<'_>, + span: Span, + feature: Symbol, + implies: Symbol, + since: Symbol, +) { + tcx.struct_span_lint_hir(lint::builtin::STABLE_FEATURES, hir::CRATE_HIR_ID, span, |lint| { + lint.build(&format!( + "the feature `{feature}` has been partially stabilized since {since} and is succeeded \ + by the feature `{implies}`" + )) + .span_suggestion( + span, + &format!( + "if you are using features which are still unstable, change to using `{implies}`" + ), + implies, + Applicability::MaybeIncorrect, + ) + .span_suggestion( + tcx.sess.source_map().span_extend_to_line(span), + "if you are using features which are now stable, remove this line", + "", + Applicability::MaybeIncorrect, + ) + .emit(); + }); +} + fn unnecessary_stable_feature_lint(tcx: TyCtxt<'_>, span: Span, feature: Symbol, since: Symbol) { tcx.struct_span_lint_hir(lint::builtin::STABLE_FEATURES, hir::CRATE_HIR_ID, span, |lint| { lint.build(&format!( - "the feature `{}` has been stable since {} and no longer requires \ - an attribute to enable", - feature, since + "the feature `{feature}` has been stable since {since} and no longer requires an \ + attribute to enable", )) .emit(); }); diff --git a/compiler/rustc_passes/src/upvars.rs b/compiler/rustc_passes/src/upvars.rs index 97a461272b4..68d9bf22bf9 100644 --- a/compiler/rustc_passes/src/upvars.rs +++ b/compiler/rustc_passes/src/upvars.rs @@ -15,8 +15,8 @@ pub fn provide(providers: &mut Providers) { return None; } - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); - let body = tcx.hir().body(tcx.hir().maybe_body_owned_by(hir_id)?); + let local_def_id = def_id.expect_local(); + let body = tcx.hir().body(tcx.hir().maybe_body_owned_by(local_def_id)?); let mut local_collector = LocalCollector::default(); local_collector.visit_body(body); diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs index 3291be05807..c48b4ecf87a 100644 --- a/compiler/rustc_passes/src/weak_lang_items.rs +++ b/compiler/rustc_passes/src/weak_lang_items.rs @@ -68,7 +68,7 @@ fn verify<'tcx>(tcx: TyCtxt<'tcx>, items: &lang_items::LanguageItems) { } } - for (name, item) in WEAK_ITEMS_REFS.clone().into_sorted_vector().into_iter() { + for (name, &item) in WEAK_ITEMS_REFS.iter() { if missing.contains(&item) && required(tcx, item) && items.require(item).is_err() { if item == LangItem::PanicImpl { tcx.sess.err("`#[panic_handler]` function required, but not found"); diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 9a835808d49..390d6f5a856 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -1754,7 +1754,6 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> { || self.in_assoc_ty || self.tcx.resolutions(()).has_pub_restricted { - let descr = descr.to_string(); let vis_span = self.tcx.sess.source_map().guess_head_span(self.tcx.def_span(def_id)); if kind == "trait" { diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index 7c1fdc4e306..eda61df7700 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -37,7 +37,7 @@ mod values; use self::values::Value; pub use rustc_query_system::query::QueryConfig; -pub(crate) use rustc_query_system::query::{QueryDescription, QueryVtable}; +pub(crate) use rustc_query_system::query::{QueryDescription, QueryVTable}; mod on_disk_cache; pub use on_disk_cache::OnDiskCache; diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 333dc5aa668..eda4401c81d 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -340,11 +340,11 @@ macro_rules! define_queries { #[inline] fn make_vtable(tcx: QueryCtxt<'tcx>, key: &Self::Key) -> - QueryVtable<QueryCtxt<$tcx>, Self::Key, Self::Value> + QueryVTable<QueryCtxt<$tcx>, Self::Key, Self::Value> { let compute = get_provider!([$($modifiers)*][tcx, $name, key]); let cache_on_disk = Self::cache_on_disk(tcx.tcx, key); - QueryVtable { + QueryVTable { anon: is_anon!([$($modifiers)*]), eval_always: is_eval_always!([$($modifiers)*]), dep_kind: dep_graph::DepKind::$name, diff --git a/compiler/rustc_query_system/src/query/config.rs b/compiler/rustc_query_system/src/query/config.rs index 7ca668f8a1f..964914a1326 100644 --- a/compiler/rustc_query_system/src/query/config.rs +++ b/compiler/rustc_query_system/src/query/config.rs @@ -19,7 +19,7 @@ pub trait QueryConfig { type Stored: Clone; } -pub struct QueryVtable<CTX: QueryContext, K, V> { +pub struct QueryVTable<CTX: QueryContext, K, V> { pub anon: bool, pub dep_kind: CTX::DepKind, pub eval_always: bool, @@ -31,7 +31,7 @@ pub struct QueryVtable<CTX: QueryContext, K, V> { pub try_load_from_disk: Option<fn(CTX, SerializedDepNodeIndex) -> Option<V>>, } -impl<CTX: QueryContext, K, V> QueryVtable<CTX, K, V> { +impl<CTX: QueryContext, K, V> QueryVTable<CTX, K, V> { pub(crate) fn to_dep_node(&self, tcx: CTX::DepContext, key: &K) -> DepNode<CTX::DepKind> where K: crate::dep_graph::DepNodeParams<CTX::DepContext>, @@ -69,7 +69,7 @@ pub trait QueryDescription<CTX: QueryContext>: QueryConfig { CTX: 'a; // Don't use this method to compute query results, instead use the methods on TyCtxt - fn make_vtable(tcx: CTX, key: &Self::Key) -> QueryVtable<CTX, Self::Key, Self::Value>; + fn make_vtable(tcx: CTX, key: &Self::Key) -> QueryVTable<CTX, Self::Key, Self::Value>; fn cache_on_disk(tcx: CTX::DepContext, key: &Self::Key) -> bool; } diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index f698a853d1e..fb2258434f4 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -12,7 +12,7 @@ pub use self::caches::{ }; mod config; -pub use self::config::{QueryConfig, QueryDescription, QueryVtable}; +pub use self::config::{QueryConfig, QueryDescription, QueryVTable}; use crate::dep_graph::{DepNodeIndex, HasDepContext, SerializedDepNodeIndex}; diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 792f2b03173..5e8ea07d00f 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -4,7 +4,7 @@ use crate::dep_graph::{DepContext, DepNode, DepNodeIndex, DepNodeParams}; use crate::query::caches::QueryCache; -use crate::query::config::{QueryDescription, QueryVtable}; +use crate::query::config::{QueryDescription, QueryVTable}; use crate::query::job::{report_cycle, QueryInfo, QueryJob, QueryJobId, QueryJobInfo}; use crate::query::{QueryContext, QueryMap, QuerySideEffects, QueryStackFrame}; use rustc_data_structures::fingerprint::Fingerprint; @@ -331,7 +331,7 @@ fn try_execute_query<CTX, C>( span: Span, key: C::Key, dep_node: Option<DepNode<CTX::DepKind>>, - query: &QueryVtable<CTX, C::Key, C::Value>, + query: &QueryVTable<CTX, C::Key, C::Value>, ) -> (C::Stored, Option<DepNodeIndex>) where C: QueryCache, @@ -368,7 +368,7 @@ fn execute_job<CTX, K, V>( tcx: CTX, key: K, mut dep_node_opt: Option<DepNode<CTX::DepKind>>, - query: &QueryVtable<CTX, K, V>, + query: &QueryVTable<CTX, K, V>, job_id: QueryJobId, ) -> (V, DepNodeIndex) where @@ -437,7 +437,7 @@ fn try_load_from_disk_and_cache_in_memory<CTX, K, V>( tcx: CTX, key: &K, dep_node: &DepNode<CTX::DepKind>, - query: &QueryVtable<CTX, K, V>, + query: &QueryVTable<CTX, K, V>, ) -> Option<(V, DepNodeIndex)> where K: Clone, @@ -530,7 +530,7 @@ fn incremental_verify_ich<CTX, K, V: Debug>( tcx: CTX::DepContext, result: &V, dep_node: &DepNode<CTX::DepKind>, - query: &QueryVtable<CTX, K, V>, + query: &QueryVTable<CTX, K, V>, ) where CTX: QueryContext, { @@ -642,7 +642,7 @@ fn incremental_verify_ich_cold(sess: &Session, dep_node: DebugArg<'_>, result: D fn ensure_must_run<CTX, K, V>( tcx: CTX, key: &K, - query: &QueryVtable<CTX, K, V>, + query: &QueryVTable<CTX, K, V>, ) -> (bool, Option<DepNode<CTX::DepKind>>) where K: crate::dep_graph::DepNodeParams<CTX::DepContext>, diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 6631470f219..e955a1798b7 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -524,7 +524,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { let crate_root = self.r.resolve_crate_root(source.ident); let crate_name = match crate_root.kind { ModuleKind::Def(.., name) => name, - ModuleKind::Block(..) => unreachable!(), + ModuleKind::Block => unreachable!(), }; // HACK(eddyb) unclear how good this is, but keeping `$crate` // in `source` breaks `src/test/ui/imports/import-crate-var.rs`, @@ -936,7 +936,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { if self.block_needs_anonymous_module(block) { let module = self.r.new_module( Some(parent), - ModuleKind::Block(block.id), + ModuleKind::Block, expansion.to_expn_id(), block.span, parent.no_implicit_prelude, diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 7a1695fc862..22a307a15ed 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -163,7 +163,7 @@ impl<'a> Resolver<'a> { let container = match parent.kind { ModuleKind::Def(kind, _, _) => kind.descr(parent.def_id()), - ModuleKind::Block(..) => "block", + ModuleKind::Block => "block", }; let old_noun = match old_binding.is_import() { @@ -565,8 +565,7 @@ impl<'a> Resolver<'a> { } else if let Some(sp) = sm.generate_fn_name_span(span) { err.span_label( sp, - "try adding a local generic parameter in this method instead" - .to_string(), + "try adding a local generic parameter in this method instead", ); } else { err.help("try using a local generic parameter instead"); @@ -2024,7 +2023,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { span: Span, mut path: Vec<Segment>, parent_scope: &ParentScope<'b>, - ) -> Option<(Vec<Segment>, Vec<String>)> { + ) -> Option<(Vec<Segment>, Option<String>)> { debug!("make_path_suggestion: span={:?} path={:?}", span, path); match (path.get(0), path.get(1)) { @@ -2059,12 +2058,12 @@ impl<'a, 'b> ImportResolver<'a, 'b> { &mut self, mut path: Vec<Segment>, parent_scope: &ParentScope<'b>, - ) -> Option<(Vec<Segment>, Vec<String>)> { + ) -> Option<(Vec<Segment>, Option<String>)> { // Replace first ident with `self` and check if that is valid. path[0].ident.name = kw::SelfLower; let result = self.r.maybe_resolve_path(&path, None, parent_scope); debug!("make_missing_self_suggestion: path={:?} result={:?}", path, result); - if let PathResult::Module(..) = result { Some((path, Vec::new())) } else { None } + if let PathResult::Module(..) = result { Some((path, None)) } else { None } } /// Suggests a missing `crate::` if that resolves to an correct module. @@ -2078,7 +2077,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { &mut self, mut path: Vec<Segment>, parent_scope: &ParentScope<'b>, - ) -> Option<(Vec<Segment>, Vec<String>)> { + ) -> Option<(Vec<Segment>, Option<String>)> { // Replace first ident with `crate` and check if that is valid. path[0].ident.name = kw::Crate; let result = self.r.maybe_resolve_path(&path, None, parent_scope); @@ -2086,12 +2085,12 @@ impl<'a, 'b> ImportResolver<'a, 'b> { if let PathResult::Module(..) = result { Some(( path, - vec![ + Some( "`use` statements changed in Rust 2018; read more at \ <https://doc.rust-lang.org/edition-guide/rust-2018/module-system/path-\ clarity.html>" .to_string(), - ], + ), )) } else { None @@ -2109,12 +2108,12 @@ impl<'a, 'b> ImportResolver<'a, 'b> { &mut self, mut path: Vec<Segment>, parent_scope: &ParentScope<'b>, - ) -> Option<(Vec<Segment>, Vec<String>)> { + ) -> Option<(Vec<Segment>, Option<String>)> { // Replace first ident with `crate` and check if that is valid. path[0].ident.name = kw::Super; let result = self.r.maybe_resolve_path(&path, None, parent_scope); debug!("make_missing_super_suggestion: path={:?} result={:?}", path, result); - if let PathResult::Module(..) = result { Some((path, Vec::new())) } else { None } + if let PathResult::Module(..) = result { Some((path, None)) } else { None } } /// Suggests a missing external crate name if that resolves to an correct module. @@ -2131,7 +2130,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { &mut self, mut path: Vec<Segment>, parent_scope: &ParentScope<'b>, - ) -> Option<(Vec<Segment>, Vec<String>)> { + ) -> Option<(Vec<Segment>, Option<String>)> { if path[1].ident.span.rust_2015() { return None; } @@ -2152,7 +2151,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { name, path, result ); if let PathResult::Module(..) = result { - return Some((path, Vec::new())); + return Some((path, None)); } } @@ -2176,7 +2175,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { import: &'b Import<'b>, module: ModuleOrUniformRoot<'b>, ident: Ident, - ) -> Option<(Option<Suggestion>, Vec<String>)> { + ) -> Option<(Option<Suggestion>, Option<String>)> { let ModuleOrUniformRoot::Module(mut crate_module) = module else { return None; }; @@ -2288,12 +2287,9 @@ impl<'a, 'b> ImportResolver<'a, 'b> { String::from("a macro with this name exists at the root of the crate"), Applicability::MaybeIncorrect, )); - let note = vec![ - "this could be because a macro annotated with `#[macro_export]` will be exported \ - at the root of the crate instead of the module where it is defined" - .to_string(), - ]; - Some((suggestion, note)) + Some((suggestion, Some("this could be because a macro annotated with `#[macro_export]` will be exported \ + at the root of the crate instead of the module where it is defined" + .to_string()))) } else { None } @@ -2603,12 +2599,12 @@ fn show_candidates( .skip(1) .all(|(_, descr, _, _)| descr == descr_first) { - descr_first.to_string() + descr_first } else { - "item".to_string() + "item" }; let plural_descr = - if descr.ends_with("s") { format!("{}es", descr) } else { format!("{}s", descr) }; + if descr.ends_with('s') { format!("{}es", descr) } else { format!("{}s", descr) }; let mut msg = format!("{}these {} exist but are inaccessible", prefix, plural_descr); let mut has_colon = false; diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 0cc6d05d1d0..6e678288142 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -218,7 +218,7 @@ impl<'a> Resolver<'a> { return Some((self.expn_def_scope(ctxt.remove_mark()), None)); } - if let ModuleKind::Block(..) = module.kind { + if let ModuleKind::Block = module.kind { return Some((module.parent.unwrap().nearest_item_scope(), None)); } @@ -333,7 +333,7 @@ impl<'a> Resolver<'a> { }; match module.kind { - ModuleKind::Block(..) => {} // We can see through blocks + ModuleKind::Block => {} // We can see through blocks _ => break, } diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index e6060ad4665..b89273990d8 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -336,7 +336,7 @@ impl<'a> Resolver<'a> { struct UnresolvedImportError { span: Span, label: Option<String>, - note: Vec<String>, + note: Option<String>, suggestion: Option<Suggestion>, } @@ -427,7 +427,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { let err = UnresolvedImportError { span: import.span, label: None, - note: Vec::new(), + note: None, suggestion: None, }; if path.contains("::") { @@ -463,10 +463,8 @@ impl<'a, 'b> ImportResolver<'a, 'b> { let mut diag = struct_span_err!(self.r.session, span, E0432, "{}", &msg); - if let Some((_, UnresolvedImportError { note, .. })) = errors.iter().last() { - for message in note { - diag.note(message); - } + if let Some((_, UnresolvedImportError { note: Some(note), .. })) = errors.iter().last() { + diag.note(note); } for (_, err) in errors.into_iter().take(MAX_LABEL_COUNT) { @@ -644,7 +642,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { None => UnresolvedImportError { span, label: Some(label), - note: Vec::new(), + note: None, suggestion, }, }; @@ -686,7 +684,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { return Some(UnresolvedImportError { span: import.span, label: Some(String::from("cannot glob-import a module into itself")), - note: Vec::new(), + note: None, suggestion: None, }); } @@ -830,7 +828,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { let (suggestion, note) = match self.check_for_module_export_macro(import, module, ident) { Some((suggestion, note)) => (suggestion.or(lev_suggestion), note), - _ => (lev_suggestion, Vec::new()), + _ => (lev_suggestion, None), }; let label = match module { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 49761023ec3..ed65100ae77 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -21,6 +21,7 @@ use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, PartialRes, PerNS}; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID}; use rustc_hir::{PrimTy, TraitCandidate}; +use rustc_middle::middle::resolve_lifetime::Set1; use rustc_middle::ty::DefIdTree; use rustc_middle::{bug, span_bug}; use rustc_session::lint; @@ -43,6 +44,10 @@ type IdentMap<T> = FxHashMap<Ident, T>; /// Map from the name in a pattern to its binding mode. type BindingMap = IdentMap<BindingInfo>; +use diagnostics::{ + ElisionFnParameter, LifetimeElisionCandidate, MissingLifetime, MissingLifetimeKind, +}; + #[derive(Copy, Clone, Debug)] struct BindingInfo { span: Span, @@ -257,8 +262,11 @@ enum LifetimeRibKind { /// error on default object bounds (e.g., `Box<dyn Foo>`). AnonymousReportError, - /// Pass responsibility to `resolve_lifetime` code for all cases. - AnonymousPassThrough(NodeId, /* in_fn_return */ bool), + /// Replace all anonymous lifetimes by provided lifetime. + Elided(LifetimeRes), + + /// Signal we cannot find which should be the anonymous lifetime. + ElisionFailure, } #[derive(Copy, Clone, Debug)] @@ -522,6 +530,10 @@ struct DiagnosticMetadata<'ast> { /// When processing impl trait currently_processing_impl_trait: Option<(TraitRef, Ty)>, + + /// Accumulate the errors due to missed lifetime elision, + /// and report them all at once for each function. + current_elision_failures: Vec<MissingLifetime>, } struct LateResolutionVisitor<'a, 'b, 'ast> { @@ -540,6 +552,13 @@ struct LateResolutionVisitor<'a, 'b, 'ast> { /// The current set of local scopes for lifetimes. lifetime_ribs: Vec<LifetimeRib>, + /// We are looking for lifetimes in an elision context. + /// The set contains all the resolutions that we encountered so far. + /// They will be used to determine the correct lifetime for the fn return type. + /// The `LifetimeElisionCandidate` is used for diagnostics, to suggest introducing named + /// lifetimes. + lifetime_elision_candidates: Option<FxIndexMap<LifetimeRes, LifetimeElisionCandidate>>, + /// The trait that the current context can refer to. current_trait_ref: Option<(Module<'a>, TraitRef)>, @@ -580,7 +599,9 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { fn visit_anon_const(&mut self, constant: &'ast AnonConst) { // We deal with repeat expressions explicitly in `resolve_expr`. self.with_lifetime_rib(LifetimeRibKind::AnonConst, |this| { - this.resolve_anon_const(constant, IsRepeatExpr::No); + this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Static), |this| { + this.resolve_anon_const(constant, IsRepeatExpr::No); + }) }) } fn visit_expr(&mut self, expr: &'ast Expr) { @@ -607,8 +628,10 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { TyKind::Rptr(None, _) => { // Elided lifetime in reference: we resolve as if there was some lifetime `'_` with // NodeId `ty.id`. + // This span will be used in case of elision failure. let span = self.r.session.source_map().next_point(ty.span.shrink_to_lo()); self.resolve_elided_lifetime(ty.id, span); + visit::walk_ty(self, ty); } TyKind::Path(ref qself, ref path) => { self.diagnostic_metadata.current_type_path = Some(ty); @@ -634,8 +657,8 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { }, |this| this.visit_path(&path, ty.id), ); - self.diagnostic_metadata.current_type_path = prev_ty; - return; + } else { + visit::walk_ty(self, ty) } } TyKind::ImplicitSelf => { @@ -649,9 +672,16 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { ) .map_or(Res::Err, |d| d.res()); self.r.record_partial_res(ty.id, PartialRes::new(res)); + visit::walk_ty(self, ty) + } + TyKind::ImplTrait(..) => { + let candidates = self.lifetime_elision_candidates.take(); + visit::walk_ty(self, ty); + self.lifetime_elision_candidates = candidates; } TyKind::TraitObject(ref bounds, ..) => { self.diagnostic_metadata.current_trait_object = Some(&bounds[..]); + visit::walk_ty(self, ty) } TyKind::BareFn(ref bare_fn) => { let span = ty.span.shrink_to_lo().to(bare_fn.decl_span.shrink_to_lo()); @@ -670,20 +700,26 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { binder: ty.id, report_in_path: 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), + |this| { + this.resolve_fn_signature( + ty.id, + false, + // We don't need to deal with patterns in parameters, because + // they are not possible for foreign or bodiless functions. + bare_fn + .decl + .inputs + .iter() + .map(|Param { ty, .. }| (None, &**ty)), + &bare_fn.decl.output, + ) + }, ); }, - ); - self.diagnostic_metadata.current_trait_object = prev; - return; + ) } - _ => (), + _ => visit::walk_ty(self, ty), } - visit::walk_ty(self, ty); self.diagnostic_metadata.current_trait_object = prev; self.diagnostic_metadata.current_type_path = prev_ty; } @@ -757,18 +793,19 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { | FnKind::Fn(_, _, sig, _, generics, None) => { self.visit_fn_header(&sig.header); self.visit_generics(generics); - // 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::AnonymousCreateParameter { binder: fn_id, report_in_path: false, }, - |this| 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), + |this| { + this.resolve_fn_signature( + fn_id, + sig.decl.has_self(), + sig.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)), + &sig.decl.output, + ) + }, ); return; } @@ -793,19 +830,22 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { let declaration = &sig.decl; let async_node_id = sig.header.asyncness.opt_return_id(); - // Argument-position elided lifetimes must be transformed into fresh - // generic parameters. This is especially useful for `async fn`, where - // these fresh generic parameters can be applied to the opaque `impl Trait` - // return type. this.with_lifetime_rib( LifetimeRibKind::AnonymousCreateParameter { binder: fn_id, - // Only emit a hard error for `async fn`, since this kind of - // elision has always been allowed in regular `fn`s. report_in_path: async_node_id.is_some(), }, - // Add each argument to the rib. - |this| this.resolve_params(&declaration.inputs), + |this| { + this.resolve_fn_signature( + fn_id, + declaration.has_self(), + declaration + .inputs + .iter() + .map(|Param { pat, ty, .. }| (Some(&**pat), &**ty)), + &declaration.output, + ) + }, ); // Construct the list of in-scope lifetime parameters for async lowering. @@ -844,23 +884,13 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { .insert(async_node_id, extra_lifetime_params); } - this.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough( - // For async fn, the return type appears inside a custom - // `impl Future` RPIT, so we override the binder's id. - async_node_id.unwrap_or(fn_id), - true, - ), - |this| visit::walk_fn_ret_ty(this, &declaration.output), - ); - if let Some(body) = body { // 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, false), + LifetimeRibKind::Elided(LifetimeRes::Infer), |this| this.visit_block(body), ); @@ -888,7 +918,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { this.with_lifetime_rib( match binder { ClosureBinder::NotPresent => { - LifetimeRibKind::AnonymousPassThrough(fn_id, true) + LifetimeRibKind::Elided(LifetimeRes::Infer) } ClosureBinder::For { .. } => LifetimeRibKind::AnonymousReportError, }, @@ -900,7 +930,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { 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, false), + LifetimeRibKind::Elided(LifetimeRes::Infer), |this| this.visit_expr(body), ); @@ -1035,11 +1065,14 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { binder, report_in_path: false, }, - |this| walk_list!(this, visit_ty, &p_args.inputs), - ); - self.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough(binder, true), - |this| visit::walk_fn_ret_ty(this, &p_args.output), + |this| { + this.resolve_fn_signature( + binder, + false, + p_args.inputs.iter().map(|ty| (None, &**ty)), + &p_args.output, + ) + }, ); break; } @@ -1049,9 +1082,10 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { visit::walk_generic_args(self, path_span, args); break; } - LifetimeRibKind::AnonymousPassThrough(..) - | LifetimeRibKind::AnonymousCreateParameter { .. } + LifetimeRibKind::AnonymousCreateParameter { .. } | LifetimeRibKind::AnonymousReportError + | LifetimeRibKind::Elided(_) + | LifetimeRibKind::ElisionFailure | LifetimeRibKind::AnonConst | LifetimeRibKind::ConstGeneric => {} } @@ -1156,6 +1190,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { }, label_ribs: Vec::new(), lifetime_ribs: Vec::new(), + lifetime_elision_candidates: None, current_trait_ref: None, diagnostic_metadata: Box::new(DiagnosticMetadata::default()), // errors at module scope should always be reported @@ -1362,7 +1397,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { work: impl FnOnce(&mut Self) -> T, ) -> T { self.lifetime_ribs.push(LifetimeRib::new(kind)); + let outer_elision_candidates = self.lifetime_elision_candidates.take(); let ret = work(self); + self.lifetime_elision_candidates = outer_elision_candidates; self.lifetime_ribs.pop(); ret } @@ -1372,7 +1409,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { let ident = lifetime.ident; if ident.name == kw::StaticLifetime { - self.record_lifetime_res(lifetime.id, LifetimeRes::Static); + self.record_lifetime_res( + lifetime.id, + LifetimeRes::Static, + LifetimeElisionCandidate::Named, + ); return; } @@ -1385,7 +1426,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { let rib = &self.lifetime_ribs[i]; let normalized_ident = ident.normalize_to_macros_2_0(); if let Some(&(_, res)) = rib.bindings.get(&normalized_ident) { - self.record_lifetime_res(lifetime.id, res); + self.record_lifetime_res(lifetime.id, res, LifetimeElisionCandidate::Named); if let LifetimeRes::Param { param, .. } = res { match self.lifetime_uses.entry(param) { @@ -1399,15 +1440,19 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // Do not suggest eliding a lifetime where an anonymous // lifetime would be illegal. LifetimeRibKind::Item - | LifetimeRibKind::AnonymousPassThrough(_, true) - | LifetimeRibKind::AnonymousReportError => { - Some(LifetimeUseSet::Many) - } + | LifetimeRibKind::AnonymousReportError + | LifetimeRibKind::ElisionFailure => Some(LifetimeUseSet::Many), // An anonymous lifetime is legal here, go ahead. - LifetimeRibKind::AnonymousPassThrough(_, false) - | LifetimeRibKind::AnonymousCreateParameter { .. } => { + LifetimeRibKind::AnonymousCreateParameter { .. } => { Some(LifetimeUseSet::One { use_span: ident.span, use_ctxt }) } + // Only report if eliding the lifetime would have the same + // semantics. + LifetimeRibKind::Elided(r) => Some(if res == r { + LifetimeUseSet::One { use_span: ident.span, use_ctxt } + } else { + LifetimeUseSet::Many + }), LifetimeRibKind::Generics { .. } | LifetimeRibKind::ConstGeneric | LifetimeRibKind::AnonConst => None, @@ -1429,12 +1474,20 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { LifetimeRibKind::Item => break, LifetimeRibKind::ConstGeneric => { self.emit_non_static_lt_in_const_generic_error(lifetime); - self.r.lifetimes_res_map.insert(lifetime.id, LifetimeRes::Error); + self.record_lifetime_res( + lifetime.id, + LifetimeRes::Error, + LifetimeElisionCandidate::Ignore, + ); return; } LifetimeRibKind::AnonConst => { self.maybe_emit_forbidden_non_static_lifetime_error(lifetime); - self.r.lifetimes_res_map.insert(lifetime.id, LifetimeRes::Error); + self.record_lifetime_res( + lifetime.id, + LifetimeRes::Error, + LifetimeElisionCandidate::Ignore, + ); return; } _ => {} @@ -1452,19 +1505,31 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } self.emit_undeclared_lifetime_error(lifetime, outer_res); - self.record_lifetime_res(lifetime.id, LifetimeRes::Error); + self.record_lifetime_res(lifetime.id, LifetimeRes::Error, LifetimeElisionCandidate::Named); } #[tracing::instrument(level = "debug", skip(self))] fn resolve_anonymous_lifetime(&mut self, lifetime: &Lifetime, elided: bool) { debug_assert_eq!(lifetime.ident.name, kw::UnderscoreLifetime); + let missing_lifetime = MissingLifetime { + id: lifetime.id, + span: lifetime.ident.span, + kind: if elided { + MissingLifetimeKind::Ampersand + } else { + MissingLifetimeKind::Underscore + }, + count: 1, + }; + let elision_candidate = LifetimeElisionCandidate::Missing(missing_lifetime); for i in (0..self.lifetime_ribs.len()).rev() { let rib = &mut self.lifetime_ribs[i]; + debug!(?rib.kind); match rib.kind { LifetimeRibKind::AnonymousCreateParameter { binder, .. } => { let res = self.create_fresh_lifetime(lifetime.id, lifetime.ident, binder); - self.record_lifetime_res(lifetime.id, res); + self.record_lifetime_res(lifetime.id, res, elision_candidate); return; } LifetimeRibKind::AnonymousReportError => { @@ -1486,14 +1551,16 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { .span_label(lifetime.ident.span, note) .emit(); - self.record_lifetime_res(lifetime.id, LifetimeRes::Error); + self.record_lifetime_res(lifetime.id, LifetimeRes::Error, elision_candidate); return; } - LifetimeRibKind::AnonymousPassThrough(node_id, _) => { - self.record_lifetime_res( - lifetime.id, - LifetimeRes::Anonymous { binder: node_id, elided }, - ); + LifetimeRibKind::Elided(res) => { + self.record_lifetime_res(lifetime.id, res, elision_candidate); + return; + } + LifetimeRibKind::ElisionFailure => { + self.diagnostic_metadata.current_elision_failures.push(missing_lifetime); + self.record_lifetime_res(lifetime.id, LifetimeRes::Error, elision_candidate); return; } LifetimeRibKind::Item => break, @@ -1502,23 +1569,20 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { | LifetimeRibKind::AnonConst => {} } } - // This resolution is wrong, it passes the work to HIR lifetime resolution. - // We cannot use `LifetimeRes::Error` because we do not emit a diagnostic. - self.record_lifetime_res( - lifetime.id, - LifetimeRes::Anonymous { binder: DUMMY_NODE_ID, elided }, - ); + self.record_lifetime_res(lifetime.id, LifetimeRes::Error, elision_candidate); + self.report_missing_lifetime_specifiers(vec![missing_lifetime], None); } #[tracing::instrument(level = "debug", skip(self))] fn resolve_elided_lifetime(&mut self, anchor_id: NodeId, span: Span) { let id = self.r.next_node_id(); + let lt = Lifetime { id, ident: Ident::new(kw::UnderscoreLifetime, span) }; + self.record_lifetime_res( anchor_id, LifetimeRes::ElidedAnchor { start: id, end: NodeId::from_u32(id.as_u32() + 1) }, + LifetimeElisionCandidate::Ignore, ); - - let lt = Lifetime { id, ident: Ident::new(kw::UnderscoreLifetime, span) }; self.resolve_anonymous_lifetime(<, true); } @@ -1587,6 +1651,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { | PathSource::Struct | PathSource::TupleStruct(..) => false, }; + if !missing && !segment.has_generic_args { + continue; + } let elided_lifetime_span = if segment.has_generic_args { // If there are brackets, but not generic arguments, then use the opening bracket @@ -1603,43 +1670,31 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.record_lifetime_res( segment_id, LifetimeRes::ElidedAnchor { start: node_ids.start, end: node_ids.end }, + LifetimeElisionCandidate::Ignore, ); if !missing { // Do not create a parameter for patterns and expressions. - for rib in self.lifetime_ribs.iter().rev() { - match rib.kind { - LifetimeRibKind::AnonymousPassThrough(binder, _) => { - let res = LifetimeRes::Anonymous { binder, elided: true }; - for id in node_ids { - self.record_lifetime_res(id, res); - } - break; - } - // `LifetimeRes::Error`, which would usually be used in the case of - // `ReportError`, is unsuitable here, as we don't emit an error yet. Instead, - // we simply resolve to an implicit lifetime, which will be checked later, at - // which point a suitable error will be emitted. - LifetimeRibKind::AnonymousReportError | LifetimeRibKind::Item => { - // FIXME(cjgillot) This resolution is wrong, but this does not matter - // since these cases are erroneous anyway. Lifetime resolution should - // emit a "missing lifetime specifier" diagnostic. - let res = - LifetimeRes::Anonymous { binder: DUMMY_NODE_ID, elided: true }; - for id in node_ids { - self.record_lifetime_res(id, res); - } - break; - } - LifetimeRibKind::AnonymousCreateParameter { .. } - | LifetimeRibKind::Generics { .. } - | LifetimeRibKind::ConstGeneric - | LifetimeRibKind::AnonConst => {} - } + for id in node_ids { + self.record_lifetime_res( + id, + LifetimeRes::Infer, + LifetimeElisionCandidate::Named, + ); } continue; } + let missing_lifetime = MissingLifetime { + id: node_ids.start, + span: elided_lifetime_span, + kind: if segment.has_generic_args { + MissingLifetimeKind::Comma + } else { + MissingLifetimeKind::Brackets + }, + count: expected_lifetimes, + }; let mut should_lint = true; for rib in self.lifetime_ribs.iter().rev() { match rib.kind { @@ -1670,23 +1725,47 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { should_lint = false; for id in node_ids { - self.record_lifetime_res(id, LifetimeRes::Error); + self.record_lifetime_res( + id, + LifetimeRes::Error, + LifetimeElisionCandidate::Named, + ); } break; } // Do not create a parameter for patterns and expressions. LifetimeRibKind::AnonymousCreateParameter { binder, .. } => { + // Group all suggestions into the first record. + let mut candidate = LifetimeElisionCandidate::Missing(missing_lifetime); for id in node_ids { let res = self.create_fresh_lifetime(id, ident, binder); - self.record_lifetime_res(id, res); + self.record_lifetime_res( + id, + res, + replace(&mut candidate, LifetimeElisionCandidate::Named), + ); } break; } - // `PassThrough` is the normal case. - LifetimeRibKind::AnonymousPassThrough(binder, _) => { - let res = LifetimeRes::Anonymous { binder, elided: true }; + LifetimeRibKind::Elided(res) => { + let mut candidate = LifetimeElisionCandidate::Missing(missing_lifetime); for id in node_ids { - self.record_lifetime_res(id, res); + self.record_lifetime_res( + id, + res, + replace(&mut candidate, LifetimeElisionCandidate::Ignore), + ); + } + break; + } + LifetimeRibKind::ElisionFailure => { + self.diagnostic_metadata.current_elision_failures.push(missing_lifetime); + for id in node_ids { + self.record_lifetime_res( + id, + LifetimeRes::Error, + LifetimeElisionCandidate::Ignore, + ); } break; } @@ -1695,13 +1774,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // we simply resolve to an implicit lifetime, which will be checked later, at // which point a suitable error will be emitted. LifetimeRibKind::AnonymousReportError | LifetimeRibKind::Item => { - // FIXME(cjgillot) This resolution is wrong, but this does not matter - // since these cases are erroneous anyway. Lifetime resolution should - // emit a "missing lifetime specifier" diagnostic. - let res = LifetimeRes::Anonymous { binder: DUMMY_NODE_ID, elided: true }; for id in node_ids { - self.record_lifetime_res(id, res); + self.record_lifetime_res( + id, + LifetimeRes::Error, + LifetimeElisionCandidate::Ignore, + ); } + self.report_missing_lifetime_specifiers(vec![missing_lifetime], None); break; } LifetimeRibKind::Generics { .. } @@ -1728,13 +1808,214 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } #[tracing::instrument(level = "debug", skip(self))] - fn record_lifetime_res(&mut self, id: NodeId, res: LifetimeRes) { + fn record_lifetime_res( + &mut self, + id: NodeId, + res: LifetimeRes, + candidate: LifetimeElisionCandidate, + ) { if let Some(prev_res) = self.r.lifetimes_res_map.insert(id, res) { panic!( "lifetime {:?} resolved multiple times ({:?} before, {:?} now)", id, prev_res, res ) } + match res { + LifetimeRes::Param { .. } | LifetimeRes::Fresh { .. } | LifetimeRes::Static => { + if let Some(ref mut candidates) = self.lifetime_elision_candidates { + candidates.insert(res, candidate); + } + } + LifetimeRes::Infer | LifetimeRes::Error | LifetimeRes::ElidedAnchor { .. } => {} + } + } + + #[tracing::instrument(level = "debug", skip(self))] + fn record_lifetime_param(&mut self, id: NodeId, res: LifetimeRes) { + if let Some(prev_res) = self.r.lifetimes_res_map.insert(id, res) { + panic!( + "lifetime parameter {:?} resolved multiple times ({:?} before, {:?} now)", + id, prev_res, res + ) + } + } + + /// Perform resolution of a function signature, accounting for lifetime elision. + #[tracing::instrument(level = "debug", skip(self, inputs))] + fn resolve_fn_signature( + &mut self, + fn_id: NodeId, + has_self: bool, + inputs: impl Iterator<Item = (Option<&'ast Pat>, &'ast Ty)> + Clone, + output_ty: &'ast FnRetTy, + ) { + // Add each argument to the rib. + let elision_lifetime = self.resolve_fn_params(has_self, inputs); + debug!(?elision_lifetime); + + let outer_failures = take(&mut self.diagnostic_metadata.current_elision_failures); + let output_rib = if let Ok(res) = elision_lifetime.as_ref() { + LifetimeRibKind::Elided(*res) + } else { + LifetimeRibKind::ElisionFailure + }; + self.with_lifetime_rib(output_rib, |this| visit::walk_fn_ret_ty(this, &output_ty)); + let elision_failures = + replace(&mut self.diagnostic_metadata.current_elision_failures, outer_failures); + if !elision_failures.is_empty() { + let Err(failure_info) = elision_lifetime else { bug!() }; + self.report_missing_lifetime_specifiers(elision_failures, Some(failure_info)); + } + } + + /// Resolve inside function parameters and parameter types. + /// Returns the lifetime for elision in fn return type, + /// or diagnostic information in case of elision failure. + fn resolve_fn_params( + &mut self, + has_self: bool, + inputs: impl Iterator<Item = (Option<&'ast Pat>, &'ast Ty)>, + ) -> Result<LifetimeRes, (Vec<MissingLifetime>, Vec<ElisionFnParameter>)> { + let outer_candidates = + replace(&mut self.lifetime_elision_candidates, Some(Default::default())); + + let mut elision_lifetime = None; + let mut lifetime_count = 0; + let mut parameter_info = Vec::new(); + + let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; + for (index, (pat, ty)) in inputs.enumerate() { + debug!(?pat, ?ty); + if let Some(pat) = pat { + self.resolve_pattern(pat, PatternSource::FnParam, &mut bindings); + } + self.visit_ty(ty); + + if let Some(ref candidates) = self.lifetime_elision_candidates { + let new_count = candidates.len(); + let local_count = new_count - lifetime_count; + if local_count != 0 { + parameter_info.push(ElisionFnParameter { + index, + ident: if let Some(pat) = pat && let PatKind::Ident(_, ident, _) = pat.kind { + Some(ident) + } else { + None + }, + lifetime_count: local_count, + span: ty.span, + }); + } + lifetime_count = new_count; + } + + // Handle `self` specially. + if index == 0 && has_self { + let self_lifetime = self.find_lifetime_for_self(ty); + if let Set1::One(lifetime) = self_lifetime { + elision_lifetime = Some(lifetime); + self.lifetime_elision_candidates = None; + } else { + self.lifetime_elision_candidates = Some(Default::default()); + lifetime_count = 0; + } + } + debug!("(resolving function / closure) recorded parameter"); + } + + let all_candidates = replace(&mut self.lifetime_elision_candidates, outer_candidates); + debug!(?all_candidates); + + if let Some(res) = elision_lifetime { + return Ok(res); + } + + // We do not have a `self` candidate, look at the full list. + let all_candidates = all_candidates.unwrap(); + if all_candidates.len() == 1 { + Ok(*all_candidates.first().unwrap().0) + } else { + let all_candidates = all_candidates + .into_iter() + .filter_map(|(_, candidate)| match candidate { + LifetimeElisionCandidate::Ignore | LifetimeElisionCandidate::Named => None, + LifetimeElisionCandidate::Missing(missing) => Some(missing), + }) + .collect(); + Err((all_candidates, parameter_info)) + } + } + + /// List all the lifetimes that appear in the provided type. + fn find_lifetime_for_self(&self, ty: &'ast Ty) -> Set1<LifetimeRes> { + struct SelfVisitor<'r, 'a> { + r: &'r Resolver<'a>, + impl_self: Option<Res>, + lifetime: Set1<LifetimeRes>, + } + + impl SelfVisitor<'_, '_> { + // Look for `self: &'a Self` - also desugared from `&'a self`, + // and if that matches, use it for elision and return early. + fn is_self_ty(&self, ty: &Ty) -> bool { + match ty.kind { + TyKind::ImplicitSelf => true, + TyKind::Path(None, _) => { + let path_res = self.r.partial_res_map[&ty.id].base_res(); + if let Res::SelfTy { .. } = path_res { + return true; + } + Some(path_res) == self.impl_self + } + _ => false, + } + } + } + + impl<'a> Visitor<'a> for SelfVisitor<'_, '_> { + fn visit_ty(&mut self, ty: &'a Ty) { + trace!("SelfVisitor considering ty={:?}", ty); + if let TyKind::Rptr(lt, ref mt) = ty.kind && self.is_self_ty(&mt.ty) { + let lt_id = if let Some(lt) = lt { + lt.id + } else { + let res = self.r.lifetimes_res_map[&ty.id]; + let LifetimeRes::ElidedAnchor { start, .. } = res else { bug!() }; + start + }; + let lt_res = self.r.lifetimes_res_map[<_id]; + trace!("SelfVisitor inserting res={:?}", lt_res); + self.lifetime.insert(lt_res); + } + visit::walk_ty(self, ty) + } + } + + let impl_self = self + .diagnostic_metadata + .current_self_type + .as_ref() + .and_then(|ty| { + if let TyKind::Path(None, _) = ty.kind { + self.r.partial_res_map.get(&ty.id) + } else { + None + } + }) + .map(|res| res.base_res()) + .filter(|res| { + // Permit the types that unambiguously always + // result in the same type constructor being used + // (it can't differ between `Self` and `self`). + matches!( + res, + Res::Def(DefKind::Struct | DefKind::Union | DefKind::Enum, _,) | Res::PrimTy(_) + ) + }); + let mut visitor = SelfVisitor { r: self.r, impl_self, lifetime: Set1::Empty }; + visitor.visit_ty(ty); + trace!("SelfVisitor found={:?}", visitor.lifetime); + visitor.lifetime } /// Searches the current set of local scopes for labels. Returns the `NodeId` of the resolved @@ -1959,22 +2240,26 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ItemKind::Static(ref ty, _, ref expr) | ItemKind::Const(_, ref ty, ref expr) => { self.with_item_rib(|this| { - this.visit_ty(ty); - if let Some(expr) = expr { - let constant_item_kind = match item.kind { - ItemKind::Const(..) => ConstantItemKind::Const, - ItemKind::Static(..) => ConstantItemKind::Static, - _ => unreachable!(), - }; - // We already forbid generic params because of the above item rib, - // so it doesn't matter whether this is a trivial constant. - this.with_constant_rib( - IsRepeatExpr::No, - HasGenericParams::Yes, - Some((item.ident, constant_item_kind)), - |this| this.visit_expr(expr), - ); - } + this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Static), |this| { + this.visit_ty(ty); + }); + this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { + if let Some(expr) = expr { + let constant_item_kind = match item.kind { + ItemKind::Const(..) => ConstantItemKind::Const, + ItemKind::Static(..) => ConstantItemKind::Static, + _ => unreachable!(), + }; + // We already forbid generic params because of the above item rib, + // so it doesn't matter whether this is a trivial constant. + this.with_constant_rib( + IsRepeatExpr::No, + HasGenericParams::Yes, + Some((item.ident, constant_item_kind)), + |this| this.visit_expr(expr), + ); + } + }); }); } @@ -2045,7 +2330,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { { diagnostics::signal_lifetime_shadowing(self.r.session, original, param.ident); // Record lifetime res, so lowering knows there is something fishy. - self.record_lifetime_res(param.id, LifetimeRes::Error); + self.record_lifetime_param(param.id, LifetimeRes::Error); continue; } @@ -2056,7 +2341,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.report_error(param.ident.span, err); if let GenericParamKind::Lifetime = param.kind { // Record lifetime res, so lowering knows there is something fishy. - self.record_lifetime_res(param.id, LifetimeRes::Error); + self.record_lifetime_param(param.id, LifetimeRes::Error); continue; } } @@ -2075,7 +2360,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { .span_label(param.ident.span, "`'_` is a reserved lifetime name") .emit(); // Record lifetime res, so lowering knows there is something fishy. - self.record_lifetime_res(param.id, LifetimeRes::Error); + self.record_lifetime_param(param.id, LifetimeRes::Error); continue; } @@ -2090,7 +2375,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { .span_label(param.ident.span, "'static is a reserved lifetime name") .emit(); // Record lifetime res, so lowering knows there is something fishy. - self.record_lifetime_res(param.id, LifetimeRes::Error); + self.record_lifetime_param(param.id, LifetimeRes::Error); continue; } @@ -2102,7 +2387,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { GenericParamKind::Const { .. } => (&mut function_value_rib, DefKind::ConstParam), GenericParamKind::Lifetime => { let res = LifetimeRes::Param { param: def_id, binder }; - self.record_lifetime_res(param.id, res); + self.record_lifetime_param(param.id, res); function_lifetime_rib.bindings.insert(ident, (param.id, res)); continue; } @@ -2111,7 +2396,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { let res = match kind { ItemRibKind(..) | AssocItemRibKind => Res::Def(def_kind, def_id.to_def_id()), NormalRibKind => Res::Err, - _ => bug!("Unexpected rib kind {:?}", kind), + _ => span_bug!(param.ident.span, "Unexpected rib kind {:?}", kind), }; self.r.record_partial_res(param.id, PartialRes::new(res)); rib.bindings.insert(ident, res); @@ -2125,7 +2410,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.ribs[TypeNS].pop(); self.ribs[ValueNS].pop(); - self.lifetime_ribs.pop(); + let function_lifetime_rib = self.lifetime_ribs.pop().unwrap(); + + // Do not account for the parameters we just bound for function lifetime elision. + if let Some(ref mut candidates) = self.lifetime_elision_candidates { + for (_, res) in function_lifetime_rib.bindings.values() { + candidates.remove(res); + } + } if let LifetimeBinderKind::BareFnType | LifetimeBinderKind::WhereBound @@ -2223,20 +2515,26 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // // Type parameters can already be used and as associated consts are // not used as part of the type system, this is far less surprising. - self.with_constant_rib( - IsRepeatExpr::No, - HasGenericParams::Yes, - None, - |this| this.visit_expr(expr), + self.with_lifetime_rib( + LifetimeRibKind::Elided(LifetimeRes::Infer), + |this| { + this.with_constant_rib( + IsRepeatExpr::No, + HasGenericParams::Yes, + None, + |this| this.visit_expr(expr), + ) + }, ); } } AssocItemKind::Fn(box Fn { generics, .. }) => { walk_assoc_item(self, generics, LifetimeBinderKind::Function, item); } - AssocItemKind::TyAlias(box TyAlias { generics, .. }) => { - walk_assoc_item(self, generics, LifetimeBinderKind::Item, item); - } + AssocItemKind::TyAlias(box TyAlias { generics, .. }) => self + .with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| { + walk_assoc_item(this, generics, LifetimeBinderKind::Item, item) + }), AssocItemKind::MacCall(_) => { panic!("unexpanded macro in resolve!") } @@ -2307,7 +2605,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { LifetimeRibKind::Generics { span: generics.span, binder: item_id, - kind: LifetimeBinderKind::ImplBlock + kind: LifetimeBinderKind::ImplBlock, }, |this| { // Dummy self type for better errors if `Self` is used in the trait path. @@ -2327,7 +2625,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // Register the trait definitions from here. if let Some(trait_id) = trait_id { - this.r.trait_impls.entry(trait_id).or_default().push(item_def_id); + this.r + .trait_impls + .entry(trait_id) + .or_default() + .push(item_def_id); } let item_def_id = item_def_id.to_def_id(); @@ -2346,21 +2648,17 @@ 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,false), - |this| { - this.with_current_self_type(self_type, |this| { - this.with_self_rib_ns(ValueNS, Res::SelfCtor(item_def_id), |this| { - debug!("resolve_implementation with_self_rib_ns(ValueNS, ...)"); - for item in impl_items { - this.resolve_impl_item(&**item); - } - }); - }); - }, - ); + this.with_current_self_type(self_type, |this| { + this.with_self_rib_ns(ValueNS, Res::SelfCtor(item_def_id), |this| { + debug!("resolve_implementation with_self_rib_ns(ValueNS, ...)"); + for item in impl_items { + this.resolve_impl_item(&**item); + } + }); + }); }); }, - ); + ) }, ); }); @@ -2391,8 +2689,13 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // // Type parameters can already be used and as associated consts are // not used as part of the type system, this is far less surprising. - self.with_constant_rib(IsRepeatExpr::No, HasGenericParams::Yes, None, |this| { - this.visit_expr(expr) + self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { + this.with_constant_rib( + IsRepeatExpr::No, + HasGenericParams::Yes, + None, + |this| this.visit_expr(expr), + ) }); } } @@ -2435,18 +2738,20 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { kind: LifetimeBinderKind::Item, }, |this| { - // If this is a trait impl, ensure the type - // exists in trait - this.check_trait_item( - item.id, - item.ident, - &item.kind, - TypeNS, - item.span, - |i, s, c| TypeNotMemberOfTrait(i, s, c), - ); + this.with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| { + // If this is a trait impl, ensure the type + // exists in trait + this.check_trait_item( + item.id, + item.ident, + &item.kind, + TypeNS, + item.span, + |i, s, c| TypeNotMemberOfTrait(i, s, c), + ); - visit::walk_assoc_item(this, item, AssocCtxt::Impl) + visit::walk_assoc_item(this, item, AssocCtxt::Impl) + }); }, ); } @@ -3581,7 +3886,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ExprKind::Repeat(ref elem, ref ct) => { self.visit_expr(elem); self.with_lifetime_rib(LifetimeRibKind::AnonConst, |this| { - this.resolve_anon_const(ct, IsRepeatExpr::Yes) + this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Static), |this| { + this.resolve_anon_const(ct, IsRepeatExpr::Yes) + }) }); } ExprKind::ConstBlock(ref ct) => { diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index e428bae479b..6b49c6b1ac6 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1,7 +1,6 @@ use crate::diagnostics::{ImportSuggestion, LabelSuggestion, TypoSuggestion}; -use crate::late::lifetimes::{ElisionFailureInfo, LifetimeContext}; use crate::late::{AliasPossibility, LateResolutionVisitor, RibKind}; -use crate::late::{LifetimeBinderKind, LifetimeRibKind, LifetimeUseSet}; +use crate::late::{LifetimeBinderKind, LifetimeRes, LifetimeRibKind, LifetimeUseSet}; use crate::path_names_to_string; use crate::{Module, ModuleKind, ModuleOrUniformRoot}; use crate::{PathResult, PathSource, Segment}; @@ -9,10 +8,10 @@ use crate::{PathResult, PathSource, Segment}; use rustc_ast::visit::{FnCtxt, FnKind, LifetimeCtxt}; use rustc_ast::{ self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind, - NodeId, Path, Ty, TyKind, + NodeId, Path, Ty, TyKind, DUMMY_NODE_ID, }; use rustc_ast_pretty::pprust::path_segment_to_string; -use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, @@ -20,7 +19,7 @@ use rustc_errors::{ use rustc_hir as hir; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, CtorOf, DefKind}; -use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; +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; @@ -29,7 +28,7 @@ use rustc_span::edition::Edition; use rustc_span::hygiene::MacroKind; use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{BytePos, Span, DUMMY_SP}; +use rustc_span::{BytePos, Span}; use std::iter; use std::ops::Deref; @@ -59,45 +58,6 @@ impl AssocSuggestion { } } -pub(crate) enum MissingLifetimeSpot<'tcx> { - Generics(&'tcx hir::Generics<'tcx>), - HigherRanked { span: Span, span_type: ForLifetimeSpanType }, - Static, -} - -pub(crate) enum ForLifetimeSpanType { - BoundEmpty, - BoundTail, - TypeEmpty, - TypeTail, - ClosureEmpty, - ClosureTail, -} - -impl ForLifetimeSpanType { - pub(crate) fn descr(&self) -> &'static str { - match self { - Self::BoundEmpty | Self::BoundTail => "bound", - Self::TypeEmpty | Self::TypeTail => "type", - Self::ClosureEmpty | Self::ClosureTail => "closure", - } - } - - pub(crate) fn suggestion(&self, sugg: impl std::fmt::Display) -> String { - match self { - Self::BoundEmpty | Self::TypeEmpty => format!("for<{}> ", sugg), - Self::ClosureEmpty => format!("for<{}>", sugg), - Self::BoundTail | Self::TypeTail | Self::ClosureTail => format!(", {}", sugg), - } - } -} - -impl<'tcx> Into<MissingLifetimeSpot<'tcx>> for &&'tcx hir::Generics<'tcx> { - fn into(self) -> MissingLifetimeSpot<'tcx> { - MissingLifetimeSpot::Generics(self) - } -} - fn is_self_type(path: &[Segment], namespace: Namespace) -> bool { namespace == TypeNS && path.len() == 1 && path[0].ident.name == kw::SelfUpper } @@ -122,6 +82,56 @@ fn import_candidate_to_enum_paths(suggestion: &ImportSuggestion) -> (String, Str (variant_path_string, enum_path_string) } +/// Description of an elided lifetime. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub(super) struct MissingLifetime { + /// Used to overwrite the resolution with the suggestion, to avoid cascasing errors. + pub id: NodeId, + /// Where to suggest adding the lifetime. + pub span: Span, + /// How the lifetime was introduced, to have the correct space and comma. + pub kind: MissingLifetimeKind, + /// Number of elided lifetimes, used for elision in path. + pub count: usize, +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub(super) enum MissingLifetimeKind { + /// An explicit `'_`. + Underscore, + /// An elided lifetime `&' ty`. + Ampersand, + /// An elided lifetime in brackets with written brackets. + Comma, + /// An elided lifetime with elided brackets. + Brackets, +} + +/// Description of the lifetimes appearing in a function parameter. +/// This is used to provide a literal explanation to the elision failure. +#[derive(Clone, Debug)] +pub(super) struct ElisionFnParameter { + /// The index of the argument in the original definition. + pub index: usize, + /// The name of the argument if it's a simple ident. + pub ident: Option<Ident>, + /// The number of lifetimes in the parameter. + pub lifetime_count: usize, + /// The span of the parameter. + pub span: Span, +} + +/// Description of lifetimes that appear as candidates for elision. +/// This is used to suggest introducing an explicit lifetime. +#[derive(Debug)] +pub(super) enum LifetimeElisionCandidate { + /// This is not a real lifetime. + Ignore, + /// There is a named lifetime, we won't suggest anything. + Named, + Missing(MissingLifetime), +} + impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { fn def_span(&self, def_id: DefId) -> Option<Span> { match def_id.krate { @@ -1434,7 +1444,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { // Items from this module self.r.add_module_candidates(module, &mut names, &filter_fn); - if let ModuleKind::Block(..) = module.kind { + if let ModuleKind::Block = module.kind { // We can see through blocks } else { // Items from the prelude @@ -2003,18 +2013,35 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { err.span_label(lifetime_ref.ident.span, "undeclared lifetime"); err }; - let mut suggest_note = true; + self.suggest_introducing_lifetime( + &mut err, + Some(lifetime_ref.ident.name.as_str()), + |err, _, span, message, suggestion| { + err.span_suggestion(span, message, suggestion, Applicability::MaybeIncorrect); + true + }, + ); + err.emit(); + } + fn suggest_introducing_lifetime( + &self, + err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + name: Option<&str>, + suggest: impl Fn(&mut DiagnosticBuilder<'_, ErrorGuaranteed>, bool, Span, &str, String) -> bool, + ) { + let mut suggest_note = true; for rib in self.lifetime_ribs.iter().rev() { + let mut should_continue = true; match rib.kind { LifetimeRibKind::Generics { binder: _, span, kind } => { - if !span.can_be_used_for_suggestions() && suggest_note { + if !span.can_be_used_for_suggestions() && suggest_note && let Some(name) = name { suggest_note = false; // Avoid displaying the same help multiple times. err.span_label( span, &format!( "lifetime `{}` is missing in item created through this procedural macro", - lifetime_ref.ident, + name, ), ); continue; @@ -2030,46 +2057,42 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { let sugg = format!( "{}<{}>{}", if higher_ranked { "for" } else { "" }, - lifetime_ref.ident, + name.unwrap_or("'a"), if higher_ranked { " " } else { "" }, ); (span, sugg) } else { let span = self.r.session.source_map().span_through_char(span, '<').shrink_to_hi(); - let sugg = format!("{}, ", lifetime_ref.ident); + let sugg = format!("{}, ", name.unwrap_or("'a")); (span, sugg) }; if higher_ranked { - err.span_suggestion( - span, - &format!( - "consider making the {} lifetime-generic with a new `{}` lifetime", - kind.descr(), - lifetime_ref - ), - sugg, - Applicability::MaybeIncorrect, + let message = format!( + "consider making the {} lifetime-generic with a new `{}` lifetime", + kind.descr(), + name.unwrap_or("'a"), ); + should_continue = suggest(err, true, span, &message, sugg); err.note_once( "for more information on higher-ranked polymorphism, visit \ https://doc.rust-lang.org/nomicon/hrtb.html", ); + } else if let Some(name) = name { + let message = format!("consider introducing lifetime `{}` here", name); + should_continue = suggest(err, false, span, &message, sugg); } else { - err.span_suggestion( - span, - &format!("consider introducing lifetime `{}` here", lifetime_ref.ident), - sugg, - Applicability::MaybeIncorrect, - ); + let message = format!("consider introducing a named lifetime parameter"); + should_continue = suggest(err, false, span, &message, sugg); } } LifetimeRibKind::Item => break, _ => {} } + if !should_continue { + break; + } } - - err.emit(); } pub(crate) fn emit_non_static_lt_in_const_generic_error(&self, lifetime_ref: &ast::Lifetime) { @@ -2105,552 +2128,209 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { .emit(); } } -} -/// Report lifetime/lifetime shadowing as an error. -pub fn signal_lifetime_shadowing(sess: &Session, orig: Ident, shadower: Ident) { - let mut err = struct_span_err!( - sess, - shadower.span, - E0496, - "lifetime name `{}` shadows a lifetime name that is already in scope", - orig.name, - ); - err.span_label(orig.span, "first declared here"); - err.span_label(shadower.span, format!("lifetime `{}` already in scope", orig.name)); - err.emit(); -} - -/// Shadowing involving a label is only a warning for historical reasons. -//FIXME: make this a proper lint. -pub fn signal_label_shadowing(sess: &Session, orig: Span, shadower: Ident) { - let name = shadower.name; - let shadower = shadower.span; - let mut err = sess.struct_span_warn( - shadower, - &format!("label name `{}` shadows a label name that is already in scope", name), - ); - err.span_label(orig, "first declared here"); - err.span_label(shadower, format!("label `{}` already in scope", name)); - err.emit(); -} - -impl<'tcx> LifetimeContext<'_, 'tcx> { pub(crate) fn report_missing_lifetime_specifiers( - &self, - spans: Vec<Span>, - count: usize, - ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - struct_span_err!( - self.tcx.sess, + &mut self, + lifetime_refs: Vec<MissingLifetime>, + function_param_lifetimes: Option<(Vec<MissingLifetime>, Vec<ElisionFnParameter>)>, + ) -> ErrorGuaranteed { + let num_lifetimes: usize = lifetime_refs.iter().map(|lt| lt.count).sum(); + let spans: Vec<_> = lifetime_refs.iter().map(|lt| lt.span).collect(); + + let mut err = struct_span_err!( + self.r.session, spans, E0106, "missing lifetime specifier{}", - pluralize!(count) - ) + pluralize!(num_lifetimes) + ); + self.add_missing_lifetime_specifiers_label( + &mut err, + lifetime_refs, + function_param_lifetimes, + ); + err.emit() } - /// Returns whether to add `'static` lifetime to the suggested lifetime list. - pub(crate) fn report_elision_failure( - &self, - diag: &mut Diagnostic, - params: &[ElisionFailureInfo], - ) -> bool { - let mut m = String::new(); - let len = params.len(); + pub(crate) fn add_missing_lifetime_specifiers_label( + &mut self, + err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + lifetime_refs: Vec<MissingLifetime>, + function_param_lifetimes: Option<(Vec<MissingLifetime>, Vec<ElisionFnParameter>)>, + ) { + for < in &lifetime_refs { + err.span_label( + lt.span, + format!( + "expected {} lifetime parameter{}", + if lt.count == 1 { "named".to_string() } else { lt.count.to_string() }, + pluralize!(lt.count), + ), + ); + } - let elided_params: Vec<_> = - params.iter().cloned().filter(|info| info.lifetime_count > 0).collect(); + let mut in_scope_lifetimes: Vec<_> = self + .lifetime_ribs + .iter() + .rev() + .take_while(|rib| !matches!(rib.kind, LifetimeRibKind::Item)) + .flat_map(|rib| rib.bindings.iter()) + .map(|(&ident, &res)| (ident, res)) + .filter(|(ident, _)| ident.name != kw::UnderscoreLifetime) + .collect(); + debug!(?in_scope_lifetimes); - let elided_len = elided_params.len(); + debug!(?function_param_lifetimes); + if let Some((param_lifetimes, params)) = &function_param_lifetimes { + let elided_len = param_lifetimes.len(); + let num_params = params.len(); - for (i, info) in elided_params.into_iter().enumerate() { - let ElisionFailureInfo { parent, index, lifetime_count: n, have_bound_regions, span } = - info; + let mut m = String::new(); - diag.span_label(span, ""); - let help_name = if let Some(ident) = - parent.and_then(|body| self.tcx.hir().body(body).params[index].pat.simple_ident()) - { - format!("`{}`", ident) - } else { - format!("argument {}", index + 1) - }; + for (i, info) in params.iter().enumerate() { + let ElisionFnParameter { ident, index, lifetime_count, span } = *info; + debug_assert_ne!(lifetime_count, 0); - m.push_str( - &(if n == 1 { - help_name + err.span_label(span, ""); + + if i != 0 { + if i + 1 < num_params { + m.push_str(", "); + } else if num_params == 2 { + m.push_str(" or "); + } else { + m.push_str(", or "); + } + } + + let help_name = if let Some(ident) = ident { + format!("`{}`", ident) } else { - format!( - "one of {}'s {} {}lifetimes", - help_name, - n, - if have_bound_regions { "free " } else { "" } - ) - })[..], - ); + format!("argument {}", index + 1) + }; - if elided_len == 2 && i == 0 { - m.push_str(" or "); - } else if i + 2 == elided_len { - m.push_str(", or "); - } else if i != elided_len - 1 { - m.push_str(", "); + if lifetime_count == 1 { + m.push_str(&help_name[..]) + } else { + m.push_str(&format!("one of {}'s {} lifetimes", help_name, lifetime_count)[..]) + } } - } - if len == 0 { - diag.help( - "this function's return type contains a borrowed value, \ + if num_params == 0 { + err.help( + "this function's return type contains a borrowed value, \ but there is no value for it to be borrowed from", - ); - true - } else if elided_len == 0 { - diag.help( - "this function's return type contains a borrowed value with \ + ); + if in_scope_lifetimes.is_empty() { + in_scope_lifetimes = vec![( + Ident::with_dummy_span(kw::StaticLifetime), + (DUMMY_NODE_ID, LifetimeRes::Static), + )]; + } + } else if elided_len == 0 { + err.help( + "this function's return type contains a borrowed value with \ an elided lifetime, but the lifetime cannot be derived from \ the arguments", - ); - true - } else if elided_len == 1 { - diag.help(&format!( - "this function's return type contains a borrowed value, \ + ); + if in_scope_lifetimes.is_empty() { + in_scope_lifetimes = vec![( + Ident::with_dummy_span(kw::StaticLifetime), + (DUMMY_NODE_ID, LifetimeRes::Static), + )]; + } + } else if num_params == 1 { + err.help(&format!( + "this function's return type contains a borrowed value, \ but the signature does not say which {} it is borrowed from", - m - )); - false - } else { - diag.help(&format!( - "this function's return type contains a borrowed value, \ + m + )); + } else { + err.help(&format!( + "this function's return type contains a borrowed value, \ but the signature does not say whether it is borrowed from {}", - m - )); - false + m + )); + } } - } - 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(), - self.tcx.lang_items().fn_trait(), - self.tcx.lang_items().fn_mut_trait(), - ] - .contains(&Some(did)) - { - let (span, span_type) = if let Some(bound) = - trait_ref.bound_generic_params.iter().rfind(|param| { - matches!( - param.kind, - hir::GenericParamKind::Lifetime { - kind: hir::LifetimeParamKind::Explicit - } - ) - }) { - (bound.span.shrink_to_hi(), ForLifetimeSpanType::BoundTail) - } else { - (trait_ref.span.shrink_to_lo(), ForLifetimeSpanType::BoundEmpty) - }; - self.missing_named_lifetime_spots - .push(MissingLifetimeSpot::HigherRanked { span, span_type }); - return true; - } + let existing_name = match &in_scope_lifetimes[..] { + [] => Symbol::intern("'a"), + [(existing, _)] => existing.name, + _ => Symbol::intern("'lifetime"), }; - false - } - - pub(crate) fn add_missing_lifetime_specifiers_label( - &self, - err: &mut Diagnostic, - mut spans_with_counts: Vec<(Span, usize)>, - in_scope_lifetimes: FxIndexSet<LocalDefId>, - params: Option<&[ElisionFailureInfo]>, - ) { - let (mut lifetime_names, lifetime_spans): (FxHashSet<_>, Vec<_>) = in_scope_lifetimes - .iter() - .filter_map(|def_id| { - let name = self.tcx.item_name(def_id.to_def_id()); - let span = self.tcx.def_ident_span(def_id.to_def_id())?; - Some((name, span)) - }) - .filter(|&(n, _)| n != kw::UnderscoreLifetime) - .unzip(); - if let Some(params) = params { - // If there's no lifetime available, suggest `'static`. - if self.report_elision_failure(err, params) && lifetime_names.is_empty() { - lifetime_names.insert(kw::StaticLifetime); + let mut spans_suggs: Vec<_> = Vec::new(); + let build_sugg = |lt: MissingLifetime| match lt.kind { + MissingLifetimeKind::Underscore => { + debug_assert_eq!(lt.count, 1); + (lt.span, existing_name.to_string()) } - } - let params = params.unwrap_or(&[]); - - let snippets: Vec<Option<String>> = spans_with_counts - .iter() - .map(|(span, _)| self.tcx.sess.source_map().span_to_snippet(*span).ok()) - .collect(); - - // Empty generics are marked with a span of "<", but since from now on - // that information is in the snippets it can be removed from the spans. - for ((span, _), snippet) in spans_with_counts.iter_mut().zip(&snippets) { - if snippet.as_deref() == Some("<") { - *span = span.shrink_to_hi(); + MissingLifetimeKind::Ampersand => { + debug_assert_eq!(lt.count, 1); + (lt.span.shrink_to_hi(), format!("{} ", existing_name)) } + MissingLifetimeKind::Comma => { + let sugg: String = std::iter::repeat([existing_name.as_str(), ", "]) + .take(lt.count) + .flatten() + .collect(); + (lt.span.shrink_to_hi(), sugg) + } + MissingLifetimeKind::Brackets => { + let sugg: String = std::iter::once("<") + .chain( + std::iter::repeat(existing_name.as_str()).take(lt.count).intersperse(", "), + ) + .chain([">"]) + .collect(); + (lt.span.shrink_to_hi(), sugg) + } + }; + for < in &lifetime_refs { + spans_suggs.push(build_sugg(lt)); } - - for &(span, count) in &spans_with_counts { - err.span_label( - span, - format!( - "expected {} lifetime parameter{}", - if count == 1 { "named".to_string() } else { count.to_string() }, - pluralize!(count), - ), - ); - } - - let suggest_existing = - |err: &mut Diagnostic, - name: Symbol, - formatters: Vec<Option<Box<dyn Fn(Symbol) -> String>>>| { - if let Some(MissingLifetimeSpot::HigherRanked { span: for_span, span_type }) = - self.missing_named_lifetime_spots.iter().rev().next() - { - // When we have `struct S<'a>(&'a dyn Fn(&X) -> &X);` we want to not only suggest - // using `'a`, but also introduce the concept of HRLTs by suggesting - // `struct S<'a>(&'a dyn for<'b> Fn(&X) -> &'b X);`. (#72404) - let mut introduce_suggestion = vec![]; - - let a_to_z_repeat_n = |n| { - (b'a'..=b'z').map(move |c| { - let mut s = '\''.to_string(); - s.extend(std::iter::repeat(char::from(c)).take(n)); - s - }) - }; - - // If all single char lifetime names are present, we wrap around and double the chars. - let lt_name = (1..) - .flat_map(a_to_z_repeat_n) - .map(|lt| Symbol::intern(<)) - .find(|lt| !lifetime_names.contains(lt)) - .unwrap(); - let msg = format!( - "consider making the {} lifetime-generic with a new `{}` lifetime", - span_type.descr(), - lt_name, - ); - err.note( - "for more information on higher-ranked polymorphism, visit \ - https://doc.rust-lang.org/nomicon/hrtb.html", - ); - let for_sugg = span_type.suggestion(<_name); - for param in params { - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(param.span) - { - if snippet.starts_with('&') && !snippet.starts_with("&'") { - introduce_suggestion - .push((param.span, format!("&{} {}", lt_name, &snippet[1..]))); - } else if let Some(stripped) = snippet.strip_prefix("&'_ ") { - introduce_suggestion - .push((param.span, format!("&{} {}", lt_name, stripped))); - } - } + debug!(?spans_suggs); + match in_scope_lifetimes.len() { + 0 => { + if let Some((param_lifetimes, _)) = function_param_lifetimes { + for lt in param_lifetimes { + spans_suggs.push(build_sugg(lt)) } - introduce_suggestion.push((*for_span, for_sugg)); - for ((span, _), formatter) in spans_with_counts.iter().zip(formatters.iter()) { - if let Some(formatter) = formatter { - introduce_suggestion.push((*span, formatter(lt_name))); - } - } - err.multipart_suggestion_verbose( - &msg, - introduce_suggestion, - Applicability::MaybeIncorrect, - ); } - - let spans_suggs: Vec<_> = formatters - .into_iter() - .zip(spans_with_counts.iter()) - .filter_map(|(formatter, (span, _))| { - if let Some(formatter) = formatter { - Some((*span, formatter(name))) - } else { - None - } - }) - .collect(); - if spans_suggs.is_empty() { - // If all the spans come from macros, we cannot extract snippets and then - // `formatters` only contains None and `spans_suggs` is empty. - return; - } - err.multipart_suggestion_verbose( - &format!( - "consider using the `{}` lifetime", - lifetime_names.iter().next().unwrap() - ), - spans_suggs, - Applicability::MaybeIncorrect, - ); - }; - let suggest_new = |err: &mut Diagnostic, suggs: Vec<Option<String>>| { - for missing in self.missing_named_lifetime_spots.iter().rev() { - let mut introduce_suggestion = vec![]; - let msg; - let should_break; - introduce_suggestion.push(match missing { - MissingLifetimeSpot::Generics(generics) => { - if generics.span == DUMMY_SP { - // Account for malformed generics in the HIR. This shouldn't happen, - // but if we make a mistake elsewhere, mainly by keeping something in - // `missing_named_lifetime_spots` that we shouldn't, like associated - // `const`s or making a mistake in the AST lowering we would provide - // nonsensical suggestions. Guard against that by skipping these. - // (#74264) - continue; - } - msg = "consider introducing a named lifetime parameter".to_string(); - should_break = true; - if let Some(param) = generics.params.iter().find(|p| { - !matches!( - p.kind, - hir::GenericParamKind::Type { synthetic: true, .. } - | hir::GenericParamKind::Lifetime { - kind: hir::LifetimeParamKind::Elided - } - ) - }) { - (param.span.shrink_to_lo(), "'a, ".to_string()) - } else { - (generics.span, "<'a>".to_string()) - } - } - MissingLifetimeSpot::HigherRanked { span, span_type } => { - msg = format!( - "consider making the {} lifetime-generic with a new `'a` lifetime", - span_type.descr(), - ); - should_break = false; - err.note( - "for more information on higher-ranked polymorphism, visit \ - https://doc.rust-lang.org/nomicon/hrtb.html", - ); - (*span, span_type.suggestion("'a")) - } - MissingLifetimeSpot::Static => { - let mut spans_suggs = Vec::new(); - for ((span, count), snippet) in - spans_with_counts.iter().copied().zip(snippets.iter()) - { - let (span, sugg) = match snippet.as_deref() { - Some("&") => (span.shrink_to_hi(), "'static ".to_owned()), - Some("'_") => (span, "'static".to_owned()), - Some(snippet) if !snippet.ends_with('>') => { - if snippet == "" { - ( - span, - std::iter::repeat("'static") - .take(count) - .collect::<Vec<_>>() - .join(", "), - ) - } else if snippet == "<" || snippet == "(" { - ( - span.shrink_to_hi(), - std::iter::repeat("'static") - .take(count) - .collect::<Vec<_>>() - .join(", "), - ) - } else { - ( - span.shrink_to_hi(), - format!( - "<{}>", - std::iter::repeat("'static") - .take(count) - .collect::<Vec<_>>() - .join(", "), - ), - ) - } - } - _ => continue, - }; - spans_suggs.push((span, sugg.to_string())); - } + self.suggest_introducing_lifetime( + err, + None, + |err, higher_ranked, span, message, intro_sugg| { err.multipart_suggestion_verbose( - "consider using the `'static` lifetime", - spans_suggs, + message, + std::iter::once((span, intro_sugg)) + .chain(spans_suggs.clone()) + .collect(), Applicability::MaybeIncorrect, ); - continue; - } - }); - - struct Lifetime(Span, String); - impl Lifetime { - fn is_unnamed(&self) -> bool { - self.1.starts_with('&') && !self.1.starts_with("&'") - } - fn is_underscore(&self) -> bool { - self.1.starts_with("&'_ ") - } - fn is_named(&self) -> bool { - self.1.starts_with("&'") - } - fn suggestion(&self, sugg: String) -> Option<(Span, String)> { - Some( - match ( - self.is_unnamed(), - self.is_underscore(), - self.is_named(), - sugg.starts_with('&'), - ) { - (true, _, _, false) => (self.span_unnamed_borrow(), sugg), - (true, _, _, true) => { - (self.span_unnamed_borrow(), sugg[1..].to_string()) - } - (_, true, _, false) => { - (self.span_underscore_borrow(), sugg.trim().to_string()) - } - (_, true, _, true) => { - (self.span_underscore_borrow(), sugg[1..].trim().to_string()) - } - (_, _, true, false) => { - (self.span_named_borrow(), sugg.trim().to_string()) - } - (_, _, true, true) => { - (self.span_named_borrow(), sugg[1..].trim().to_string()) - } - _ => return None, - }, - ) - } - fn span_unnamed_borrow(&self) -> Span { - let lo = self.0.lo() + BytePos(1); - self.0.with_lo(lo).with_hi(lo) - } - fn span_named_borrow(&self) -> Span { - let lo = self.0.lo() + BytePos(1); - self.0.with_lo(lo) - } - fn span_underscore_borrow(&self) -> Span { - let lo = self.0.lo() + BytePos(1); - let hi = lo + BytePos(2); - self.0.with_lo(lo).with_hi(hi) - } - } - - for param in params { - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(param.span) { - if let Some((span, sugg)) = - Lifetime(param.span, snippet).suggestion("'a ".to_string()) - { - introduce_suggestion.push((span, sugg)); - } - } - } - for (span, sugg) in spans_with_counts.iter().copied().zip(suggs.iter()).filter_map( - |((span, _), sugg)| match &sugg { - Some(sugg) => Some((span, sugg.to_string())), - _ => None, + higher_ranked }, - ) { - let (span, sugg) = self - .tcx - .sess - .source_map() - .span_to_snippet(span) - .ok() - .and_then(|snippet| Lifetime(span, snippet).suggestion(sugg.clone())) - .unwrap_or((span, sugg)); - introduce_suggestion.push((span, sugg.to_string())); - } + ); + } + 1 => { err.multipart_suggestion_verbose( - &msg, - introduce_suggestion, + &format!("consider using the `{}` lifetime", existing_name), + spans_suggs, Applicability::MaybeIncorrect, ); - if should_break { - break; - } - } - }; - let lifetime_names: Vec<_> = lifetime_names.iter().collect(); - match &lifetime_names[..] { - [name] => { - let mut suggs: Vec<Option<Box<dyn Fn(Symbol) -> String>>> = Vec::new(); - for (snippet, (_, count)) in snippets.iter().zip(spans_with_counts.iter().copied()) - { - suggs.push(match snippet.as_deref() { - Some("&") => Some(Box::new(|name| format!("&{} ", name))), - Some("'_") => Some(Box::new(|n| n.to_string())), - Some("") => Some(Box::new(move |n| format!("{}, ", n).repeat(count))), - Some("<") => Some(Box::new(move |n| { - std::iter::repeat(n) - .take(count) - .map(|n| n.to_string()) - .collect::<Vec<_>>() - .join(", ") - })), - Some(snippet) if !snippet.ends_with('>') => Some(Box::new(move |name| { - format!( - "{}<{}>", - snippet, - std::iter::repeat(name.to_string()) - .take(count) - .collect::<Vec<_>>() - .join(", ") - ) - })), - _ => None, - }); + // Record as using the suggested resolution. + let (_, (_, res)) = in_scope_lifetimes[0]; + for < in &lifetime_refs { + self.r.lifetimes_res_map.insert(lt.id, res); } - suggest_existing(err, **name, suggs); } - [] => { - let mut suggs = Vec::new(); - for (snippet, (_, count)) in - snippets.iter().cloned().zip(spans_with_counts.iter().copied()) - { - suggs.push(match snippet.as_deref() { - Some("&") => Some("&'a ".to_string()), - Some("'_") => Some("'a".to_string()), - Some("") => { - Some(std::iter::repeat("'a, ").take(count).collect::<Vec<_>>().join("")) - } - Some("<") => { - Some(std::iter::repeat("'a").take(count).collect::<Vec<_>>().join(", ")) - } - Some(snippet) => Some(format!( - "{}<{}>", - snippet, - std::iter::repeat("'a").take(count).collect::<Vec<_>>().join(", "), - )), - None => None, - }); - } - suggest_new(err, suggs); - } - lts if lts.len() > 1 => { + _ => { + let lifetime_spans: Vec<_> = + in_scope_lifetimes.iter().map(|(ident, _)| ident.span).collect(); err.span_note(lifetime_spans, "these named lifetimes are available to use"); - let mut spans_suggs: Vec<_> = Vec::new(); - for ((span, _), snippet) in spans_with_counts.iter().copied().zip(snippets.iter()) { - match snippet.as_deref() { - Some("") => spans_suggs.push((span, "'lifetime, ".to_string())), - Some("&") => spans_suggs - .push((span.with_lo(span.lo() + BytePos(1)), "'lifetime ".to_string())), - _ => {} - } - } - if spans_suggs.len() > 0 { // This happens when we have `Foo<T>` where we point at the space before `T`, // but this can be confusing so we give a suggestion with placeholders. @@ -2661,7 +2341,34 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { ); } } - _ => unreachable!(), } } } + +/// Report lifetime/lifetime shadowing as an error. +pub fn signal_lifetime_shadowing(sess: &Session, orig: Ident, shadower: Ident) { + let mut err = struct_span_err!( + sess, + shadower.span, + E0496, + "lifetime name `{}` shadows a lifetime name that is already in scope", + orig.name, + ); + err.span_label(orig.span, "first declared here"); + err.span_label(shadower.span, format!("lifetime `{}` already in scope", orig.name)); + err.emit(); +} + +/// Shadowing involving a label is only a warning for historical reasons. +//FIXME: make this a proper lint. +pub fn signal_label_shadowing(sess: &Session, orig: Span, shadower: Ident) { + let name = shadower.name; + let shadower = shadower.span; + let mut err = sess.struct_span_warn( + shadower, + &format!("label name `{}` shadows a label name that is already in scope", name), + ); + err.span_label(orig, "first declared here"); + err.span_label(shadower, format!("label `{}` already in scope", name)); + err.emit(); +} diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index 0eb11cd3e9f..94460e33d8b 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -1,12 +1,11 @@ -// ignore-tidy-filelength -//! Name resolution for lifetimes. +//! Resolution of early vs late bound lifetimes. //! -//! Name resolution for lifetimes follows *much* simpler rules than the -//! full resolve. For example, lifetime names are never exported or -//! used between functions, and they operate in a purely top-down -//! way. Therefore, we break lifetime name resolution into a separate pass. +//! Name resolution for lifetimes is performed on the AST and embedded into HIR. From this +//! information, typechecking needs to transform the lifetime parameters into bound lifetimes. +//! Lifetimes can be early-bound or late-bound. Construction of typechecking terms needs to visit +//! the types in HIR to identify late-bound lifetimes and assign their Debruijn indices. This file +//! is also responsible for assigning their semantics to implicit lifetimes in trait objects. -use crate::late::diagnostics::{ForLifetimeSpanType, MissingLifetimeSpot}; use rustc_ast::walk_list; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::struct_span_err; @@ -14,18 +13,16 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefIdMap, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{GenericArg, GenericParam, LifetimeName, Node}; -use rustc_hir::{GenericParamKind, HirIdMap, LifetimeParamKind}; +use rustc_hir::{GenericArg, GenericParam, GenericParamKind, HirIdMap, LifetimeName, Node}; +use rustc_middle::bug; use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_lifetime::*; use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt}; -use rustc_middle::{bug, span_bug}; use rustc_span::def_id::DefId; -use rustc_span::symbol::{kw, sym, Ident}; +use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use std::borrow::Cow; -use std::cell::Cell; use std::fmt; use std::mem::take; @@ -34,8 +31,6 @@ trait RegionExt { fn late(index: u32, hir_map: Map<'_>, param: &GenericParam<'_>) -> (LocalDefId, Region); - fn late_anon(named_late_bound_vars: u32, index: &Cell<u32>) -> Region; - fn id(&self) -> Option<DefId>; fn shifted(self, amount: u32) -> Region; @@ -66,16 +61,9 @@ impl RegionExt for Region { (def_id, Region::LateBound(depth, idx, def_id.to_def_id())) } - fn late_anon(named_late_bound_vars: u32, index: &Cell<u32>) -> Region { - let i = index.get(); - index.set(i + 1); - let depth = ty::INNERMOST; - Region::LateBoundAnon(depth, named_late_bound_vars + i, i) - } - fn id(&self) -> Option<DefId> { match *self { - Region::Static | Region::LateBoundAnon(..) => None, + Region::Static => None, Region::EarlyBound(_, id) | Region::LateBound(_, _, id) | Region::Free(_, id) => { Some(id) @@ -88,9 +76,6 @@ impl RegionExt for Region { Region::LateBound(debruijn, idx, id) => { Region::LateBound(debruijn.shifted_in(amount), idx, id) } - Region::LateBoundAnon(debruijn, index, anon_index) => { - Region::LateBoundAnon(debruijn.shifted_in(amount), index, anon_index) - } _ => self, } } @@ -100,9 +85,6 @@ impl RegionExt for Region { Region::LateBound(debruijn, index, id) => { Region::LateBound(debruijn.shifted_out_to_binder(binder), index, id) } - Region::LateBoundAnon(debruijn, index, anon_index) => { - Region::LateBoundAnon(debruijn.shifted_out_to_binder(binder), index, anon_index) - } _ => self, } } @@ -152,10 +134,6 @@ pub(crate) struct LifetimeContext<'a, 'tcx> { /// Cache for cross-crate per-definition object lifetime defaults. xcrate_object_lifetime_defaults: DefIdMap<Vec<ObjectLifetimeDefault>>, - - /// When encountering an undefined named lifetime, we will suggest introducing it in these - /// places. - pub(crate) missing_named_lifetime_spots: Vec<MissingLifetimeSpot<'tcx>>, } #[derive(Debug)] @@ -198,10 +176,6 @@ enum Scope<'a> { s: ScopeRef<'a>, - /// In some cases not allowing late bounds allows us to avoid ICEs. - /// This is almost ways set to true. - allow_late_bound: bool, - /// If this binder comes from a where clause, specify how it was created. /// This is used to diagnose inaccessible lifetimes in APIT: /// ```ignore (illustrative) @@ -220,9 +194,8 @@ enum Scope<'a> { }, /// A scope which either determines unspecified lifetimes or errors - /// on them (e.g., due to ambiguity). For more details, see `Elide`. + /// on them (e.g., due to ambiguity). Elision { - elide: Elide, s: ScopeRef<'a>, }, @@ -278,7 +251,6 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { opaque_type_parent, scope_type, hir_id, - allow_late_bound, where_bound_origin, s: _, } => f @@ -288,16 +260,13 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { .field("opaque_type_parent", opaque_type_parent) .field("scope_type", scope_type) .field("hir_id", hir_id) - .field("allow_late_bound", allow_late_bound) .field("where_bound_origin", where_bound_origin) .field("s", &"..") .finish(), Scope::Body { id, s: _ } => { f.debug_struct("Body").field("id", id).field("s", &"..").finish() } - Scope::Elision { elide, s: _ } => { - f.debug_struct("Elision").field("elide", elide).field("s", &"..").finish() - } + Scope::Elision { s: _ } => f.debug_struct("Elision").field("s", &"..").finish(), Scope::ObjectLifetimeDefault { lifetime, s: _ } => f .debug_struct("ObjectLifetimeDefault") .field("lifetime", lifetime) @@ -314,32 +283,6 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { } } -#[derive(Clone, Debug)] -enum Elide { - /// Use a fresh anonymous late-bound lifetime each time, by - /// incrementing the counter to generate sequential indices. All - /// anonymous lifetimes must start *after* named bound vars. - FreshLateAnon(u32, Cell<u32>), - /// Always use this one lifetime. - Exact(Region), - /// Less or more than one lifetime were found, error on unspecified. - Error(Vec<ElisionFailureInfo>), - /// Forbid lifetime elision inside of a larger scope where it would be - /// permitted. For example, in let position impl trait. - Forbid, -} - -#[derive(Clone, Debug)] -pub(crate) struct ElisionFailureInfo { - /// Where we can find the argument pattern. - pub(crate) parent: Option<hir::BodyId>, - /// The index of the argument in the original definition. - 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>; const ROOT_SCOPE: ScopeRef<'static> = &Scope::Root; @@ -421,7 +364,6 @@ fn do_resolve( scope: ROOT_SCOPE, trait_definition_only, xcrate_object_lifetime_defaults: Default::default(), - missing_named_lifetime_spots: vec![], }; visitor.visit_item(item); @@ -503,9 +445,6 @@ fn late_region_as_bound_region<'tcx>(tcx: TyCtxt<'tcx>, region: &Region) -> ty:: let name = tcx.hir().name(tcx.hir().local_def_id_to_hir_id(def_id.expect_local())); ty::BoundVariableKind::Region(ty::BrNamed(*def_id, name)) } - Region::LateBoundAnon(_, _, anon_idx) => { - ty::BoundVariableKind::Region(ty::BrAnon(*anon_idx)) - } _ => bug!("{:?} is not a late region", region), } } @@ -640,44 +579,14 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { next_early_index, opaque_type_parent: false, scope_type: BinderScopeType::Normal, - allow_late_bound: true, where_bound_origin: None, }; - if let &hir::ClosureBinder::For { span, .. } = binder { - let last_lt = bound_generic_params - .iter() - .filter(|p| { - matches!( - p, - GenericParam { - kind: GenericParamKind::Lifetime { - kind: LifetimeParamKind::Explicit - }, - .. - } - ) - }) - .last(); - let (span, span_type) = match last_lt { - Some(GenericParam { span: last_sp, .. }) => { - (last_sp.shrink_to_hi(), ForLifetimeSpanType::ClosureTail) - } - None => (span, ForLifetimeSpanType::ClosureEmpty), - }; - self.missing_named_lifetime_spots - .push(MissingLifetimeSpot::HigherRanked { span, span_type }); - } - self.with(scope, |this| { // a closure has no bounds, so everything // contained within is scoped within its binder. intravisit::walk_expr(this, e) }); - - if let hir::ClosureBinder::For { .. } = binder { - self.missing_named_lifetime_spots.pop(); - } } else { intravisit::walk_expr(self, e) } @@ -694,11 +603,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } match item.kind { hir::ItemKind::Fn(_, ref generics, _) => { - self.missing_named_lifetime_spots.push(generics.into()); self.visit_early_late(None, item.hir_id(), generics, |this| { intravisit::walk_item(this, item); }); - self.missing_named_lifetime_spots.pop(); } hir::ItemKind::ExternCrate(_) @@ -712,8 +619,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } hir::ItemKind::Static(..) | hir::ItemKind::Const(..) => { // No lifetime parameters, but implied 'static. - let scope = Scope::Elision { elide: Elide::Exact(Region::Static), s: ROOT_SCOPE }; - self.with(scope, |this| intravisit::walk_item(this, item)); + self.with(Scope::Elision { s: self.scope }, |this| { + intravisit::walk_item(this, item) + }); } hir::ItemKind::OpaqueTy(hir::OpaqueTy { .. }) => { // Opaque types are visited when we visit the @@ -761,8 +669,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { | hir::ItemKind::Trait(_, _, ref generics, ..) | hir::ItemKind::TraitAlias(ref generics, ..) | hir::ItemKind::Impl(hir::Impl { ref generics, .. }) => { - self.missing_named_lifetime_spots.push(generics.into()); - // 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 @@ -791,7 +697,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { opaque_type_parent: true, scope_type: BinderScopeType::Normal, s: ROOT_SCOPE, - allow_late_bound: false, where_bound_origin: None, }; self.with(scope, |this| { @@ -800,7 +705,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { intravisit::walk_item(this, item); }); }); - self.missing_named_lifetime_spots.pop(); } } } @@ -826,20 +730,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { match ty.kind { hir::TyKind::BareFn(ref c) => { let next_early_index = self.next_early_index(); - let lifetime_span: Option<Span> = - c.generic_params.iter().rev().find_map(|param| match param.kind { - GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit } => { - Some(param.span) - } - _ => None, - }); - let (span, span_type) = if let Some(span) = lifetime_span { - (span.shrink_to_hi(), ForLifetimeSpanType::TypeTail) - } else { - (ty.span.shrink_to_lo(), ForLifetimeSpanType::TypeEmpty) - }; - self.missing_named_lifetime_spots - .push(MissingLifetimeSpot::HigherRanked { span, span_type }); let (lifetimes, binders): (FxIndexMap<LocalDefId, Region>, Vec<_>) = c .generic_params .iter() @@ -859,7 +749,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { next_early_index, opaque_type_parent: false, scope_type: BinderScopeType::Normal, - allow_late_bound: true, where_bound_origin: None, }; self.with(scope, |this| { @@ -867,7 +756,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // contained within is scoped within its binder. intravisit::walk_ty(this, ty); }); - self.missing_named_lifetime_spots.pop(); } hir::TyKind::TraitObject(bounds, ref lifetime, _) => { debug!(?bounds, ?lifetime, "TraitObject"); @@ -878,11 +766,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } }); match lifetime.name { - LifetimeName::Implicit => { - // For types like `dyn Foo`, we should - // generate a special form of elided. - span_bug!(ty.span, "object-lifetime-default expected, not implicit",); - } LifetimeName::ImplicitObjectLifetimeDefault => { // If the user does not write *anything*, we // use the object lifetime defaulting @@ -890,13 +773,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // `Box<dyn Debug + 'static>`. self.resolve_object_lifetime_default(lifetime) } - LifetimeName::Underscore => { + LifetimeName::Infer => { // If the user writes `'_`, we use the *ordinary* elision // rules. So the `'_` in e.g., `Box<dyn Debug + '_>` will be // resolved the same as the `'_` in `&'_ Foo`. // // cc #48468 - self.resolve_elided_lifetimes(&[lifetime]) } LifetimeName::Param(..) | LifetimeName::Static => { // If the user wrote an explicit name, use that. @@ -931,7 +813,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // position impl Trait let scope = Scope::TraitRefBoundary { s: self.scope }; self.with(scope, |this| { - let scope = Scope::Elision { elide: Elide::Forbid, s: this.scope }; + let scope = Scope::Elision { s: this.scope }; this.with(scope, |this| { intravisit::walk_item(this, opaque_ty); }) @@ -1007,24 +889,14 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { let mut index = self.next_early_index_for_opaque_type(); debug!(?index); - let mut elision = None; let mut lifetimes = FxIndexMap::default(); let mut non_lifetime_count = 0; + debug!(?generics.params); for param in generics.params { match param.kind { GenericParamKind::Lifetime { .. } => { let (def_id, reg) = Region::early(self.tcx.hir(), &mut index, ¶m); - if let hir::ParamName::Plain(Ident { - name: kw::UnderscoreLifetime, - .. - }) = param.name - { - // Pick the elided lifetime "definition" if one exists - // and use it to make an elision scope. - elision = Some(reg); - } else { - lifetimes.insert(def_id, reg); - } + lifetimes.insert(def_id, reg); } GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => { non_lifetime_count += 1; @@ -1034,51 +906,24 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { let next_early_index = index + non_lifetime_count; self.map.late_bound_vars.insert(ty.hir_id, vec![]); - if let Some(elision_region) = elision { - let scope = - Scope::Elision { elide: Elide::Exact(elision_region), s: self.scope }; - self.with(scope, |this| { - let scope = Scope::Binder { - hir_id: ty.hir_id, - lifetimes, - next_early_index, - s: this.scope, - opaque_type_parent: false, - scope_type: BinderScopeType::Normal, - allow_late_bound: false, - where_bound_origin: None, - }; - this.with(scope, |this| { - this.visit_generics(generics); - let scope = Scope::TraitRefBoundary { s: this.scope }; - this.with(scope, |this| { - for bound in bounds { - this.visit_param_bound(bound); - } - }) - }); - }); - } else { - let scope = Scope::Binder { - hir_id: ty.hir_id, - lifetimes, - next_early_index, - s: self.scope, - opaque_type_parent: false, - scope_type: BinderScopeType::Normal, - allow_late_bound: false, - where_bound_origin: None, - }; - self.with(scope, |this| { - let scope = Scope::TraitRefBoundary { s: this.scope }; - this.with(scope, |this| { - this.visit_generics(generics); - for bound in bounds { - this.visit_param_bound(bound); - } - }) - }); - } + let scope = Scope::Binder { + hir_id: ty.hir_id, + lifetimes, + next_early_index, + s: self.scope, + opaque_type_parent: false, + scope_type: BinderScopeType::Normal, + where_bound_origin: None, + }; + self.with(scope, |this| { + let scope = Scope::TraitRefBoundary { s: this.scope }; + this.with(scope, |this| { + this.visit_generics(generics); + for bound in bounds { + this.visit_param_bound(bound); + } + }) + }); } _ => intravisit::walk_ty(self, ty), } @@ -1088,7 +933,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { use self::hir::TraitItemKind::*; match trait_item.kind { Fn(_, _) => { - self.missing_named_lifetime_spots.push((&trait_item.generics).into()); let tcx = self.tcx; self.visit_early_late( Some(tcx.hir().get_parent_item(trait_item.hir_id())), @@ -1096,10 +940,8 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { &trait_item.generics, |this| intravisit::walk_trait_item(this, trait_item), ); - self.missing_named_lifetime_spots.pop(); } Type(bounds, ref ty) => { - self.missing_named_lifetime_spots.push((&trait_item.generics).into()); let generics = &trait_item.generics; let mut index = self.next_early_index(); debug!("visit_ty: index = {}", index); @@ -1125,7 +967,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { s: self.scope, opaque_type_parent: true, scope_type: BinderScopeType::Normal, - allow_late_bound: false, where_bound_origin: None, }; self.with(scope, |this| { @@ -1140,14 +981,11 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } }) }); - self.missing_named_lifetime_spots.pop(); } Const(_, _) => { // Only methods and types support generics. assert!(trait_item.generics.params.is_empty()); - self.missing_named_lifetime_spots.push(MissingLifetimeSpot::Static); intravisit::walk_trait_item(self, trait_item); - self.missing_named_lifetime_spots.pop(); } } } @@ -1156,7 +994,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { use self::hir::ImplItemKind::*; match impl_item.kind { Fn(..) => { - self.missing_named_lifetime_spots.push((&impl_item.generics).into()); let tcx = self.tcx; self.visit_early_late( Some(tcx.hir().get_parent_item(impl_item.hir_id())), @@ -1164,11 +1001,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { &impl_item.generics, |this| intravisit::walk_impl_item(this, impl_item), ); - self.missing_named_lifetime_spots.pop(); } TyAlias(ref ty) => { let generics = &impl_item.generics; - self.missing_named_lifetime_spots.push(generics.into()); let mut index = self.next_early_index(); let mut non_lifetime_count = 0; debug!("visit_ty: index = {}", index); @@ -1193,7 +1028,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { s: self.scope, opaque_type_parent: true, scope_type: BinderScopeType::Normal, - allow_late_bound: true, where_bound_origin: None, }; self.with(scope, |this| { @@ -1203,14 +1037,11 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { this.visit_ty(ty); }) }); - self.missing_named_lifetime_spots.pop(); } Const(_, _) => { // Only methods and types support generics. assert!(impl_item.generics.params.is_empty()); - self.missing_named_lifetime_spots.push(MissingLifetimeSpot::Static); intravisit::walk_impl_item(self, impl_item); - self.missing_named_lifetime_spots.pop(); } } } @@ -1218,15 +1049,14 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { #[tracing::instrument(level = "debug", skip(self))] fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) { match lifetime_ref.name { - hir::LifetimeName::ImplicitObjectLifetimeDefault - | hir::LifetimeName::Implicit - | hir::LifetimeName::Underscore => self.resolve_elided_lifetimes(&[lifetime_ref]), hir::LifetimeName::Static => self.insert_lifetime(lifetime_ref, Region::Static), hir::LifetimeName::Param(param_def_id, _) => { self.resolve_lifetime_ref(param_def_id, lifetime_ref) } // If we've already reported an error, just ignore `lifetime_ref`. hir::LifetimeName::Error => {} + // Those will be resolved by typechecking. + hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Infer => {} } } @@ -1239,12 +1069,21 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } } - fn visit_fn_decl(&mut self, fd: &'tcx hir::FnDecl<'tcx>) { + fn visit_fn( + &mut self, + fk: intravisit::FnKind<'tcx>, + fd: &'tcx hir::FnDecl<'tcx>, + body_id: hir::BodyId, + _: Span, + _: hir::HirId, + ) { let output = match fd.output { hir::FnRetTy::DefaultReturn(_) => None, hir::FnRetTy::Return(ref ty) => Some(&**ty), }; - self.visit_fn_like_elision(&fd.inputs, output); + self.visit_fn_like_elision(&fd.inputs, output, matches!(fk, intravisit::FnKind::Closure)); + intravisit::walk_fn_kind(self, fk); + self.visit_nested_body(body_id) } fn visit_generics(&mut self, generics: &'tcx hir::Generics<'tcx>) { @@ -1302,7 +1141,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { next_early_index, opaque_type_parent: false, scope_type: BinderScopeType::Normal, - allow_late_bound: true, where_bound_origin: Some(origin), }; this.with(scope, |this| { @@ -1375,7 +1213,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { next_early_index: self.next_early_index(), opaque_type_parent: false, scope_type, - allow_late_bound: true, where_bound_origin: None, }; self.with(scope, |this| { @@ -1393,8 +1230,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { ) { debug!("visit_poly_trait_ref(trait_ref={:?})", trait_ref); - let should_pop_missing_lt = self.is_trait_ref_fn_scope(trait_ref); - let next_early_index = self.next_early_index(); let (mut binders, scope_type) = self.poly_trait_ref_binder_info(); @@ -1428,17 +1263,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { next_early_index, opaque_type_parent: false, scope_type, - allow_late_bound: true, where_bound_origin: None, }; self.with(scope, |this| { walk_list!(this, visit_generic_param, trait_ref.bound_generic_params); this.visit_trait_ref(&trait_ref.trait_ref); }); - - if should_pop_missing_lt { - self.missing_named_lifetime_spots.pop(); - } } } @@ -1584,14 +1414,12 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { { let LifetimeContext { tcx, map, .. } = self; let xcrate_object_lifetime_defaults = take(&mut self.xcrate_object_lifetime_defaults); - let missing_named_lifetime_spots = take(&mut self.missing_named_lifetime_spots); let mut this = LifetimeContext { tcx: *tcx, map, scope: &wrap_scope, trait_definition_only: self.trait_definition_only, xcrate_object_lifetime_defaults, - missing_named_lifetime_spots, }; let span = tracing::debug_span!("scope", scope = ?TruncatedScopeDebug(&this.scope)); { @@ -1599,7 +1427,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { f(&mut this); } self.xcrate_object_lifetime_defaults = this.xcrate_object_lifetime_defaults; - self.missing_named_lifetime_spots = this.missing_named_lifetime_spots; } /// Visits self by adding a scope and handling recursive walk over the contents with `walk`. @@ -1689,7 +1516,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { s: self.scope, opaque_type_parent: true, scope_type: BinderScopeType::Normal, - allow_late_bound: true, where_bound_origin: None, }; self.with(scope, walk); @@ -1865,30 +1691,18 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { ); if generic_args.parenthesized { - self.visit_fn_like_elision(generic_args.inputs(), Some(generic_args.bindings[0].ty())); + self.visit_fn_like_elision( + generic_args.inputs(), + Some(generic_args.bindings[0].ty()), + false, + ); return; } - let mut elide_lifetimes = true; - let lifetimes: Vec<_> = generic_args - .args - .iter() - .filter_map(|arg| match arg { - hir::GenericArg::Lifetime(lt) => { - if !lt.is_elided() { - elide_lifetimes = false; - } - Some(lt) - } - _ => None, - }) - .collect(); - // We short-circuit here if all are elided in order to pluralize - // possible errors - if elide_lifetimes { - self.resolve_elided_lifetimes(&lifetimes); - } else { - lifetimes.iter().for_each(|lt| self.visit_lifetime(lt)); + for arg in generic_args.args { + if let hir::GenericArg::Lifetime(lt) = arg { + self.visit_lifetime(lt); + } } // Figure out if this is a type/trait segment, @@ -2144,436 +1958,19 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { &mut self, inputs: &'tcx [hir::Ty<'tcx>], output: Option<&'tcx hir::Ty<'tcx>>, + in_closure: bool, ) { - debug!("visit_fn_like_elision: enter"); - let mut scope = &*self.scope; - let hir_id = loop { - match scope { - Scope::Binder { hir_id, allow_late_bound: true, .. } => { - break *hir_id; - } - Scope::ObjectLifetimeDefault { ref s, .. } - | Scope::Elision { ref s, .. } - | Scope::Supertrait { ref s, .. } - | Scope::TraitRefBoundary { ref s, .. } => { - scope = *s; - } - Scope::Root - | Scope::Body { .. } - | Scope::Binder { allow_late_bound: false, .. } => { - // See issues #83907 and #83693. Just bail out from looking inside. - // See the issue #95023 for not allowing late bound - self.tcx.sess.delay_span_bug( - rustc_span::DUMMY_SP, - "In fn_like_elision without appropriate scope above", - ); - return; - } - } - }; - // While not strictly necessary, we gather anon lifetimes *before* actually - // visiting the argument types. - let mut gather = GatherAnonLifetimes { anon_count: 0 }; - for input in inputs { - gather.visit_ty(input); - } - trace!(?gather.anon_count); - let late_bound_vars = self.map.late_bound_vars.entry(hir_id).or_default(); - let named_late_bound_vars = late_bound_vars.len() as u32; - late_bound_vars.extend( - (0..gather.anon_count).map(|var| ty::BoundVariableKind::Region(ty::BrAnon(var))), - ); - let arg_scope = Scope::Elision { - elide: Elide::FreshLateAnon(named_late_bound_vars, Cell::new(0)), - s: self.scope, - }; - self.with(arg_scope, |this| { + self.with(Scope::Elision { s: self.scope }, |this| { for input in inputs { this.visit_ty(input); } - }); - - let Some(output) = output else { return }; - - debug!("determine output"); - - // Figure out if there's a body we can get argument names from, - // and whether there's a `self` argument (treated specially). - let mut assoc_item_kind = None; - let mut impl_self = None; - let parent = self.tcx.hir().get_parent_node(output.hir_id); - let body = match self.tcx.hir().get(parent) { - // `fn` definitions and methods. - Node::Item(&hir::Item { kind: hir::ItemKind::Fn(.., body), .. }) => Some(body), - - Node::TraitItem(&hir::TraitItem { kind: hir::TraitItemKind::Fn(_, ref m), .. }) => { - if let hir::ItemKind::Trait(.., ref trait_items) = - self.tcx.hir().expect_item(self.tcx.hir().get_parent_item(parent)).kind - { - assoc_item_kind = - trait_items.iter().find(|ti| ti.id.hir_id() == parent).map(|ti| ti.kind); - } - match *m { - hir::TraitFn::Required(_) => None, - hir::TraitFn::Provided(body) => Some(body), - } - } - - Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, body), .. }) => { - if let hir::ItemKind::Impl(hir::Impl { ref self_ty, ref items, .. }) = - self.tcx.hir().expect_item(self.tcx.hir().get_parent_item(parent)).kind - { - impl_self = Some(self_ty); - assoc_item_kind = - items.iter().find(|ii| ii.id.hir_id() == parent).map(|ii| ii.kind); - } - Some(body) - } - - // Foreign functions, `fn(...) -> R` and `Trait(...) -> R` (both types and bounds). - Node::ForeignItem(_) | Node::Ty(_) | Node::TraitRef(_) => None, - - Node::TypeBinding(_) if let Node::TraitRef(_) = self.tcx.hir().get(self.tcx.hir().get_parent_node(parent)) => None, - - // Everything else (only closures?) doesn't - // actually enjoy elision in return types. - _ => { - self.visit_ty(output); - return; + if !in_closure && let Some(output) = output { + this.visit_ty(output); } - }; - - let has_self = match assoc_item_kind { - Some(hir::AssocItemKind::Fn { has_self }) => has_self, - _ => false, - }; - - // In accordance with the rules for lifetime elision, we can determine - // what region to use for elision in the output type in two ways. - // First (determined here), if `self` is by-reference, then the - // implied output region is the region of the self parameter. - if has_self { - struct SelfVisitor<'a> { - map: &'a NamedRegionMap, - impl_self: Option<&'a hir::TyKind<'a>>, - lifetime: Set1<Region>, - } - - impl SelfVisitor<'_> { - // Look for `self: &'a Self` - also desugared from `&'a self`, - // and if that matches, use it for elision and return early. - fn is_self_ty(&self, res: Res) -> bool { - if let Res::SelfTy { .. } = res { - return true; - } - - // Can't always rely on literal (or implied) `Self` due - // to the way elision rules were originally specified. - if let Some(&hir::TyKind::Path(hir::QPath::Resolved(None, ref path))) = - self.impl_self - { - match path.res { - // Permit the types that unambiguously always - // result in the same type constructor being used - // (it can't differ between `Self` and `self`). - Res::Def(DefKind::Struct | DefKind::Union | DefKind::Enum, _) - | Res::PrimTy(_) => return res == path.res, - _ => {} - } - } - - false - } - } - - impl<'a> Visitor<'a> for SelfVisitor<'a> { - fn visit_ty(&mut self, ty: &'a hir::Ty<'a>) { - if let hir::TyKind::Rptr(lifetime_ref, ref mt) = ty.kind { - if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = mt.ty.kind - { - if self.is_self_ty(path.res) { - if let Some(lifetime) = self.map.defs.get(&lifetime_ref.hir_id) { - self.lifetime.insert(*lifetime); - } - } - } - } - intravisit::walk_ty(self, ty) - } - } - - let mut visitor = SelfVisitor { - map: self.map, - impl_self: impl_self.map(|ty| &ty.kind), - lifetime: Set1::Empty, - }; - visitor.visit_ty(&inputs[0]); - if let Set1::One(lifetime) = visitor.lifetime { - let scope = Scope::Elision { elide: Elide::Exact(lifetime), s: self.scope }; - self.with(scope, |this| this.visit_ty(output)); - return; - } - } - - // Second, if there was exactly one lifetime (either a substitution or a - // reference) in the arguments, then any anonymous regions in the output - // have that lifetime. - let mut possible_implied_output_region = None; - let mut lifetime_count = 0; - let arg_lifetimes = inputs - .iter() - .enumerate() - .skip(has_self as usize) - .map(|(i, input)| { - let mut gather = GatherLifetimes { - map: self.map, - outer_index: ty::INNERMOST, - have_bound_regions: false, - lifetimes: Default::default(), - }; - gather.visit_ty(input); - - lifetime_count += gather.lifetimes.len(); - - if lifetime_count == 1 && gather.lifetimes.len() == 1 { - // there's a chance that the unique lifetime of this - // iteration will be the appropriate lifetime for output - // parameters, so lets store it. - possible_implied_output_region = gather.lifetimes.iter().cloned().next(); - } - - ElisionFailureInfo { - parent: body, - index: i, - lifetime_count: gather.lifetimes.len(), - have_bound_regions: gather.have_bound_regions, - span: input.span, - } - }) - .collect(); - - let elide = if lifetime_count == 1 { - Elide::Exact(possible_implied_output_region.unwrap()) - } else { - Elide::Error(arg_lifetimes) - }; - - debug!(?elide); - - let scope = Scope::Elision { elide, s: self.scope }; - self.with(scope, |this| this.visit_ty(output)); - - struct GatherLifetimes<'a> { - map: &'a NamedRegionMap, - outer_index: ty::DebruijnIndex, - have_bound_regions: bool, - lifetimes: FxHashSet<Region>, - } - - impl<'v, 'a> Visitor<'v> for GatherLifetimes<'a> { - fn visit_ty(&mut self, ty: &hir::Ty<'_>) { - if let hir::TyKind::BareFn(_) = ty.kind { - self.outer_index.shift_in(1); - } - match ty.kind { - hir::TyKind::TraitObject(bounds, ref lifetime, _) => { - for bound in bounds { - self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None); - } - - // Stay on the safe side and don't include the object - // lifetime default (which may not end up being used). - if !lifetime.is_elided() { - self.visit_lifetime(lifetime); - } - } - _ => { - intravisit::walk_ty(self, ty); - } - } - if let hir::TyKind::BareFn(_) = ty.kind { - self.outer_index.shift_out(1); - } - } - - fn visit_generic_param(&mut self, param: &hir::GenericParam<'_>) { - if let hir::GenericParamKind::Lifetime { .. } = param.kind { - // FIXME(eddyb) Do we want this? It only makes a difference - // if this `for<'a>` lifetime parameter is never used. - self.have_bound_regions = true; - } - - intravisit::walk_generic_param(self, param); - } - - fn visit_poly_trait_ref( - &mut self, - trait_ref: &hir::PolyTraitRef<'_>, - modifier: hir::TraitBoundModifier, - ) { - self.outer_index.shift_in(1); - intravisit::walk_poly_trait_ref(self, trait_ref, modifier); - self.outer_index.shift_out(1); - } - - fn visit_param_bound(&mut self, bound: &hir::GenericBound<'_>) { - if let hir::GenericBound::LangItemTrait { .. } = bound { - self.outer_index.shift_in(1); - intravisit::walk_param_bound(self, bound); - self.outer_index.shift_out(1); - } else { - intravisit::walk_param_bound(self, bound); - } - } - - fn visit_lifetime(&mut self, lifetime_ref: &hir::Lifetime) { - if let Some(&lifetime) = self.map.defs.get(&lifetime_ref.hir_id) { - match lifetime { - Region::LateBound(debruijn, _, _) - | Region::LateBoundAnon(debruijn, _, _) - if debruijn < self.outer_index => - { - self.have_bound_regions = true; - } - _ => { - // FIXME(jackh726): nested trait refs? - self.lifetimes.insert(lifetime.shifted_out_to_binder(self.outer_index)); - } - } - } - } - } - - struct GatherAnonLifetimes { - anon_count: u32, - } - impl<'v> Visitor<'v> for GatherAnonLifetimes { - #[instrument(skip(self), level = "trace")] - fn visit_ty(&mut self, ty: &hir::Ty<'_>) { - // If we enter a `BareFn`, then we enter a *new* binding scope - if let hir::TyKind::BareFn(_) = ty.kind { - return; - } - intravisit::walk_ty(self, ty); - } - - fn visit_generic_args( - &mut self, - path_span: Span, - generic_args: &'v hir::GenericArgs<'v>, - ) { - // parenthesized args enter a new elision scope - if generic_args.parenthesized { - return; - } - intravisit::walk_generic_args(self, path_span, generic_args) - } - - #[instrument(skip(self), level = "trace")] - fn visit_lifetime(&mut self, lifetime_ref: &hir::Lifetime) { - if lifetime_ref.is_elided() { - self.anon_count += 1; - } - } - } - } - - fn resolve_elided_lifetimes(&mut self, lifetime_refs: &[&'tcx hir::Lifetime]) { - debug!("resolve_elided_lifetimes(lifetime_refs={:?})", lifetime_refs); - - if lifetime_refs.is_empty() { - return; + }); + if in_closure && let Some(output) = output { + self.visit_ty(output); } - - let mut late_depth = 0; - let mut scope = self.scope; - let mut in_scope_lifetimes = FxIndexSet::default(); - let error = loop { - match *scope { - // Do not assign any resolution, it will be inferred. - Scope::Body { .. } => return, - - Scope::Root => break None, - - Scope::Binder { s, ref lifetimes, scope_type, .. } => { - // collect named lifetimes for suggestions - in_scope_lifetimes.extend(lifetimes.keys().copied()); - match scope_type { - BinderScopeType::Normal => late_depth += 1, - BinderScopeType::Concatenating => {} - } - scope = s; - } - - Scope::Elision { - elide: Elide::FreshLateAnon(named_late_bound_vars, ref counter), - .. - } => { - for lifetime_ref in lifetime_refs { - let lifetime = - Region::late_anon(named_late_bound_vars, counter).shifted(late_depth); - - self.insert_lifetime(lifetime_ref, lifetime); - } - return; - } - - Scope::Elision { elide: Elide::Exact(l), .. } => { - let lifetime = l.shifted(late_depth); - for lifetime_ref in lifetime_refs { - self.insert_lifetime(lifetime_ref, lifetime); - } - return; - } - - Scope::Elision { elide: Elide::Error(ref e), ref s, .. } => { - let mut scope = s; - loop { - match scope { - Scope::Binder { ref lifetimes, s, .. } => { - // Collect named lifetimes for suggestions. - in_scope_lifetimes.extend(lifetimes.keys().copied()); - scope = s; - } - Scope::ObjectLifetimeDefault { ref s, .. } - | Scope::Elision { ref s, .. } - | Scope::TraitRefBoundary { ref s, .. } => { - scope = s; - } - _ => break, - } - } - break Some(&e[..]); - } - - Scope::Elision { elide: Elide::Forbid, .. } => break None, - - Scope::ObjectLifetimeDefault { s, .. } - | Scope::Supertrait { s, .. } - | Scope::TraitRefBoundary { s, .. } => { - scope = s; - } - } - }; - - let mut spans: Vec<_> = lifetime_refs.iter().map(|lt| lt.span).collect(); - spans.sort(); - let mut spans_dedup = spans.clone(); - spans_dedup.dedup(); - let spans_with_counts: Vec<_> = spans_dedup - .into_iter() - .map(|sp| (sp, spans.iter().filter(|nsp| *nsp == &sp).count())) - .collect(); - - let mut err = self.report_missing_lifetime_specifiers(spans.clone(), lifetime_refs.len()); - - self.add_missing_lifetime_specifiers_label( - &mut err, - spans_with_counts, - in_scope_lifetimes, - error, - ); - err.emit(); } fn resolve_object_lifetime_default(&mut self, lifetime_ref: &'tcx hir::Lifetime) { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 2fcbe1d4c14..31d10008efb 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -11,6 +11,7 @@ #![feature(drain_filter)] #![feature(if_let_guard)] #![cfg_attr(bootstrap, feature(let_chains))] +#![feature(iter_intersperse)] #![feature(let_else)] #![feature(never_type)] #![recursion_limit = "256"] @@ -436,7 +437,7 @@ enum ModuleKind { /// f(); // Resolves to (1) /// } /// ``` - Block(NodeId), + Block, /// Any module with a name. /// /// This could be: @@ -453,7 +454,7 @@ impl ModuleKind { /// Get name of the module. pub fn name(&self) -> Option<Symbol> { match self { - ModuleKind::Block(..) => None, + ModuleKind::Block => None, ModuleKind::Def(.., name) => Some(*name), } } @@ -529,7 +530,7 @@ impl<'a> ModuleData<'a> { ) -> Self { let is_foreign = match kind { ModuleKind::Def(_, def_id, _) => !def_id.is_local(), - ModuleKind::Block(_) => false, + ModuleKind::Block => false, }; ModuleData { parent, diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 54dd15270a1..070fb9c721b 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -796,16 +796,23 @@ impl<'a> Resolver<'a> { ) { let span = path.span; if let Some(stability) = &ext.stability { - if let StabilityLevel::Unstable { reason, issue, is_soft } = stability.level { + if let StabilityLevel::Unstable { reason, issue, is_soft, implied_by } = stability.level + { let feature = stability.feature; - if !self.active_features.contains(&feature) && !span.allows_unstable(feature) { + + let is_allowed = |feature| { + self.active_features.contains(&feature) || span.allows_unstable(feature) + }; + let allowed_by_implication = + implied_by.map(|feature| is_allowed(feature)).unwrap_or(false); + if !is_allowed(feature) && !allowed_by_implication { let lint_buffer = &mut self.lint_buffer; let soft_handler = |lint, span, msg: &_| lint_buffer.buffer_lint(lint, node_id, span, msg); stability::report_unstable( self.session, feature, - reason, + reason.to_opt_reason(), issue, None, is_soft, diff --git a/compiler/rustc_serialize/Cargo.toml b/compiler/rustc_serialize/Cargo.toml index 5056163b7fe..dbc5c15195c 100644 --- a/compiler/rustc_serialize/Cargo.toml +++ b/compiler/rustc_serialize/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" edition = "2021" [dependencies] -indexmap = "1.8.0" +indexmap = "1.9.1" smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } [dev-dependencies] diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 55307b9cebb..70f942a6508 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -948,6 +948,8 @@ fn default_configuration(sess: &Session) -> CrateConfig { if sess.opts.debug_assertions { ret.insert((sym::debug_assertions, None)); } + // JUSTIFICATION: before wrapper fn is available + #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] if sess.opts.crate_types.contains(&CrateType::ProcMacro) { ret.insert((sym::proc_macro, None)); } @@ -1942,9 +1944,22 @@ fn parse_native_lib_kind( "static" => NativeLibKind::Static { bundle: None, whole_archive: None }, "dylib" => NativeLibKind::Dylib { as_needed: None }, "framework" => NativeLibKind::Framework { as_needed: None }, + "link-arg" => { + 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!("library kind `link-arg` is unstable{why}")) + } + NativeLibKind::LinkArg + } _ => early_error( error_format, - &format!("unknown library kind `{kind}`, expected one of: static, dylib, framework"), + &format!( + "unknown library kind `{kind}`, expected one of: static, dylib, framework, link-arg" + ), ), }; match modifiers { @@ -2043,7 +2058,7 @@ fn parse_libs(matches: &getopts::Matches, error_format: ErrorOutputType) -> Vec< .into_iter() .map(|s| { // Parse string of the form "[KIND[:MODIFIERS]=]lib[:new_name]", - // where KIND is one of "dylib", "framework", "static" and + // where KIND is one of "dylib", "framework", "static", "link-arg" and // where MODIFIERS are a comma separated list of supported modifiers // (bundle, verbatim, whole-archive, as-needed). Each modifier is prefixed // with either + or - to indicate whether it is enabled or disabled. @@ -2196,6 +2211,8 @@ fn parse_remap_path_prefix( mapping } +// JUSTIFICATION: before wrapper fn is available +#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] pub fn build_session_options(matches: &getopts::Matches) -> Options { let color = parse_color(matches); @@ -2955,3 +2972,13 @@ impl OomStrategy { } } } + +/// How to run proc-macro code when building this crate +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum ProcMacroExecutionStrategy { + /// Run the proc-macro code on the same thread as the server. + SameThread, + + /// Run the proc-macro code on a different thread. + CrossThread, +} diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 01ff9e254f7..6495339eaf1 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -102,28 +102,6 @@ macro_rules! top_level_options { ); } -impl Options { - pub fn mir_opt_level(&self) -> usize { - self.unstable_opts - .mir_opt_level - .unwrap_or_else(|| if self.optimize != OptLevel::No { 2 } else { 1 }) - } - - pub fn instrument_coverage(&self) -> bool { - self.cg.instrument_coverage.unwrap_or(InstrumentCoverage::Off) != InstrumentCoverage::Off - } - - pub fn instrument_coverage_except_unused_generics(&self) -> bool { - self.cg.instrument_coverage.unwrap_or(InstrumentCoverage::Off) - == InstrumentCoverage::ExceptUnusedGenerics - } - - pub fn instrument_coverage_except_unused_functions(&self) -> bool { - self.cg.instrument_coverage.unwrap_or(InstrumentCoverage::Off) - == InstrumentCoverage::ExceptUnusedFunctions - } -} - top_level_options!( /// The top-level command-line options struct. /// @@ -149,9 +127,11 @@ top_level_options!( /// `CodegenOptions`, think about how it influences incremental compilation. If in /// doubt, specify `[TRACKED]`, which is always "correct" but might lead to /// unnecessary re-compilation. + #[cfg_attr(not(bootstrap), rustc_lint_opt_ty)] pub struct Options { /// The crate config requested for the session, which may be combined /// with additional crate configurations during the compile process. + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::crate_types` instead of this field"))] crate_types: Vec<CrateType> [TRACKED], optimize: OptLevel [TRACKED], /// Include the `debug_assertions` flag in dependency tracking, since it @@ -198,7 +178,9 @@ top_level_options!( /// what rustc was invoked with, but massaged a bit to agree with /// commands like `--emit llvm-ir` which they're often incompatible with /// if we otherwise use the defaults of rustc. + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::codegen_units` instead of this field"))] cli_forced_codegen_units: Option<usize> [UNTRACKED], + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field"))] cli_forced_thinlto_off: bool [UNTRACKED], /// Remap source path prefixes in all output (messages, object files, debug, etc.). @@ -249,11 +231,12 @@ macro_rules! options { ),* ,) => ( #[derive(Clone)] - pub struct $struct_name { $(pub $opt: $t),* } + #[cfg_attr(not(bootstrap), rustc_lint_opt_ty)] + pub struct $struct_name { $( $( #[$attr] )* pub $opt: $t),* } impl Default for $struct_name { fn default() -> $struct_name { - $struct_name { $( $( #[$attr] )* $opt: $init),* } + $struct_name { $($opt: $init),* } } } @@ -297,6 +280,22 @@ macro_rules! options { ) } +impl Options { + // JUSTIFICATION: defn of the suggested wrapper fn + #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] + pub fn time_passes(&self) -> bool { + self.unstable_opts.time_passes || self.unstable_opts.time + } +} + +impl CodegenOptions { + // JUSTIFICATION: defn of the suggested wrapper fn + #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] + pub fn instrument_coverage(&self) -> InstrumentCoverage { + self.instrument_coverage.unwrap_or(InstrumentCoverage::Off) + } +} + // Sometimes different options need to build a common structure. // That structure can be kept in one of the options' fields, the others become dummy. macro_rules! redirect_field { @@ -377,7 +376,7 @@ mod desc { pub const parse_opt_panic_strategy: &str = parse_panic_strategy; pub const parse_oom_strategy: &str = "either `panic` or `abort`"; pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`"; - pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `hwaddress`, `leak`, `memory`, `memtag`, or `thread`"; + pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `hwaddress`, `leak`, `memory`, `memtag`, `shadow-call-stack`, or `thread`"; pub const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2"; pub const parse_cfguard: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`"; @@ -416,6 +415,8 @@ mod desc { "one of (`none` (default), `basic`, `strong`, or `all`)"; pub const parse_branch_protection: &str = "a `,` separated combination of `bti`, `b-key`, `pac-ret`, or `leaf`"; + pub const parse_proc_macro_execution_strategy: &str = + "one of supported execution strategies (`same-thread`, or `cross-thread`)"; } mod parse { @@ -535,7 +536,7 @@ mod parse { ) -> bool { match v { Some(s) => { - for s in s.split(",") { + for s in s.split(',') { let Some(pass_name) = s.strip_prefix(&['+', '-'][..]) else { return false }; slot.push((pass_name.to_string(), &s[..1] == "+")); } @@ -683,6 +684,7 @@ mod parse { "leak" => SanitizerSet::LEAK, "memory" => SanitizerSet::MEMORY, "memtag" => SanitizerSet::MEMTAG, + "shadow-call-stack" => SanitizerSet::SHADOWCALLSTACK, "thread" => SanitizerSet::THREAD, "hwaddress" => SanitizerSet::HWADDRESS, _ => return false, @@ -1062,6 +1064,18 @@ mod parse { } true } + + pub(crate) fn parse_proc_macro_execution_strategy( + slot: &mut ProcMacroExecutionStrategy, + v: Option<&str>, + ) -> bool { + *slot = match v { + Some("same-thread") => ProcMacroExecutionStrategy::SameThread, + Some("cross-thread") => ProcMacroExecutionStrategy::CrossThread, + _ => return false, + }; + true + } } options! { @@ -1075,6 +1089,7 @@ options! { ar: String = (String::new(), parse_string, [UNTRACKED], "this option is deprecated and does nothing"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::code_model` instead of this field"))] code_model: Option<CodeModel> = (None, parse_code_model, [TRACKED], "choose the code model to use (`rustc --print code-models` for details)"), codegen_units: Option<usize> = (None, parse_opt_number, [UNTRACKED], @@ -1094,12 +1109,14 @@ options! { "extra data to put in each output filename"), force_frame_pointers: Option<bool> = (None, parse_opt_bool, [TRACKED], "force use of the frame pointers"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::must_emit_unwind_tables` instead of this field"))] force_unwind_tables: Option<bool> = (None, parse_opt_bool, [TRACKED], "force use of unwind tables"), incremental: Option<String> = (None, parse_opt_string, [UNTRACKED], "enable incremental compilation"), inline_threshold: Option<u32> = (None, parse_opt_number, [TRACKED], "set the threshold for inlining a function"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::instrument_coverage` instead of this field"))] instrument_coverage: Option<InstrumentCoverage> = (None, parse_instrument_coverage, [TRACKED], "instrument the generated code to support LLVM source-based code coverage \ reports (note, the compiler build config must include `profiler = true`); \ @@ -1112,6 +1129,7 @@ options! { "a single extra argument to append to the linker invocation (can be used several times)"), link_args: Vec<String> = (Vec::new(), parse_list, [UNTRACKED], "extra arguments to append to the linker invocation (space separated)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::link_dead_code` instead of this field"))] link_dead_code: Option<bool> = (None, parse_opt_bool, [TRACKED], "keep dead code at link time (useful for code coverage) (default: no)"), link_self_contained: Option<bool> = (None, parse_opt_bool, [UNTRACKED], @@ -1126,6 +1144,7 @@ options! { "generate build artifacts that are compatible with linker-based LTO"), llvm_args: Vec<String> = (Vec::new(), parse_list, [TRACKED], "a list of arguments to pass to LLVM (space separated)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field"))] lto: LtoCli = (LtoCli::Unspecified, parse_lto, [TRACKED], "perform LLVM link-time optimizations"), metadata: Vec<String> = (Vec::new(), parse_list, [TRACKED], @@ -1142,8 +1161,10 @@ options! { "disable LLVM's SLP vectorization pass"), opt_level: String = ("0".to_string(), parse_string, [TRACKED], "optimization level (0-3, s, or z; default: 0)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::overflow_checks` instead of this field"))] overflow_checks: Option<bool> = (None, parse_opt_bool, [TRACKED], "use overflow checks for integer arithmetic"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::panic_strategy` instead of this field"))] panic: Option<PanicStrategy> = (None, parse_opt_panic_strategy, [TRACKED], "panic strategy to compile crate with"), passes: Vec<String> = (Vec::new(), parse_list, [TRACKED], @@ -1155,6 +1176,7 @@ options! { "compile the program with profiling instrumentation"), profile_use: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED], "use the given `.profdata` file for profile-guided optimization"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::relocation_model` instead of this field"))] relocation_model: Option<RelocModel> = (None, parse_relocation_model, [TRACKED], "control generation of position-independent code (PIC) \ (`rustc --print relocation-models` for details)"), @@ -1166,6 +1188,7 @@ options! { "save all temporary output files during compilation (default: no)"), soft_float: bool = (false, parse_bool, [TRACKED], "use soft float ABI (*eabihf targets only) (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::split_debuginfo` instead of this field"))] split_debuginfo: Option<SplitDebuginfo> = (None, parse_split_debuginfo, [TRACKED], "how to handle split-debuginfo, a platform-specific option"), strip: Strip = (Strip::None, parse_strip, [UNTRACKED], @@ -1201,14 +1224,18 @@ options! { "encode MIR of all functions into the crate metadata (default: no)"), assume_incomplete_release: bool = (false, parse_bool, [TRACKED], "make cfg(version) treat the current version as incomplete (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::asm_comments` instead of this field"))] asm_comments: bool = (false, parse_bool, [TRACKED], "generate comments into the assembly (may change behavior) (default: no)"), assert_incr_state: Option<String> = (None, parse_opt_string, [UNTRACKED], "assert that the incremental cache is in given state: \ either `loaded` or `not-loaded`."), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::binary_dep_depinfo` instead of this field"))] binary_dep_depinfo: bool = (false, parse_bool, [TRACKED], "include artifacts (sysroot, crate dependencies) used during compilation in dep-info \ (default: no)"), + box_noalias: Option<bool> = (None, parse_opt_bool, [TRACKED], + "emit noalias metadata for box (default: yes)"), branch_protection: Option<BranchProtection> = (None, parse_branch_protection, [TRACKED], "set options for branch target identification and pointer authentication on AArch64"), cf_protection: CFProtection = (CFProtection::None, parse_cfprotection, [TRACKED], @@ -1277,6 +1304,11 @@ options! { "version of DWARF debug information to emit (default: 2 or 4, depending on platform)"), emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED], "emit a section containing stack size metadata (default: no)"), + emit_thin_lto: bool = (true, parse_bool, [TRACKED], + "emit the bc module with thin LTO info (default: yes)"), + export_executable_symbols: bool = (false, parse_bool, [TRACKED], + "export symbols from executables, as if they were dynamic libraries"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::fewer_names` instead of this field"))] fewer_names: Option<bool> = (None, parse_opt_bool, [TRACKED], "reduce memory use by retaining fewer names within compilation artifacts (LLVM-IR) \ (default: no)"), @@ -1319,6 +1351,7 @@ options! { "control whether `#[inline]` functions are in all CGUs"), input_stats: bool = (false, parse_bool, [UNTRACKED], "gather statistics about the input (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::instrument_coverage` instead of this field"))] instrument_coverage: Option<InstrumentCoverage> = (None, parse_instrument_coverage, [TRACKED], "instrument the generated code to support LLVM source-based code coverage \ reports (note, the compiler build config must include `profiler = true`); \ @@ -1327,6 +1360,7 @@ options! { `=except-unused-generics` `=except-unused-functions` `=off` (default)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::instrument_mcount` instead of this field"))] instrument_mcount: bool = (false, parse_bool, [TRACKED], "insert function instrument code for mcount-based tracing (default: no)"), keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED], @@ -1349,6 +1383,7 @@ options! { merge_functions: Option<MergeFunctions> = (None, parse_merge_functions, [TRACKED], "control the operation of the MergeFunctions LLVM pass, taking \ the same values as the target option of the same name"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::meta_stats` instead of this field"))] meta_stats: bool = (false, parse_bool, [UNTRACKED], "gather metadata statistics (default: no)"), mir_emit_retag: bool = (false, parse_bool, [TRACKED], @@ -1358,6 +1393,9 @@ options! { "use like `-Zmir-enable-passes=+DestProp,-InstCombine`. Forces the specified passes to be \ enabled, overriding all other checks. Passes that are not specified are enabled or \ disabled by other flags as usual."), + mir_pretty_relative_line_numbers: bool = (false, parse_bool, [UNTRACKED], + "use line numbers relative to the function in mir pretty printing"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::mir_opt_level` instead of this field"))] mir_opt_level: Option<usize> = (None, parse_opt_number, [TRACKED], "MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds)"), move_size_limit: Option<usize> = (None, parse_opt_number, [TRACKED], @@ -1424,6 +1462,7 @@ options! { See #77382 and #74551."), print_fuel: Option<String> = (None, parse_opt_string, [TRACKED], "make rustc print the total optimization fuel used by a crate"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::print_llvm_passes` instead of this field"))] print_llvm_passes: bool = (false, parse_bool, [UNTRACKED], "print the LLVM optimization passes being run (default: no)"), print_mono_items: Option<String> = (None, parse_opt_string, [UNTRACKED], @@ -1432,6 +1471,9 @@ options! { "print layout information for each type encountered (default: no)"), proc_macro_backtrace: bool = (false, parse_bool, [UNTRACKED], "show backtraces for panics during proc-macro execution (default: no)"), + proc_macro_execution_strategy: ProcMacroExecutionStrategy = (ProcMacroExecutionStrategy::SameThread, + parse_proc_macro_execution_strategy, [UNTRACKED], + "how to run proc-macro code (default: same-thread)"), profile: bool = (false, parse_bool, [TRACKED], "insert profiling code (default: no)"), profile_closures: bool = (false, parse_no_flag, [UNTRACKED], @@ -1498,6 +1540,7 @@ options! { "exclude spans when debug-printing compiler state (default: no)"), src_hash_algorithm: Option<SourceFileHashAlgorithm> = (None, parse_src_file_hash, [TRACKED], "hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::stack_protector` instead of this field"))] stack_protector: StackProtector = (StackProtector::None, parse_stack_protector, [TRACKED], "control stack smash protection strategy (`rustc --print stack-protector-strategies` for details)"), strict_init_checks: bool = (false, parse_bool, [TRACKED], @@ -1518,6 +1561,7 @@ options! { symbol_mangling_version: Option<SymbolManglingVersion> = (None, parse_symbol_mangling_version, [TRACKED], "which mangling version to use for symbol names ('legacy' (default) or 'v0')"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::teach` instead of this field"))] teach: bool = (false, parse_bool, [TRACKED], "show extended diagnostic help (default: no)"), temps_dir: Option<String> = (None, parse_opt_string, [UNTRACKED], @@ -1533,6 +1577,7 @@ options! { "emit directionality isolation markers in translated diagnostics"), tune_cpu: Option<String> = (None, parse_opt_string, [TRACKED], "select processor to schedule for (`rustc --print target-cpus` for details)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field"))] thinlto: Option<bool> = (None, parse_opt_bool, [TRACKED], "enable ThinLTO when possible"), thir_unsafeck: bool = (false, parse_bool, [TRACKED], @@ -1541,14 +1586,19 @@ options! { /// a sequential compiler for now. This'll likely be adjusted /// in the future. Note that -Zthreads=0 is the way to get /// the num_cpus behavior. + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::threads` instead of this field"))] threads: usize = (1, parse_threads, [UNTRACKED], "use a thread pool with N threads"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::time_passes` instead of this field"))] time: bool = (false, parse_bool, [UNTRACKED], "measure time of rustc processes (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::time_llvm_passes` instead of this field"))] time_llvm_passes: bool = (false, parse_bool, [UNTRACKED], "measure time of each LLVM pass (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::time_passes` instead of this field"))] time_passes: bool = (false, parse_bool, [UNTRACKED], "measure time of each rustc pass (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::tls_model` instead of this field"))] tls_model: Option<TlsModel> = (None, parse_tls_model, [TRACKED], "choose the TLS model to use (`rustc --print tls-models` for details)"), trace_macros: bool = (false, parse_bool, [UNTRACKED], @@ -1583,14 +1633,17 @@ options! { "enable unsound and buggy MIR optimizations (default: no)"), /// This name is kind of confusing: Most unstable options enable something themselves, while /// this just allows "normal" options to be feature-gated. + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::unstable_options` instead of this field"))] unstable_options: bool = (false, parse_bool, [UNTRACKED], "adds unstable command line options to rustc interface (default: no)"), use_ctors_section: Option<bool> = (None, parse_opt_bool, [TRACKED], "use legacy .ctors section for initializers rather than .init_array"), validate_mir: bool = (false, parse_bool, [UNTRACKED], "validate MIR after each transformation"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::verbose` instead of this field"))] verbose: bool = (false, parse_bool, [UNTRACKED], "in general, enable more debug printouts (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::verify_llvm_ir` instead of this field"))] verify_llvm_ir: bool = (false, parse_bool, [TRACKED], "verify LLVM IR (default: no)"), virtual_function_elimination: bool = (false, parse_bool, [TRACKED], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 854cad79a20..9669287b3f3 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1,7 +1,7 @@ use crate::cgu_reuse_tracker::CguReuseTracker; use crate::code_stats::CodeStats; pub use crate::code_stats::{DataTypeKind, FieldInfo, SizeKind, VariantInfo}; -use crate::config::{self, CrateType, OutputType, SwitchWithOptPath}; +use crate::config::{self, CrateType, InstrumentCoverage, OptLevel, OutputType, SwitchWithOptPath}; use crate::parse::{add_feature_diagnostics, ParseSess}; use crate::search_paths::{PathKind, SearchPath}; use crate::{filesearch, lint}; @@ -583,35 +583,28 @@ impl Session { pub fn source_map(&self) -> &SourceMap { self.parse_sess.source_map() } - pub fn verbose(&self) -> bool { - self.opts.unstable_opts.verbose - } + pub fn time_passes(&self) -> bool { - self.opts.unstable_opts.time_passes || self.opts.unstable_opts.time + self.opts.time_passes() } - pub fn instrument_mcount(&self) -> bool { - self.opts.unstable_opts.instrument_mcount - } - pub fn time_llvm_passes(&self) -> bool { - self.opts.unstable_opts.time_llvm_passes - } - pub fn meta_stats(&self) -> bool { - self.opts.unstable_opts.meta_stats - } - pub fn asm_comments(&self) -> bool { - self.opts.unstable_opts.asm_comments - } - pub fn verify_llvm_ir(&self) -> bool { - self.opts.unstable_opts.verify_llvm_ir || option_env!("RUSTC_VERIFY_LLVM_IR").is_some() + + /// Returns `true` if internal lints should be added to the lint store - i.e. if + /// `-Zunstable-options` is provided and this isn't rustdoc (internal lints can trigger errors + /// to be emitted under rustdoc). + pub fn enable_internal_lints(&self) -> bool { + self.unstable_options() && !self.opts.actually_rustdoc } - pub fn print_llvm_passes(&self) -> bool { - self.opts.unstable_opts.print_llvm_passes + + pub fn instrument_coverage(&self) -> bool { + self.opts.cg.instrument_coverage() != InstrumentCoverage::Off } - pub fn binary_dep_depinfo(&self) -> bool { - self.opts.unstable_opts.binary_dep_depinfo + + pub fn instrument_coverage_except_unused_generics(&self) -> bool { + self.opts.cg.instrument_coverage() == InstrumentCoverage::ExceptUnusedGenerics } - pub fn mir_opt_level(&self) -> usize { - self.opts.mir_opt_level() + + pub fn instrument_coverage_except_unused_functions(&self) -> bool { + self.opts.cg.instrument_coverage() == InstrumentCoverage::ExceptUnusedFunctions } /// Gets the features enabled for the current compilation session. @@ -629,103 +622,9 @@ impl Session { } } - /// Calculates the flavor of LTO to use for this compilation. - pub fn lto(&self) -> config::Lto { - // If our target has codegen requirements ignore the command line - if self.target.requires_lto { - return config::Lto::Fat; - } - - // If the user specified something, return that. If they only said `-C - // lto` and we've for whatever reason forced off ThinLTO via the CLI, - // then ensure we can't use a ThinLTO. - match self.opts.cg.lto { - config::LtoCli::Unspecified => { - // The compiler was invoked without the `-Clto` flag. Fall - // through to the default handling - } - config::LtoCli::No => { - // The user explicitly opted out of any kind of LTO - return config::Lto::No; - } - config::LtoCli::Yes | config::LtoCli::Fat | config::LtoCli::NoParam => { - // All of these mean fat LTO - return config::Lto::Fat; - } - config::LtoCli::Thin => { - return if self.opts.cli_forced_thinlto_off { - config::Lto::Fat - } else { - config::Lto::Thin - }; - } - } - - // Ok at this point the target doesn't require anything and the user - // hasn't asked for anything. Our next decision is whether or not - // we enable "auto" ThinLTO where we use multiple codegen units and - // then do ThinLTO over those codegen units. The logic below will - // either return `No` or `ThinLocal`. - - // If processing command line options determined that we're incompatible - // with ThinLTO (e.g., `-C lto --emit llvm-ir`) then return that option. - if self.opts.cli_forced_thinlto_off { - return config::Lto::No; - } - - // If `-Z thinlto` specified process that, but note that this is mostly - // a deprecated option now that `-C lto=thin` exists. - if let Some(enabled) = self.opts.unstable_opts.thinlto { - if enabled { - return config::Lto::ThinLocal; - } else { - return config::Lto::No; - } - } - - // If there's only one codegen unit and LTO isn't enabled then there's - // no need for ThinLTO so just return false. - if self.codegen_units() == 1 { - return config::Lto::No; - } - - // Now we're in "defaults" territory. By default we enable ThinLTO for - // optimized compiles (anything greater than O0). - match self.opts.optimize { - config::OptLevel::No => config::Lto::No, - _ => config::Lto::ThinLocal, - } - } - - /// Returns the panic strategy for this compile session. If the user explicitly selected one - /// using '-C panic', use that, otherwise use the panic strategy defined by the target. - pub fn panic_strategy(&self) -> PanicStrategy { - self.opts.cg.panic.unwrap_or(self.target.panic_strategy) - } - pub fn fewer_names(&self) -> bool { - if let Some(fewer_names) = self.opts.unstable_opts.fewer_names { - fewer_names - } else { - let more_names = self.opts.output_types.contains_key(&OutputType::LlvmAssembly) - || self.opts.output_types.contains_key(&OutputType::Bitcode) - // AddressSanitizer and MemorySanitizer use alloca name when reporting an issue. - || self.opts.unstable_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY); - !more_names - } - } - - pub fn unstable_options(&self) -> bool { - self.opts.unstable_opts.unstable_options - } - pub fn is_nightly_build(&self) -> bool { - self.opts.unstable_features.is_nightly_build() - } pub fn is_sanitizer_cfi_enabled(&self) -> bool { self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI) } - pub fn overflow_checks(&self) -> bool { - self.opts.cg.overflow_checks.unwrap_or(self.opts.debug_assertions) - } /// Check whether this compile session and crate type use static crt. pub fn crt_static(&self, crate_type: Option<CrateType>) -> bool { @@ -738,6 +637,8 @@ impl Session { let found_negative = requested_features.clone().any(|r| r == "-crt-static"); let found_positive = requested_features.clone().any(|r| r == "+crt-static"); + // JUSTIFICATION: necessary use of crate_types directly (see FIXME below) + #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] if found_positive || found_negative { found_positive } else if crate_type == Some(CrateType::ProcMacro) @@ -752,18 +653,6 @@ impl Session { } } - pub fn relocation_model(&self) -> RelocModel { - self.opts.cg.relocation_model.unwrap_or(self.target.relocation_model) - } - - pub fn code_model(&self) -> Option<CodeModel> { - self.opts.cg.code_model.or(self.target.code_model) - } - - pub fn tls_model(&self) -> TlsModel { - self.opts.unstable_opts.tls_model.unwrap_or(self.target.tls_model) - } - pub fn is_wasi_reactor(&self) -> bool { self.target.options.os == "wasi" && matches!( @@ -772,49 +661,10 @@ impl Session { ) } - pub fn split_debuginfo(&self) -> SplitDebuginfo { - self.opts.cg.split_debuginfo.unwrap_or(self.target.split_debuginfo) - } - - pub fn stack_protector(&self) -> StackProtector { - if self.target.options.supports_stack_protector { - self.opts.unstable_opts.stack_protector - } else { - StackProtector::None - } - } - pub fn target_can_use_split_dwarf(&self) -> bool { !self.target.is_like_windows && !self.target.is_like_osx } - pub fn must_emit_unwind_tables(&self) -> bool { - // This is used to control the emission of the `uwtable` attribute on - // LLVM functions. - // - // Unwind tables are needed when compiling with `-C panic=unwind`, but - // LLVM won't omit unwind tables unless the function is also marked as - // `nounwind`, so users are allowed to disable `uwtable` emission. - // Historically rustc always emits `uwtable` attributes by default, so - // even they can be disabled, they're still emitted by default. - // - // On some targets (including windows), however, exceptions include - // other events such as illegal instructions, segfaults, etc. This means - // that on Windows we end up still needing unwind tables even if the `-C - // panic=abort` flag is passed. - // - // You can also find more info on why Windows needs unwind tables in: - // https://bugzilla.mozilla.org/show_bug.cgi?id=1302078 - // - // If a target requires unwind tables, then they must be emitted. - // Otherwise, we can defer to the `-C force-unwind-tables=<yes/no>` - // value, if it is provided, or disable them, if not. - self.target.requires_uwtable - || self.opts.cg.force_unwind_tables.unwrap_or( - self.panic_strategy() == PanicStrategy::Unwind || self.target.default_uwtable, - ) - } - pub fn generate_proc_macro_decls_symbol(&self, stable_crate_id: StableCrateId) -> String { format!("__rustc_proc_macro_decls_{:08x}__", stable_crate_id.to_u64()) } @@ -960,6 +810,280 @@ impl Session { ret } + pub fn rust_2015(&self) -> bool { + self.edition() == Edition::Edition2015 + } + + /// Are we allowed to use features from the Rust 2018 edition? + pub fn rust_2018(&self) -> bool { + self.edition() >= Edition::Edition2018 + } + + /// Are we allowed to use features from the Rust 2021 edition? + pub fn rust_2021(&self) -> bool { + self.edition() >= Edition::Edition2021 + } + + /// Are we allowed to use features from the Rust 2024 edition? + pub fn rust_2024(&self) -> bool { + self.edition() >= Edition::Edition2024 + } + + /// Returns `true` if we cannot skip the PLT for shared library calls. + pub fn needs_plt(&self) -> bool { + // Check if the current target usually needs PLT to be enabled. + // The user can use the command line flag to override it. + let needs_plt = self.target.needs_plt; + + let dbg_opts = &self.opts.unstable_opts; + + let relro_level = dbg_opts.relro_level.unwrap_or(self.target.relro_level); + + // Only enable this optimization by default if full relro is also enabled. + // In this case, lazy binding was already unavailable, so nothing is lost. + // This also ensures `-Wl,-z,now` is supported by the linker. + let full_relro = RelroLevel::Full == relro_level; + + // If user didn't explicitly forced us to use / skip the PLT, + // then try to skip it where possible. + dbg_opts.plt.unwrap_or(needs_plt || !full_relro) + } + + /// Checks if LLVM lifetime markers should be emitted. + pub fn emit_lifetime_markers(&self) -> bool { + self.opts.optimize != config::OptLevel::No + // AddressSanitizer uses lifetimes to detect use after scope bugs. + // MemorySanitizer uses lifetimes to detect use of uninitialized stack variables. + // HWAddressSanitizer will use lifetimes to detect use after scope bugs in the future. + || self.opts.unstable_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS) + } + + pub fn is_proc_macro_attr(&self, attr: &Attribute) -> bool { + [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive] + .iter() + .any(|kind| attr.has_name(*kind)) + } + + pub fn contains_name(&self, attrs: &[Attribute], name: Symbol) -> bool { + attrs.iter().any(|item| item.has_name(name)) + } + + pub fn find_by_name<'a>( + &'a self, + attrs: &'a [Attribute], + name: Symbol, + ) -> Option<&'a Attribute> { + attrs.iter().find(|attr| attr.has_name(name)) + } + + pub fn filter_by_name<'a>( + &'a self, + attrs: &'a [Attribute], + name: Symbol, + ) -> impl Iterator<Item = &'a Attribute> { + attrs.iter().filter(move |attr| attr.has_name(name)) + } + + pub fn first_attr_value_str_by_name( + &self, + attrs: &[Attribute], + name: Symbol, + ) -> Option<Symbol> { + attrs.iter().find(|at| at.has_name(name)).and_then(|at| at.value_str()) + } +} + +// JUSTIFICATION: defn of the suggested wrapper fns +#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] +impl Session { + pub fn verbose(&self) -> bool { + self.opts.unstable_opts.verbose + } + + pub fn instrument_mcount(&self) -> bool { + self.opts.unstable_opts.instrument_mcount + } + + pub fn time_llvm_passes(&self) -> bool { + self.opts.unstable_opts.time_llvm_passes + } + + pub fn meta_stats(&self) -> bool { + self.opts.unstable_opts.meta_stats + } + + pub fn asm_comments(&self) -> bool { + self.opts.unstable_opts.asm_comments + } + + pub fn verify_llvm_ir(&self) -> bool { + self.opts.unstable_opts.verify_llvm_ir || option_env!("RUSTC_VERIFY_LLVM_IR").is_some() + } + + pub fn print_llvm_passes(&self) -> bool { + self.opts.unstable_opts.print_llvm_passes + } + + pub fn binary_dep_depinfo(&self) -> bool { + self.opts.unstable_opts.binary_dep_depinfo + } + + pub fn mir_opt_level(&self) -> usize { + self.opts + .unstable_opts + .mir_opt_level + .unwrap_or_else(|| if self.opts.optimize != OptLevel::No { 2 } else { 1 }) + } + + /// Calculates the flavor of LTO to use for this compilation. + pub fn lto(&self) -> config::Lto { + // If our target has codegen requirements ignore the command line + if self.target.requires_lto { + return config::Lto::Fat; + } + + // If the user specified something, return that. If they only said `-C + // lto` and we've for whatever reason forced off ThinLTO via the CLI, + // then ensure we can't use a ThinLTO. + match self.opts.cg.lto { + config::LtoCli::Unspecified => { + // The compiler was invoked without the `-Clto` flag. Fall + // through to the default handling + } + config::LtoCli::No => { + // The user explicitly opted out of any kind of LTO + return config::Lto::No; + } + config::LtoCli::Yes | config::LtoCli::Fat | config::LtoCli::NoParam => { + // All of these mean fat LTO + return config::Lto::Fat; + } + config::LtoCli::Thin => { + return if self.opts.cli_forced_thinlto_off { + config::Lto::Fat + } else { + config::Lto::Thin + }; + } + } + + // Ok at this point the target doesn't require anything and the user + // hasn't asked for anything. Our next decision is whether or not + // we enable "auto" ThinLTO where we use multiple codegen units and + // then do ThinLTO over those codegen units. The logic below will + // either return `No` or `ThinLocal`. + + // If processing command line options determined that we're incompatible + // with ThinLTO (e.g., `-C lto --emit llvm-ir`) then return that option. + if self.opts.cli_forced_thinlto_off { + return config::Lto::No; + } + + // If `-Z thinlto` specified process that, but note that this is mostly + // a deprecated option now that `-C lto=thin` exists. + if let Some(enabled) = self.opts.unstable_opts.thinlto { + if enabled { + return config::Lto::ThinLocal; + } else { + return config::Lto::No; + } + } + + // If there's only one codegen unit and LTO isn't enabled then there's + // no need for ThinLTO so just return false. + if self.codegen_units() == 1 { + return config::Lto::No; + } + + // Now we're in "defaults" territory. By default we enable ThinLTO for + // optimized compiles (anything greater than O0). + match self.opts.optimize { + config::OptLevel::No => config::Lto::No, + _ => config::Lto::ThinLocal, + } + } + + /// Returns the panic strategy for this compile session. If the user explicitly selected one + /// using '-C panic', use that, otherwise use the panic strategy defined by the target. + pub fn panic_strategy(&self) -> PanicStrategy { + self.opts.cg.panic.unwrap_or(self.target.panic_strategy) + } + + pub fn fewer_names(&self) -> bool { + if let Some(fewer_names) = self.opts.unstable_opts.fewer_names { + fewer_names + } else { + let more_names = self.opts.output_types.contains_key(&OutputType::LlvmAssembly) + || self.opts.output_types.contains_key(&OutputType::Bitcode) + // AddressSanitizer and MemorySanitizer use alloca name when reporting an issue. + || self.opts.unstable_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY); + !more_names + } + } + + pub fn unstable_options(&self) -> bool { + self.opts.unstable_opts.unstable_options + } + + pub fn is_nightly_build(&self) -> bool { + self.opts.unstable_features.is_nightly_build() + } + + pub fn overflow_checks(&self) -> bool { + self.opts.cg.overflow_checks.unwrap_or(self.opts.debug_assertions) + } + + pub fn relocation_model(&self) -> RelocModel { + self.opts.cg.relocation_model.unwrap_or(self.target.relocation_model) + } + + pub fn code_model(&self) -> Option<CodeModel> { + self.opts.cg.code_model.or(self.target.code_model) + } + + pub fn tls_model(&self) -> TlsModel { + self.opts.unstable_opts.tls_model.unwrap_or(self.target.tls_model) + } + + pub fn split_debuginfo(&self) -> SplitDebuginfo { + self.opts.cg.split_debuginfo.unwrap_or(self.target.split_debuginfo) + } + + pub fn stack_protector(&self) -> StackProtector { + if self.target.options.supports_stack_protector { + self.opts.unstable_opts.stack_protector + } else { + StackProtector::None + } + } + + pub fn must_emit_unwind_tables(&self) -> bool { + // This is used to control the emission of the `uwtable` attribute on + // LLVM functions. + // + // Unwind tables are needed when compiling with `-C panic=unwind`, but + // LLVM won't omit unwind tables unless the function is also marked as + // `nounwind`, so users are allowed to disable `uwtable` emission. + // Historically rustc always emits `uwtable` attributes by default, so + // even they can be disabled, they're still emitted by default. + // + // On some targets (including windows), however, exceptions include + // other events such as illegal instructions, segfaults, etc. This means + // that on Windows we end up still needing unwind tables even if the `-C + // panic=abort` flag is passed. + // + // You can also find more info on why Windows needs unwind tables in: + // https://bugzilla.mozilla.org/show_bug.cgi?id=1302078 + // + // If a target requires unwind tables, then they must be emitted. + // Otherwise, we can defer to the `-C force-unwind-tables=<yes/no>` + // value, if it is provided, or disable them, if not. + self.target.requires_uwtable + || self.opts.cg.force_unwind_tables.unwrap_or( + self.panic_strategy() == PanicStrategy::Unwind || self.target.default_uwtable, + ) + } + /// Returns the number of query threads that should be used for this /// compilation pub fn threads(&self) -> usize { @@ -1040,109 +1164,17 @@ impl Session { self.opts.unstable_opts.teach && self.diagnostic().must_teach(code) } - pub fn rust_2015(&self) -> bool { - self.opts.edition == Edition::Edition2015 - } - - /// Are we allowed to use features from the Rust 2018 edition? - pub fn rust_2018(&self) -> bool { - self.opts.edition >= Edition::Edition2018 - } - - /// Are we allowed to use features from the Rust 2021 edition? - pub fn rust_2021(&self) -> bool { - self.opts.edition >= Edition::Edition2021 - } - - /// Are we allowed to use features from the Rust 2024 edition? - pub fn rust_2024(&self) -> bool { - self.opts.edition >= Edition::Edition2024 - } - pub fn edition(&self) -> Edition { self.opts.edition } - /// Returns `true` if we cannot skip the PLT for shared library calls. - pub fn needs_plt(&self) -> bool { - // Check if the current target usually needs PLT to be enabled. - // The user can use the command line flag to override it. - let needs_plt = self.target.needs_plt; - - let dbg_opts = &self.opts.unstable_opts; - - let relro_level = dbg_opts.relro_level.unwrap_or(self.target.relro_level); - - // Only enable this optimization by default if full relro is also enabled. - // In this case, lazy binding was already unavailable, so nothing is lost. - // This also ensures `-Wl,-z,now` is supported by the linker. - let full_relro = RelroLevel::Full == relro_level; - - // If user didn't explicitly forced us to use / skip the PLT, - // then try to skip it where possible. - dbg_opts.plt.unwrap_or(needs_plt || !full_relro) - } - - /// Checks if LLVM lifetime markers should be emitted. - pub fn emit_lifetime_markers(&self) -> bool { - self.opts.optimize != config::OptLevel::No - // AddressSanitizer uses lifetimes to detect use after scope bugs. - // MemorySanitizer uses lifetimes to detect use of uninitialized stack variables. - // HWAddressSanitizer will use lifetimes to detect use after scope bugs in the future. - || self.opts.unstable_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS) - } - pub fn link_dead_code(&self) -> bool { self.opts.cg.link_dead_code.unwrap_or(false) } - - pub fn instrument_coverage(&self) -> bool { - self.opts.instrument_coverage() - } - - pub fn instrument_coverage_except_unused_generics(&self) -> bool { - self.opts.instrument_coverage_except_unused_generics() - } - - pub fn instrument_coverage_except_unused_functions(&self) -> bool { - self.opts.instrument_coverage_except_unused_functions() - } - - pub fn is_proc_macro_attr(&self, attr: &Attribute) -> bool { - [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive] - .iter() - .any(|kind| attr.has_name(*kind)) - } - - pub fn contains_name(&self, attrs: &[Attribute], name: Symbol) -> bool { - attrs.iter().any(|item| item.has_name(name)) - } - - pub fn find_by_name<'a>( - &'a self, - attrs: &'a [Attribute], - name: Symbol, - ) -> Option<&'a Attribute> { - attrs.iter().find(|attr| attr.has_name(name)) - } - - pub fn filter_by_name<'a>( - &'a self, - attrs: &'a [Attribute], - name: Symbol, - ) -> impl Iterator<Item = &'a Attribute> { - attrs.iter().filter(move |attr| attr.has_name(name)) - } - - pub fn first_attr_value_str_by_name( - &self, - attrs: &[Attribute], - name: Symbol, - ) -> Option<Symbol> { - attrs.iter().find(|at| at.has_name(name)).and_then(|at| at.value_str()) - } } +// JUSTIFICATION: part of session construction +#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] fn default_emitter( sopts: &config::Options, registry: rustc_errors::registry::Registry, @@ -1227,6 +1259,8 @@ pub enum DiagnosticOutput { Raw(Box<dyn Write + Send>), } +// JUSTIFICATION: literally session construction +#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] pub fn build_session( sopts: config::Options, local_crate_source_file: Option<PathBuf>, @@ -1348,11 +1382,8 @@ pub fn build_session( CguReuseTracker::new_disabled() }; - let prof = SelfProfilerRef::new( - self_profiler, - sopts.unstable_opts.time_passes || sopts.unstable_opts.time, - sopts.unstable_opts.time_passes, - ); + let prof = + SelfProfilerRef::new(self_profiler, sopts.time_passes(), sopts.unstable_opts.time_passes); let ctfe_backtrace = Lock::new(match env::var("RUSTC_CTFE_BACKTRACE") { Ok(ref val) if val == "immediate" => CtfeBacktrace::Immediate, @@ -1401,8 +1432,12 @@ pub fn build_session( sess } -// If it is useful to have a Session available already for validating a -// commandline argument, you can do so here. +/// Validate command line arguments with a `Session`. +/// +/// If it is useful to have a Session available already for validating a commandline argument, you +/// can do so here. +// JUSTIFICATION: needs to access args to validate them +#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] fn validate_commandline_args_with_session_available(sess: &Session) { // Since we don't know if code in an rlib will be linked to statically or // dynamically downstream, rustc generates `__imp_` symbols that help linkers diff --git a/compiler/rustc_session/src/utils.rs b/compiler/rustc_session/src/utils.rs index bda7b314308..9a4f6f9f9ef 100644 --- a/compiler/rustc_session/src/utils.rs +++ b/compiler/rustc_session/src/utils.rs @@ -34,6 +34,9 @@ pub enum NativeLibKind { /// Whether the framework will be linked only if it satisfies some undefined symbols as_needed: Option<bool>, }, + /// Argument which is passed to linker, relative order with libraries and other arguments + /// is preserved + LinkArg, /// The library kind wasn't specified, `Dylib` is currently used as a default. Unspecified, } @@ -47,7 +50,7 @@ impl NativeLibKind { NativeLibKind::Dylib { as_needed } | NativeLibKind::Framework { as_needed } => { as_needed.is_some() } - NativeLibKind::RawDylib | NativeLibKind::Unspecified => false, + NativeLibKind::RawDylib | NativeLibKind::Unspecified | NativeLibKind::LinkArg => false, } } } diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index afbb88e9233..28381157d50 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -463,6 +463,33 @@ impl SourceMap { self.span_to_string(sp, FileNameDisplayPreference::Remapped) } + /// Format the span location suitable for pretty printing anotations with relative line numbers + pub fn span_to_relative_line_string(&self, sp: Span, relative_to: Span) -> String { + if self.files.borrow().source_files.is_empty() || sp.is_dummy() || relative_to.is_dummy() { + return "no-location".to_string(); + } + + let lo = self.lookup_char_pos(sp.lo()); + let hi = self.lookup_char_pos(sp.hi()); + let offset = self.lookup_char_pos(relative_to.lo()); + + if lo.file.name != offset.file.name { + return self.span_to_embeddable_string(sp); + } + + let lo_line = lo.line.saturating_sub(offset.line); + let hi_line = hi.line.saturating_sub(offset.line); + + format!( + "{}:+{}:{}: +{}:{}", + lo.file.name.display(FileNameDisplayPreference::Remapped), + lo_line, + lo.col.to_usize() + 1, + hi_line, + hi.col.to_usize() + 1, + ) + } + /// Format the span location to be printed in diagnostics. Must not be emitted /// to build artifacts as this may leak local file paths. Use span_to_embeddable_string /// for string suitable for embedding. @@ -586,17 +613,6 @@ impl SourceMap { } } - /// Returns whether or not this span points into a file - /// in the current crate. This may be `false` for spans - /// produced by a macro expansion, or for spans associated - /// with the definition of an item in a foreign crate - pub fn is_local_span(&self, sp: Span) -> bool { - let local_begin = self.lookup_byte_offset(sp.lo()); - let local_end = self.lookup_byte_offset(sp.hi()); - // This might be a weird span that covers multiple files - local_begin.sf.src.is_some() && local_end.sf.src.is_some() - } - pub fn is_span_accessible(&self, sp: Span) -> bool { self.span_to_source(sp, |src, start_index, end_index| { Ok(src.get(start_index..end_index).is_some()) @@ -718,6 +734,11 @@ impl SourceMap { sp } + /// Extends the given `Span` to contain the entire line it is on. + pub fn span_extend_to_line(&self, sp: Span) -> Span { + self.span_extend_to_prev_char(self.span_extend_to_next_char(sp, '\n', true), '\n', true) + } + /// Given a `Span`, tries to get a shorter span ending before the first occurrence of `char` /// `c`. pub fn span_until_char(&self, sp: Span, c: char) -> Span { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 3ccd3fcbafa..060e7a7b90a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -696,6 +696,7 @@ symbols! { fabsf32, fabsf64, fadd_fast, + fake_variadic, fdiv_fast, feature, fence, @@ -799,6 +800,7 @@ symbols! { impl_lint_pass, impl_macros, impl_trait_in_bindings, + implied_by, import, import_shadowing, imported_main, @@ -1200,6 +1202,7 @@ symbols! { rustc, rustc_allocator, rustc_allocator_nounwind, + rustc_allocator_zeroed, rustc_allow_const_fn_unstable, rustc_allow_incoherent_impl, rustc_allowed_through_unstable_modules, @@ -1212,6 +1215,7 @@ symbols! { rustc_const_stable, rustc_const_unstable, rustc_conversion_suggestion, + rustc_deallocator, rustc_def_path, rustc_diagnostic_item, rustc_diagnostic_macros, @@ -1234,6 +1238,8 @@ symbols! { rustc_layout_scalar_valid_range_start, rustc_legacy_const_generics, rustc_lint_diagnostics, + rustc_lint_opt_deny_field_access, + rustc_lint_opt_ty, rustc_lint_query_instability, rustc_macro_transparency, rustc_main, @@ -1256,6 +1262,7 @@ symbols! { rustc_private, rustc_proc_macro_decls, rustc_promotable, + rustc_reallocator, rustc_regions, rustc_reservation_impl, rustc_serialize, @@ -1282,6 +1289,7 @@ symbols! { self_in_typedefs, self_struct_ctor, semitransparent, + shadow_call_stack, shl, shl_assign, should_panic, @@ -1471,7 +1479,6 @@ symbols! { tuple, tuple_from_req, tuple_indexing, - tuple_variadic, two_phase, ty, type_alias_enum_variants, @@ -1526,6 +1533,9 @@ symbols! { unsized_locals, unsized_tuple_coercion, unstable, + unstable_location_reason_default: "this crate is being loaded from the sysroot, an \ + unstable location; did you mean to load this crate \ + from crates.io via `Cargo.toml` instead?", untagged_unions, unused_imports, unused_qualifications, @@ -1564,6 +1574,8 @@ symbols! { volatile_store, vreg, vreg_low16, + vtable_align, + vtable_size, warn, wasm_abi, wasm_import_module, diff --git a/compiler/rustc_symbol_mangling/Cargo.toml b/compiler/rustc_symbol_mangling/Cargo.toml index d5befa10e23..b104a40c231 100644 --- a/compiler/rustc_symbol_mangling/Cargo.toml +++ b/compiler/rustc_symbol_mangling/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" doctest = false [dependencies] +bitflags = "1.2.1" tracing = "0.1" punycode = "0.4.0" rustc-demangle = "0.1.21" diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index e3045c9321d..9241fd82c74 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -67,7 +67,7 @@ pub(super) fn mangle<'tcx>( ) .unwrap(); - if let ty::InstanceDef::VtableShim(..) = instance.def { + if let ty::InstanceDef::VTableShim(..) = instance.def { let _ = printer.write_str("{{vtable-shim}}"); } @@ -129,7 +129,7 @@ fn get_symbol_hash<'tcx>( } // We want to avoid accidental collision between different types of instances. - // Especially, `VtableShim`s and `ReifyShim`s may overlap with their original + // Especially, `VTableShim`s and `ReifyShim`s may overlap with their original // instances without this. discriminant(&instance.def).hash_stable(hcx, &mut hasher); }); diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index bed0e81e66e..5fc992023ca 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -102,9 +102,8 @@ 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; -use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; +use rustc_middle::ty::{self, Instance, TyCtxt}; use rustc_session::config::SymbolManglingVersion; -use rustc_target::abi::call::FnAbi; use tracing::debug; @@ -112,6 +111,7 @@ mod legacy; mod v0; pub mod test; +pub mod typeid; /// This function computes the symbol name for the given `instance` and the /// given instantiating crate. That is, if you know that instance X is @@ -150,11 +150,6 @@ fn symbol_name_provider<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> ty ty::SymbolName::new(tcx, &symbol_name) } -/// This function computes the typeid for the given function ABI. -pub fn typeid_for_fnabi<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> String { - v0::mangle_typeid_for_fnabi(tcx, fn_abi) -} - pub fn typeid_for_trait_ref<'tcx>( tcx: TyCtxt<'tcx>, trait_ref: ty::PolyExistentialTraitRef<'tcx>, diff --git a/compiler/rustc_symbol_mangling/src/typeid.rs b/compiler/rustc_symbol_mangling/src/typeid.rs new file mode 100644 index 00000000000..9228bea43f9 --- /dev/null +++ b/compiler/rustc_symbol_mangling/src/typeid.rs @@ -0,0 +1,18 @@ +// For more information about type metadata and type metadata identifiers for cross-language LLVM +// CFI support, see Type metadata in the design document in the tracking issue #89653. + +use rustc_middle::ty::{FnSig, Ty, TyCtxt}; +use rustc_target::abi::call::FnAbi; + +mod typeid_itanium_cxx_abi; +use typeid_itanium_cxx_abi::TypeIdOptions; + +/// Returns a type metadata identifier for the specified FnAbi. +pub fn typeid_for_fnabi<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> String { + typeid_itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, TypeIdOptions::NO_OPTIONS) +} + +/// Returns a type metadata identifier for the specified FnSig. +pub fn typeid_for_fnsig<'tcx>(tcx: TyCtxt<'tcx>, fn_sig: &FnSig<'tcx>) -> String { + typeid_itanium_cxx_abi::typeid_for_fnsig(tcx, fn_sig, TypeIdOptions::NO_OPTIONS) +} diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs new file mode 100644 index 00000000000..a09b52fbfdf --- /dev/null +++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs @@ -0,0 +1,929 @@ +// For more information about type metadata and type metadata identifiers for cross-language LLVM +// CFI support, see Type metadata in the design document in the tracking issue #89653. + +// FIXME(rcvalle): Identify C char and integer type uses and encode them with their respective +// builtin type encodings as specified by the Itanium C++ ABI for extern function types with the "C" +// calling convention to use this encoding for cross-language LLVM CFI. + +use bitflags::bitflags; +use core::fmt::Display; +use rustc_data_structures::base_n; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir as hir; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; +use rustc_middle::ty::{ + self, Binder, Const, ExistentialPredicate, FloatTy, FnSig, IntTy, List, Region, RegionKind, + Term, Ty, TyCtxt, UintTy, +}; +use rustc_span::def_id::DefId; +use rustc_span::symbol::sym; +use rustc_target::abi::call::{Conv, FnAbi}; +use rustc_target::spec::abi::Abi; +use std::fmt::Write as _; + +/// Type and extended type qualifiers. +#[derive(Eq, Hash, PartialEq)] +enum TyQ { + None, + Const, + Mut, +} + +/// Substitution dictionary key. +#[derive(Eq, Hash, PartialEq)] +enum DictKey<'tcx> { + Ty(Ty<'tcx>, TyQ), + Region(Region<'tcx>), + Const(Const<'tcx>), + Predicate(ExistentialPredicate<'tcx>), +} + +bitflags! { + /// Options for typeid_for_fnabi and typeid_for_fnsig. + pub struct TypeIdOptions: u32 { + const NO_OPTIONS = 0; + const GENERALIZE_POINTERS = 1; + const GENERALIZE_REPR_C = 2; + } +} + +/// Options for encode_ty. +type EncodeTyOptions = TypeIdOptions; + +/// Options for transform_ty. +type TransformTyOptions = TypeIdOptions; + +/// Converts a number to a disambiguator (see +/// <https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html>). +fn to_disambiguator(num: u64) -> String { + if let Some(num) = num.checked_sub(1) { + format!("s{}_", base_n::encode(num as u128, 62)) + } else { + "s_".to_string() + } +} + +/// Converts a number to a sequence number (see +/// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.seq-id>). +fn to_seq_id(num: usize) -> String { + if let Some(num) = num.checked_sub(1) { + base_n::encode(num as u128, 36).to_uppercase() + } else { + "".to_string() + } +} + +/// Substitutes a component if found in the substitution dictionary (see +/// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression>). +fn compress<'tcx>( + dict: &mut FxHashMap<DictKey<'tcx>, usize>, + key: DictKey<'tcx>, + comp: &mut String, +) { + match dict.get(&key) { + Some(num) => { + comp.clear(); + let _ = write!(comp, "S{}_", to_seq_id(*num)); + } + None => { + dict.insert(key, dict.len()); + } + } +} + +// FIXME(rcvalle): Move to compiler/rustc_middle/src/ty/sty.rs after C types work is done, possibly +// along with other is_c_type methods. +/// Returns whether a `ty::Ty` is `c_void`. +fn is_c_void_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { + match ty.kind() { + ty::Adt(adt_def, ..) => { + let def_id = adt_def.0.did; + let crate_name = tcx.crate_name(def_id.krate); + if tcx.item_name(def_id).as_str() == "c_void" + && (crate_name == sym::core || crate_name == sym::std || crate_name == sym::libc) + { + true + } else { + false + } + } + _ => false, + } +} + +/// Encodes a const using the Itanium C++ ABI as a literal argument (see +/// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling.literal>). +fn encode_const<'tcx>( + tcx: TyCtxt<'tcx>, + c: Const<'tcx>, + dict: &mut FxHashMap<DictKey<'tcx>, usize>, + options: EncodeTyOptions, +) -> String { + // L<element-type>[n]<element-value>E as literal argument + let mut s = String::from('L'); + + // Element type + s.push_str(&encode_ty(tcx, c.ty(), dict, options)); + + // The only allowed types of const parameters are bool, u8, u16, u32, u64, u128, usize i8, i16, + // i32, i64, i128, isize, and char. The bool value false is encoded as 0 and true as 1. + fn push_signed_value<T: Display + PartialOrd>(s: &mut String, value: T, zero: T) { + if value < zero { + s.push('n') + }; + let _ = write!(s, "{}", value); + } + + fn push_unsigned_value<T: Display>(s: &mut String, value: T) { + let _ = write!(s, "{}", value); + } + + if let Some(scalar_int) = c.kind().try_to_scalar_int() { + let signed = c.ty().is_signed(); + match scalar_int.size().bits() { + 8 if signed => push_signed_value(&mut s, scalar_int.try_to_i8().unwrap(), 0), + 16 if signed => push_signed_value(&mut s, scalar_int.try_to_i16().unwrap(), 0), + 32 if signed => push_signed_value(&mut s, scalar_int.try_to_i32().unwrap(), 0), + 64 if signed => push_signed_value(&mut s, scalar_int.try_to_i64().unwrap(), 0), + 128 if signed => push_signed_value(&mut s, scalar_int.try_to_i128().unwrap(), 0), + 8 => push_unsigned_value(&mut s, scalar_int.try_to_u8().unwrap()), + 16 => push_unsigned_value(&mut s, scalar_int.try_to_u16().unwrap()), + 32 => push_unsigned_value(&mut s, scalar_int.try_to_u32().unwrap()), + 64 => push_unsigned_value(&mut s, scalar_int.try_to_u64().unwrap()), + 128 => push_unsigned_value(&mut s, scalar_int.try_to_u128().unwrap()), + _ => { + bug!("encode_const: unexpected size `{:?}`", scalar_int.size().bits()); + } + }; + } else { + bug!("encode_const: unexpected type `{:?}`", c.ty()); + } + + // Close the "L..E" pair + s.push('E'); + + compress(dict, DictKey::Const(c), &mut s); + + s +} + +/// Encodes a FnSig using the Itanium C++ ABI with vendor extended type qualifiers and types for +/// Rust types that are not used at the FFI boundary. +fn encode_fnsig<'tcx>( + tcx: TyCtxt<'tcx>, + fn_sig: &FnSig<'tcx>, + dict: &mut FxHashMap<DictKey<'tcx>, usize>, + options: TypeIdOptions, +) -> String { + // Function types are delimited by an "F..E" pair + let mut s = String::from("F"); + + let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits()) + .unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits())); + match fn_sig.abi { + Abi::C { .. } => { + encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C); + } + _ => { + encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C); + } + } + + // Encode the return type + let transform_ty_options = TransformTyOptions::from_bits(options.bits()) + .unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits())); + let ty = transform_ty(tcx, fn_sig.output(), transform_ty_options); + s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options)); + + // Encode the parameter types + let tys = fn_sig.inputs(); + if !tys.is_empty() { + for ty in tys { + let ty = transform_ty(tcx, *ty, transform_ty_options); + s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options)); + } + + if fn_sig.c_variadic { + s.push('z'); + } + } else { + if fn_sig.c_variadic { + s.push('z'); + } else { + // Empty parameter lists, whether declared as () or conventionally as (void), are + // encoded with a void parameter specifier "v". + s.push('v') + } + } + + // Close the "F..E" pair + s.push('E'); + + s +} + +/// Encodes a predicate using the Itanium C++ ABI with vendor extended type qualifiers and types for +/// Rust types that are not used at the FFI boundary. +fn encode_predicate<'tcx>( + tcx: TyCtxt<'tcx>, + predicate: Binder<'tcx, ExistentialPredicate<'tcx>>, + dict: &mut FxHashMap<DictKey<'tcx>, usize>, + options: EncodeTyOptions, +) -> String { + // u<length><name>[I<element-type1..element-typeN>E], where <element-type> is <subst>, as vendor + // extended type. + let mut s = String::new(); + match predicate.as_ref().skip_binder() { + ty::ExistentialPredicate::Trait(trait_ref) => { + let name = encode_ty_name(tcx, trait_ref.def_id); + let _ = write!(s, "u{}{}", name.len(), &name); + s.push_str(&encode_substs(tcx, trait_ref.substs, dict, options)); + } + ty::ExistentialPredicate::Projection(projection) => { + let name = encode_ty_name(tcx, projection.item_def_id); + let _ = write!(s, "u{}{}", name.len(), &name); + s.push_str(&encode_substs(tcx, projection.substs, dict, options)); + match projection.term { + Term::Ty(ty) => { + s.push_str(&encode_ty(tcx, ty, dict, options)); + } + Term::Const(c) => { + s.push_str(&encode_const(tcx, c, dict, options)); + } + } + } + ty::ExistentialPredicate::AutoTrait(def_id) => { + let name = encode_ty_name(tcx, *def_id); + let _ = write!(s, "u{}{}", name.len(), &name); + } + }; + compress(dict, DictKey::Predicate(*predicate.as_ref().skip_binder()), &mut s); + s +} + +/// Encodes predicates using the Itanium C++ ABI with vendor extended type qualifiers and types for +/// Rust types that are not used at the FFI boundary. +fn encode_predicates<'tcx>( + tcx: TyCtxt<'tcx>, + predicates: &List<Binder<'tcx, ExistentialPredicate<'tcx>>>, + dict: &mut FxHashMap<DictKey<'tcx>, usize>, + options: EncodeTyOptions, +) -> String { + // <predicate1[..predicateN]>E as part of vendor extended type + let mut s = String::new(); + let predicates: Vec<Binder<'tcx, ExistentialPredicate<'tcx>>> = + predicates.iter().map(|predicate| predicate).collect(); + for predicate in predicates { + s.push_str(&encode_predicate(tcx, predicate, dict, options)); + } + s +} + +/// Encodes a region using the Itanium C++ ABI as a vendor extended type. +fn encode_region<'tcx>( + _tcx: TyCtxt<'tcx>, + region: Region<'tcx>, + dict: &mut FxHashMap<DictKey<'tcx>, usize>, + _options: EncodeTyOptions, +) -> String { + // u6region[I[<region-disambiguator>][<region-index>]E] as vendor extended type + let mut s = String::new(); + match region.kind() { + RegionKind::ReLateBound(debruijn, r) => { + s.push_str("u6regionI"); + // Debruijn index, which identifies the binder, as region disambiguator + let num = debruijn.index() as u64; + if num > 0 { + s.push_str(&to_disambiguator(num)); + } + // Index within the binder + let _ = write!(s, "{}", r.var.index() as u64); + s.push('E'); + compress(dict, DictKey::Region(region), &mut s); + } + RegionKind::ReErased => { + s.push_str("u6region"); + compress(dict, DictKey::Region(region), &mut s); + } + RegionKind::ReEarlyBound(..) + | RegionKind::ReFree(..) + | RegionKind::ReStatic + | RegionKind::ReVar(..) + | RegionKind::RePlaceholder(..) + | RegionKind::ReEmpty(..) => { + bug!("encode_region: unexpected `{:?}`", region.kind()); + } + } + s +} + +/// Encodes substs using the Itanium C++ ABI with vendor extended type qualifiers and types for Rust +/// types that are not used at the FFI boundary. +fn encode_substs<'tcx>( + tcx: TyCtxt<'tcx>, + substs: SubstsRef<'tcx>, + dict: &mut FxHashMap<DictKey<'tcx>, usize>, + options: EncodeTyOptions, +) -> String { + // [I<subst1..substN>E] as part of vendor extended type + let mut s = String::new(); + let substs: Vec<GenericArg<'_>> = substs.iter().map(|subst| subst).collect(); + if !substs.is_empty() { + s.push('I'); + for subst in substs { + match subst.unpack() { + GenericArgKind::Lifetime(region) => { + s.push_str(&encode_region(tcx, region, dict, options)); + } + GenericArgKind::Type(ty) => { + s.push_str(&encode_ty(tcx, ty, dict, options)); + } + GenericArgKind::Const(c) => { + s.push_str(&encode_const(tcx, c, dict, options)); + } + } + } + s.push('E'); + } + s +} + +/// Encodes a ty:Ty name, including its crate and path disambiguators and names. +fn encode_ty_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> String { + // Encode <name> for use in u<length><name>[I<element-type1..element-typeN>E], where + // <element-type> is <subst>, using v0's <path> without v0's extended form of paths: + // + // N<namespace-tagN>..N<namespace-tag1> + // C<crate-disambiguator><crate-name> + // <path-disambiguator1><path-name1>..<path-disambiguatorN><path-nameN> + // + // With additional tags for DefPathData::Impl and DefPathData::ForeignMod. For instance: + // + // pub type Type1 = impl Send; + // let _: Type1 = <Struct1<i32>>::foo; + // fn foo1(_: Type1) { } + // + // pub type Type2 = impl Send; + // let _: Type2 = <Trait1<i32>>::foo; + // fn foo2(_: Type2) { } + // + // pub type Type3 = impl Send; + // let _: Type3 = <i32 as Trait1<i32>>::foo; + // fn foo3(_: Type3) { } + // + // pub type Type4 = impl Send; + // let _: Type4 = <Struct1<i32> as Trait1<i32>>::foo; + // fn foo3(_: Type4) { } + // + // Are encoded as: + // + // _ZTSFvu29NvNIC1234_5crate8{{impl}}3fooIu3i32EE + // _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3dynIu21NtC1234_5crate6Trait1Iu3i32Eu6regionES_EE + // _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3i32S_EE + // _ZTSFvu27NvNtC1234_5crate6Trait13fooIu22NtC1234_5crate7Struct1Iu3i32ES_EE + // + // The reason for not using v0's extended form of paths is to use a consistent and simpler + // encoding, as the reasoning for using it isn't relevand for type metadata identifiers (i.e., + // keep symbol names close to how methods are represented in error messages). See + // https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html#methods. + let mut s = String::new(); + + // Start and namespace tags + let mut def_path = tcx.def_path(def_id); + def_path.data.reverse(); + for disambiguated_data in &def_path.data { + s.push('N'); + s.push_str(match disambiguated_data.data { + hir::definitions::DefPathData::Impl => "I", // Not specified in v0's <namespace> + hir::definitions::DefPathData::ForeignMod => "F", // Not specified in v0's <namespace> + hir::definitions::DefPathData::TypeNs(..) => "t", + hir::definitions::DefPathData::ValueNs(..) => "v", + hir::definitions::DefPathData::ClosureExpr => "C", + hir::definitions::DefPathData::Ctor => "c", + hir::definitions::DefPathData::AnonConst => "k", + hir::definitions::DefPathData::ImplTrait => "i", + hir::definitions::DefPathData::CrateRoot + | hir::definitions::DefPathData::Use + | hir::definitions::DefPathData::GlobalAsm + | hir::definitions::DefPathData::MacroNs(..) + | hir::definitions::DefPathData::LifetimeNs(..) => { + bug!("encode_ty_name: unexpected `{:?}`", disambiguated_data.data); + } + }); + } + + // Crate disambiguator and name + s.push('C'); + s.push_str(&to_disambiguator(tcx.stable_crate_id(def_path.krate).to_u64())); + let crate_name = tcx.crate_name(def_path.krate).to_string(); + let _ = write!(s, "{}{}", crate_name.len(), &crate_name); + + // Disambiguators and names + def_path.data.reverse(); + for disambiguated_data in &def_path.data { + let num = disambiguated_data.disambiguator as u64; + if num > 0 { + s.push_str(&to_disambiguator(num)); + } + + let name = disambiguated_data.data.to_string(); + let _ = write!(s, "{}", name.len()); + + // Prepend a '_' if name starts with a digit or '_' + if let Some(first) = name.as_bytes().get(0) { + if first.is_ascii_digit() || *first == b'_' { + s.push('_'); + } + } else { + bug!("encode_ty_name: invalid name `{:?}`", name); + } + + s.push_str(&name); + } + + s +} + +/// Encodes a ty:Ty using the Itanium C++ ABI with vendor extended type qualifiers and types for +/// Rust types that are not used at the FFI boundary. +fn encode_ty<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + dict: &mut FxHashMap<DictKey<'tcx>, usize>, + options: EncodeTyOptions, +) -> String { + let mut typeid = String::new(); + + match ty.kind() { + // Primitive types + ty::Bool => { + typeid.push('b'); + } + + ty::Int(..) | ty::Uint(..) | ty::Float(..) => { + // u<length><type-name> as vendor extended type + let mut s = String::from(match ty.kind() { + ty::Int(IntTy::I8) => "u2i8", + ty::Int(IntTy::I16) => "u3i16", + ty::Int(IntTy::I32) => "u3i32", + ty::Int(IntTy::I64) => "u3i64", + ty::Int(IntTy::I128) => "u4i128", + ty::Int(IntTy::Isize) => "u5isize", + ty::Uint(UintTy::U8) => "u2u8", + ty::Uint(UintTy::U16) => "u3u16", + ty::Uint(UintTy::U32) => "u3u32", + ty::Uint(UintTy::U64) => "u3u64", + ty::Uint(UintTy::U128) => "u4u128", + ty::Uint(UintTy::Usize) => "u5usize", + ty::Float(FloatTy::F32) => "u3f32", + ty::Float(FloatTy::F64) => "u3f64", + _ => "", + }); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + ty::Char => { + // u4char as vendor extended type + let mut s = String::from("u4char"); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + ty::Str => { + // u3str as vendor extended type + let mut s = String::from("u3str"); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + ty::Never => { + // u5never as vendor extended type + let mut s = String::from("u5never"); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + // Compound types + // () in Rust is equivalent to void return type in C + _ if ty.is_unit() => { + typeid.push('v'); + } + + // Sequence types + ty::Tuple(tys) => { + // u5tupleI<element-type1..element-typeN>E as vendor extended type + let mut s = String::from("u5tupleI"); + for ty in tys.iter() { + s.push_str(&encode_ty(tcx, ty, dict, options)); + } + s.push('E'); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + ty::Array(ty0, len) => { + // A<array-length><element-type> + let mut s = String::from("A"); + let _ = write!(s, "{}", &len.kind().try_to_scalar().unwrap().to_u64().unwrap()); + s.push_str(&encode_ty(tcx, *ty0, dict, options)); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + ty::Slice(ty0) => { + // u5sliceI<element-type>E as vendor extended type + let mut s = String::from("u5sliceI"); + s.push_str(&encode_ty(tcx, *ty0, dict, options)); + s.push('E'); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + // User-defined types + ty::Adt(adt_def, substs) => { + let mut s = String::new(); + let def_id = adt_def.0.did; + if options.contains(EncodeTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c() { + // For for cross-language CFI support, the encoding must be compatible at the FFI + // boundary. For instance: + // + // struct type1 {}; + // void foo(struct type1* bar) {} + // + // Is encoded as: + // + // _ZTSFvP5type1E + // + // So, encode any repr(C) user-defined type for extern function types with the "C" + // calling convention (or extern types [i.e., ty::Foreign]) as <length><name>, where + // <name> is <unscoped-name>. + let name = tcx.item_name(def_id).to_string(); + let _ = write!(s, "{}{}", name.len(), &name); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + } else { + // u<length><name>[I<element-type1..element-typeN>E], where <element-type> is + // <subst>, as vendor extended type. + let name = encode_ty_name(tcx, def_id); + let _ = write!(s, "u{}{}", name.len(), &name); + s.push_str(&encode_substs(tcx, substs, dict, options)); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + } + typeid.push_str(&s); + } + + ty::Foreign(def_id) => { + // <length><name>, where <name> is <unscoped-name> + let mut s = String::new(); + let name = tcx.item_name(*def_id).to_string(); + let _ = write!(s, "{}{}", name.len(), &name); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + // Function types + ty::FnDef(def_id, substs) + | ty::Closure(def_id, substs) + | ty::Generator(def_id, substs, ..) => { + // u<length><name>[I<element-type1..element-typeN>E], where <element-type> is <subst>, + // as vendor extended type. + let mut s = String::new(); + let name = encode_ty_name(tcx, *def_id); + let _ = write!(s, "u{}{}", name.len(), &name); + s.push_str(&encode_substs(tcx, substs, dict, options)); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + // Pointer types + ty::Ref(region, ty0, ..) => { + // [U3mut]u3refI<element-type>E as vendor extended type qualifier and type + let mut s = String::new(); + s.push_str("u3refI"); + s.push_str(&encode_ty(tcx, *ty0, dict, options)); + s.push('E'); + compress(dict, DictKey::Ty(tcx.mk_imm_ref(*region, *ty0), TyQ::None), &mut s); + if ty.is_mutable_ptr() { + s = format!("{}{}", "U3mut", &s); + compress(dict, DictKey::Ty(ty, TyQ::Mut), &mut s); + } + typeid.push_str(&s); + } + + ty::RawPtr(tm) => { + // P[K]<element-type> + let mut s = String::new(); + s.push_str(&encode_ty(tcx, tm.ty, dict, options)); + if !ty.is_mutable_ptr() { + s = format!("{}{}", "K", &s); + compress(dict, DictKey::Ty(tm.ty, TyQ::Const), &mut s); + }; + s = format!("{}{}", "P", &s); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + ty::FnPtr(fn_sig) => { + // PF<return-type><parameter-type1..parameter-typeN>E + let mut s = String::from("P"); + s.push_str(&encode_fnsig(tcx, &fn_sig.skip_binder(), dict, TypeIdOptions::NO_OPTIONS)); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + // Trait types + ty::Dynamic(predicates, region) => { + // u3dynI<element-type1[..element-typeN]>E, where <element-type> is <predicate>, as + // vendor extended type. + let mut s = String::from("u3dynI"); + s.push_str(&encode_predicates(tcx, predicates, dict, options)); + s.push_str(&encode_region(tcx, *region, dict, options)); + s.push('E'); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + // Unexpected types + ty::Bound(..) + | ty::Error(..) + | ty::GeneratorWitness(..) + | ty::Infer(..) + | ty::Opaque(..) + | ty::Param(..) + | ty::Placeholder(..) + | ty::Projection(..) => { + bug!("encode_ty: unexpected `{:?}`", ty.kind()); + } + }; + + typeid +} + +// Transforms a ty:Ty for being encoded and used in the substitution dictionary. It transforms all +// c_void types into unit types unconditionally, and generalizes all pointers if +// TransformTyOptions::GENERALIZE_POINTERS option is set. +fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptions) -> Ty<'tcx> { + let mut ty = ty; + + match ty.kind() { + ty::Bool + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Char + | ty::Str + | ty::Never + | ty::Foreign(..) + | ty::Dynamic(..) => {} + + _ if ty.is_unit() => {} + + ty::Tuple(tys) => { + ty = tcx.mk_tup(tys.iter().map(|ty| transform_ty(tcx, ty, options))); + } + + ty::Array(ty0, len) => { + let len = len.kind().try_to_scalar().unwrap().to_u64().unwrap(); + ty = tcx.mk_array(transform_ty(tcx, *ty0, options), len); + } + + ty::Slice(ty0) => { + ty = tcx.mk_slice(transform_ty(tcx, *ty0, options)); + } + + ty::Adt(adt_def, substs) => { + if is_c_void_ty(tcx, ty) { + ty = tcx.mk_unit(); + } else if options.contains(TransformTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c() + { + ty = tcx.mk_adt(*adt_def, ty::List::empty()); + } else if adt_def.repr().transparent() && adt_def.is_struct() { + let variant = adt_def.non_enum_variant(); + let param_env = tcx.param_env(variant.def_id); + let field = variant.fields.iter().find(|field| { + let ty = tcx.type_of(field.did); + let is_zst = + tcx.layout_of(param_env.and(ty)).map_or(false, |layout| layout.is_zst()); + !is_zst + }); + if field.is_none() { + // Transform repr(transparent) types without non-ZST field into () + ty = tcx.mk_unit(); + } else { + let ty0 = tcx.type_of(field.unwrap().did); + // Generalize any repr(transparent) user-defined type that is either a pointer + // or reference, and either references itself or any other type that contains or + // references itself, to avoid a reference cycle. + if ty0.is_any_ptr() && ty0.contains(ty) { + ty = transform_ty( + tcx, + ty0, + options | TransformTyOptions::GENERALIZE_POINTERS, + ); + } else { + ty = transform_ty(tcx, ty0, options); + } + } + } else { + ty = tcx.mk_adt(*adt_def, transform_substs(tcx, substs, options)); + } + } + + ty::FnDef(def_id, substs) => { + ty = tcx.mk_fn_def(*def_id, transform_substs(tcx, substs, options)); + } + + ty::Closure(def_id, substs) => { + ty = tcx.mk_closure(*def_id, transform_substs(tcx, substs, options)); + } + + ty::Generator(def_id, substs, movability) => { + ty = tcx.mk_generator(*def_id, transform_substs(tcx, substs, options), *movability); + } + + ty::Ref(region, ty0, ..) => { + if options.contains(TransformTyOptions::GENERALIZE_POINTERS) { + if ty.is_mutable_ptr() { + ty = tcx.mk_mut_ref(tcx.lifetimes.re_static, tcx.mk_unit()); + } else { + ty = tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_unit()); + } + } else { + if ty.is_mutable_ptr() { + ty = tcx.mk_mut_ref(*region, transform_ty(tcx, *ty0, options)); + } else { + ty = tcx.mk_imm_ref(*region, transform_ty(tcx, *ty0, options)); + } + } + } + + ty::RawPtr(tm) => { + if options.contains(TransformTyOptions::GENERALIZE_POINTERS) { + if ty.is_mutable_ptr() { + ty = tcx.mk_mut_ptr(tcx.mk_unit()); + } else { + ty = tcx.mk_imm_ptr(tcx.mk_unit()); + } + } else { + if ty.is_mutable_ptr() { + ty = tcx.mk_mut_ptr(transform_ty(tcx, tm.ty, options)); + } else { + ty = tcx.mk_imm_ptr(transform_ty(tcx, tm.ty, options)); + } + } + } + + ty::FnPtr(fn_sig) => { + if options.contains(TransformTyOptions::GENERALIZE_POINTERS) { + ty = tcx.mk_imm_ptr(tcx.mk_unit()); + } else { + let parameters: Vec<Ty<'tcx>> = fn_sig + .skip_binder() + .inputs() + .iter() + .map(|ty| transform_ty(tcx, *ty, options)) + .collect(); + let output = transform_ty(tcx, fn_sig.skip_binder().output(), options); + ty = tcx.mk_fn_ptr(ty::Binder::bind_with_vars( + tcx.mk_fn_sig( + parameters.iter(), + &output, + fn_sig.c_variadic(), + fn_sig.unsafety(), + fn_sig.abi(), + ), + fn_sig.bound_vars(), + )); + } + } + + ty::Bound(..) + | ty::Error(..) + | ty::GeneratorWitness(..) + | ty::Infer(..) + | ty::Opaque(..) + | ty::Param(..) + | ty::Placeholder(..) + | ty::Projection(..) => { + bug!("transform_ty: unexpected `{:?}`", ty.kind()); + } + } + + ty +} + +/// Transforms substs for being encoded and used in the substitution dictionary. +fn transform_substs<'tcx>( + tcx: TyCtxt<'tcx>, + substs: SubstsRef<'tcx>, + options: TransformTyOptions, +) -> SubstsRef<'tcx> { + let substs: Vec<GenericArg<'tcx>> = substs + .iter() + .map(|subst| { + if let GenericArgKind::Type(ty) = subst.unpack() { + if is_c_void_ty(tcx, ty) { + tcx.mk_unit().into() + } else { + transform_ty(tcx, ty, options).into() + } + } else { + subst + } + }) + .collect(); + tcx.mk_substs(substs.iter()) +} + +/// Returns a type metadata identifier for the specified FnAbi using the Itanium C++ ABI with vendor +/// extended type qualifiers and types for Rust types that are not used at the FFI boundary. +pub fn typeid_for_fnabi<'tcx>( + tcx: TyCtxt<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + options: TypeIdOptions, +) -> String { + // A name is mangled by prefixing "_Z" to an encoding of its name, and in the case of functions + // its type. + let mut typeid = String::from("_Z"); + + // Clang uses the Itanium C++ ABI's virtual tables and RTTI typeinfo structure name as type + // metadata identifiers for function pointers. The typeinfo name encoding is a two-character + // code (i.e., 'TS') prefixed to the type encoding for the function. + typeid.push_str("TS"); + + // Function types are delimited by an "F..E" pair + typeid.push('F'); + + // A dictionary of substitution candidates used for compression (see + // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression). + let mut dict: FxHashMap<DictKey<'tcx>, usize> = FxHashMap::default(); + + let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits()) + .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits())); + match fn_abi.conv { + Conv::C => { + encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C); + } + _ => { + encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C); + } + } + + // Encode the return type + let transform_ty_options = TransformTyOptions::from_bits(options.bits()) + .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits())); + let ty = transform_ty(tcx, fn_abi.ret.layout.ty, transform_ty_options); + typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); + + // Encode the parameter types + if !fn_abi.c_variadic { + if !fn_abi.args.is_empty() { + for arg in fn_abi.args.iter() { + let ty = transform_ty(tcx, arg.layout.ty, transform_ty_options); + typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); + } + } else { + // Empty parameter lists, whether declared as () or conventionally as (void), are + // encoded with a void parameter specifier "v". + typeid.push('v'); + } + } else { + for n in 0..fn_abi.fixed_count { + let ty = transform_ty(tcx, fn_abi.args[n].layout.ty, transform_ty_options); + typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); + } + + typeid.push('z'); + } + + // Close the "F..E" pair + typeid.push('E'); + + typeid +} + +/// Returns a type metadata identifier for the specified FnSig using the Itanium C++ ABI with vendor +/// extended type qualifiers and types for Rust types that are not used at the FFI boundary. +pub fn typeid_for_fnsig<'tcx>( + tcx: TyCtxt<'tcx>, + fn_sig: &FnSig<'tcx>, + options: TypeIdOptions, +) -> String { + // A name is mangled by prefixing "_Z" to an encoding of its name, and in the case of functions + // its type. + let mut typeid = String::from("_Z"); + + // Clang uses the Itanium C++ ABI's virtual tables and RTTI typeinfo structure name as type + // metadata identifiers for function pointers. The typeinfo name encoding is a two-character + // code (i.e., 'TS') prefixed to the type encoding for the function. + typeid.push_str("TS"); + + // A dictionary of substitution candidates used for compression (see + // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression). + let mut dict: FxHashMap<DictKey<'tcx>, usize> = FxHashMap::default(); + + // Encode the function signature + typeid.push_str(&encode_fnsig(tcx, fn_sig, &mut dict, options)); + + typeid +} diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 13229a3995c..71fa5a44887 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -12,7 +12,6 @@ use rustc_middle::ty::{ self, EarlyBinder, FloatTy, Instance, IntTy, Ty, TyCtxt, TypeVisitable, UintTy, }; use rustc_span::symbol::kw; -use rustc_target::abi::call::FnAbi; use rustc_target::abi::Integer; use rustc_target::spec::abi::Abi; @@ -42,7 +41,7 @@ pub(super) fn mangle<'tcx>( // Append `::{shim:...#0}` to shims that can coexist with a non-shim instance. let shim_kind = match instance.def { - ty::InstanceDef::VtableShim(_) => Some("vtable"), + ty::InstanceDef::VTableShim(_) => Some("vtable"), ty::InstanceDef::ReifyShim(_) => Some("reify"), _ => None, @@ -59,41 +58,6 @@ pub(super) fn mangle<'tcx>( std::mem::take(&mut cx.out) } -pub(super) fn mangle_typeid_for_fnabi<'tcx>( - _tcx: TyCtxt<'tcx>, - fn_abi: &FnAbi<'tcx, Ty<'tcx>>, -) -> String { - // LLVM uses type metadata to allow IR modules to aggregate pointers by their types.[1] This - // type metadata is used by LLVM Control Flow Integrity to test whether a given pointer is - // associated with a type identifier (i.e., test type membership). - // - // Clang uses the Itanium C++ ABI's[2] virtual tables and RTTI typeinfo structure name[3] as - // type metadata identifiers for function pointers. The typeinfo name encoding is a - // two-character code (i.e., “TS”) prefixed to the type encoding for the function. - // - // For cross-language LLVM CFI support, a compatible encoding must be used by either - // - // a. Using a superset of types that encompasses types used by Clang (i.e., Itanium C++ ABI's - // type encodings[4]), or at least types used at the FFI boundary. - // b. Reducing the types to the least common denominator between types used by Clang (or at - // least types used at the FFI boundary) and Rust compilers (if even possible). - // c. Creating a new ABI for cross-language CFI and using it for Clang and Rust compilers (and - // possibly other compilers). - // - // Option (b) may weaken the protection for Rust-compiled only code, so it should be provided - // as an alternative to a Rust-specific encoding for when mixing Rust and C and C++ -compiled - // code. Option (c) would require changes to Clang to use the new ABI. - // - // [1] https://llvm.org/docs/TypeMetadata.html - // [2] https://itanium-cxx-abi.github.io/cxx-abi/abi.html - // [3] https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-special-vtables - // [4] https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-type - // - // FIXME(rcvalle): See comment above. - let arg_count = fn_abi.args.len() + fn_abi.ret.is_indirect() as usize; - format!("typeid{}", arg_count) -} - pub(super) fn mangle_typeid_for_trait_ref<'tcx>( tcx: TyCtxt<'tcx>, trait_ref: ty::PolyExistentialTraitRef<'tcx>, diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs index 6f4d073d704..b35502d9ee4 100644 --- a/compiler/rustc_target/src/abi/mod.rs +++ b/compiler/rustc_target/src/abi/mod.rs @@ -1350,15 +1350,19 @@ impl<'a, Ty> Deref for TyAndLayout<'a, Ty> { #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum PointerKind { /// Most general case, we know no restrictions to tell LLVM. - Shared, + SharedMutable, - /// `&T` where `T` contains no `UnsafeCell`, is `noalias` and `readonly`. + /// `&T` where `T` contains no `UnsafeCell`, is `dereferenceable`, `noalias` and `readonly`. Frozen, - /// `&mut T` which is `noalias` but not `readonly`. + /// `&mut T` which is `dereferenceable` and `noalias` but not `readonly`. UniqueBorrowed, - /// `Box<T>`, unlike `UniqueBorrowed`, it also has `noalias` on returns. + /// `&mut !Unpin`, which is `dereferenceable` but neither `noalias` nor `readonly`. + UniqueBorrowedPinned, + + /// `Box<T>`, which is `noalias` (even on return types, unlike the above) but neither `readonly` + /// nor `dereferenceable`. UniqueOwned, } diff --git a/compiler/rustc_target/src/asm/aarch64.rs b/compiler/rustc_target/src/asm/aarch64.rs index 25842049413..62a0f9fb034 100644 --- a/compiler/rustc_target/src/asm/aarch64.rs +++ b/compiler/rustc_target/src/asm/aarch64.rs @@ -1,6 +1,6 @@ use super::{InlineAsmArch, InlineAsmType}; use crate::spec::{RelocModel, Target}; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; diff --git a/compiler/rustc_target/src/asm/arm.rs b/compiler/rustc_target/src/asm/arm.rs index aaa632333db..0db3eb6fcac 100644 --- a/compiler/rustc_target/src/asm/arm.rs +++ b/compiler/rustc_target/src/asm/arm.rs @@ -1,6 +1,6 @@ use super::{InlineAsmArch, InlineAsmType}; use crate::spec::{RelocModel, Target}; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_macros::HashStable_Generic; use rustc_span::{sym, Symbol}; use std::fmt; diff --git a/compiler/rustc_target/src/asm/riscv.rs b/compiler/rustc_target/src/asm/riscv.rs index 987bf970529..e41bdc9a58c 100644 --- a/compiler/rustc_target/src/asm/riscv.rs +++ b/compiler/rustc_target/src/asm/riscv.rs @@ -1,6 +1,6 @@ use super::{InlineAsmArch, InlineAsmType}; use crate::spec::{RelocModel, Target}; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_macros::HashStable_Generic; use rustc_span::{sym, Symbol}; use std::fmt; diff --git a/compiler/rustc_target/src/asm/x86.rs b/compiler/rustc_target/src/asm/x86.rs index e35035fd25a..238c365093f 100644 --- a/compiler/rustc_target/src/asm/x86.rs +++ b/compiler/rustc_target/src/asm/x86.rs @@ -1,6 +1,6 @@ use super::{InlineAsmArch, InlineAsmType}; use crate::spec::{RelocModel, Target}; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; diff --git a/compiler/rustc_target/src/spec/aarch64_linux_android.rs b/compiler/rustc_target/src/spec/aarch64_linux_android.rs index 5e31859aaef..c85f7f62a42 100644 --- a/compiler/rustc_target/src/spec/aarch64_linux_android.rs +++ b/compiler/rustc_target/src/spec/aarch64_linux_android.rs @@ -17,6 +17,7 @@ pub fn target() -> Target { supported_sanitizers: SanitizerSet::CFI | SanitizerSet::HWADDRESS | SanitizerSet::MEMTAG + | SanitizerSet::SHADOWCALLSTACK | SanitizerSet::ADDRESS, ..super::android_base::opts() }, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 1a6bb4a2eaf..f7abeafd38f 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -618,6 +618,7 @@ bitflags::bitflags! { const HWADDRESS = 1 << 4; const CFI = 1 << 5; const MEMTAG = 1 << 6; + const SHADOWCALLSTACK = 1 << 7; } } @@ -632,6 +633,7 @@ impl SanitizerSet { SanitizerSet::LEAK => "leak", SanitizerSet::MEMORY => "memory", SanitizerSet::MEMTAG => "memtag", + SanitizerSet::SHADOWCALLSTACK => "shadow-call-stack", SanitizerSet::THREAD => "thread", SanitizerSet::HWADDRESS => "hwaddress", _ => return None, @@ -666,6 +668,7 @@ impl IntoIterator for SanitizerSet { SanitizerSet::LEAK, SanitizerSet::MEMORY, SanitizerSet::MEMTAG, + SanitizerSet::SHADOWCALLSTACK, SanitizerSet::THREAD, SanitizerSet::HWADDRESS, ] @@ -1960,6 +1963,7 @@ impl Target { Some("leak") => SanitizerSet::LEAK, Some("memory") => SanitizerSet::MEMORY, Some("memtag") => SanitizerSet::MEMTAG, + Some("shadow-call-stack") => SanitizerSet::SHADOWCALLSTACK, Some("thread") => SanitizerSet::THREAD, Some("hwaddress") => SanitizerSet::HWADDRESS, Some(s) => return Err(format!("unknown sanitizer {}", s)), diff --git a/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs b/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs index 8d6130a8a79..7125d141af7 100644 --- a/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs +++ b/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs @@ -4,11 +4,21 @@ //! //! Please ping @Lokathor if changes are needed. //! -//! This target profile assumes that you have the ARM binutils in your path (specifically the linker, `arm-none-eabi-ld`). They can be obtained for free for all major OSes from the ARM developer's website, and they may also be available in your system's package manager. Unfortunately, the standard linker that Rust uses (`lld`) only supports as far back as `ARMv5TE`, so we must use the GNU `ld` linker. +//! This target profile assumes that you have the ARM binutils in your path +//! (specifically the linker, `arm-none-eabi-ld`). They can be obtained for free +//! for all major OSes from the ARM developer's website, and they may also be +//! available in your system's package manager. Unfortunately, the standard +//! linker that Rust uses (`lld`) only supports as far back as `ARMv5TE`, so we +//! must use the GNU `ld` linker. //! -//! **Important:** This target profile **does not** specify a linker script. You just get the default link script when you build a binary for this target. The default link script is very likely wrong, so you should use `-Clink-arg=-Tmy_script.ld` to override that with a correct linker script. +//! **Important:** This target profile **does not** specify a linker script. You +//! just get the default link script when you build a binary for this target. +//! The default link script is very likely wrong, so you should use +//! `-Clink-arg=-Tmy_script.ld` to override that with a correct linker script. -use crate::spec::{cvs, LinkerFlavor, Target, TargetOptions}; +use crate::spec::{ + cvs, FramePointer, LinkerFlavor, PanicStrategy, RelocModel, Target, TargetOptions, +}; pub fn target() -> Target { Target { @@ -39,6 +49,14 @@ pub fn target() -> Target { // minimum extra features, these cannot be disabled via -C features: "+soft-float,+strict-align".into(), + panic_strategy: PanicStrategy::Abort, + relocation_model: RelocModel::Static, + // suggested from thumb_base, rust-lang/rust#44993. + emit_debug_gdb_scripts: false, + // suggested from thumb_base, with no-os gcc/clang use 8-bit enums + c_enum_min_bits: 8, + frame_pointer: FramePointer::MayOmit, + main_needs_argc_argv: false, // don't have atomic compare-and-swap diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 65ff9ceb67e..5763e6d1b55 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -793,9 +793,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { } ty::PredicateKind::RegionOutlives(binder) => { let binder = bound_predicate.rebind(binder); - if select.infcx().region_outlives_predicate(&dummy_cause, binder).is_err() { - return false; - } + select.infcx().region_outlives_predicate(&dummy_cause, binder) } ty::PredicateKind::TypeOutlives(binder) => { let binder = bound_predicate.rebind(binder); diff --git a/compiler/rustc_trait_selection/src/traits/codegen.rs b/compiler/rustc_trait_selection/src/traits/codegen.rs index 6ca630b74cc..5fcaa52d417 100644 --- a/compiler/rustc_trait_selection/src/traits/codegen.rs +++ b/compiler/rustc_trait_selection/src/traits/codegen.rs @@ -3,7 +3,7 @@ // seems likely that they should eventually be merged into more // general routines. -use crate::infer::TyCtxtInferExt; +use crate::infer::{DefiningAnchor, TyCtxtInferExt}; use crate::traits::{ FulfillmentContext, ImplSource, Obligation, ObligationCause, SelectionContext, TraitEngine, Unimplemented, @@ -30,7 +30,11 @@ pub fn codegen_fulfill_obligation<'tcx>( // Do the initial selection for the obligation. This yields the // shallow result we are looking for -- that is, what specific impl. - tcx.infer_ctxt().enter(|infcx| { + let mut infcx_builder = + tcx.infer_ctxt().ignoring_regions().with_opaque_type_inference(DefiningAnchor::Bubble); + infcx_builder.enter(|infcx| { + //~^ HACK `Bubble` is required for + // this test to pass: type-alias-impl-trait/assoc-projection-ice.rs let mut selcx = SelectionContext::new(&infcx); let obligation_cause = ObligationCause::dummy(); @@ -69,7 +73,8 @@ pub fn codegen_fulfill_obligation<'tcx>( // Opaque types may have gotten their hidden types constrained, but we can ignore them safely // as they will get constrained elsewhere, too. - let _opaque_types = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); + // (ouz-a) This is required for `type-alias-impl-trait/assoc-projection-ice.rs` to pass + let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); debug!("Cache miss: {trait_ref:?} => {impl_source:?}"); Ok(&*tcx.arena.alloc(impl_source)) diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 52ca23c4b30..f62ccb99df5 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -22,11 +22,12 @@ use rustc_middle::traits::specialization_graph::OverlapMode; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::visit::TypeVisitable; -use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt}; +use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt, TypeVisitor}; use rustc_span::symbol::sym; use rustc_span::DUMMY_SP; use std::fmt::Debug; use std::iter; +use std::ops::ControlFlow; /// Whether we do the orphan check relative to this crate or /// to some remote crate. @@ -397,13 +398,7 @@ fn resolve_negative_obligation<'cx, 'tcx>( let outlives_env = OutlivesEnvironment::new(param_env); infcx.process_registered_region_obligations(outlives_env.region_bound_pairs(), param_env); - let errors = infcx.resolve_regions(&outlives_env); - - if !errors.is_empty() { - return false; - } - - true + infcx.resolve_regions(&outlives_env).is_empty() } pub fn trait_ref_is_knowable<'tcx>( @@ -578,220 +573,175 @@ fn orphan_check_trait_ref<'tcx>( ); } - // Given impl<P1..=Pn> Trait<T1..=Tn> for T0, an impl is valid only - // if at least one of the following is true: - // - // - Trait is a local trait - // (already checked in orphan_check prior to calling this function) - // - All of - // - At least one of the types T0..=Tn must be a local type. - // Let Ti be the first such type. - // - No uncovered type parameters P1..=Pn may appear in T0..Ti (excluding Ti) - // - fn uncover_fundamental_ty<'tcx>( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, - in_crate: InCrate, - ) -> Vec<Ty<'tcx>> { - // FIXME: this is currently somewhat overly complicated, - // but fixing this requires a more complicated refactor. - if !contained_non_local_types(tcx, ty, in_crate).is_empty() { - if let Some(inner_tys) = fundamental_ty_inner_tys(tcx, ty) { - return inner_tys - .flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate)) - .collect(); + let mut checker = OrphanChecker::new(tcx, in_crate); + match trait_ref.visit_with(&mut checker) { + ControlFlow::Continue(()) => Err(OrphanCheckErr::NonLocalInputType(checker.non_local_tys)), + ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(ty)) => { + // Does there exist some local type after the `ParamTy`. + checker.search_first_local_ty = true; + if let Some(OrphanCheckEarlyExit::LocalTy(local_ty)) = + trait_ref.visit_with(&mut checker).break_value() + { + Err(OrphanCheckErr::UncoveredTy(ty, Some(local_ty))) + } else { + Err(OrphanCheckErr::UncoveredTy(ty, None)) } } - - vec![ty] + ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(_)) => Ok(()), } - - let mut non_local_spans = vec![]; - for (i, input_ty) in trait_ref - .substs - .types() - .flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate)) - .enumerate() - { - debug!("orphan_check_trait_ref: check ty `{:?}`", input_ty); - let non_local_tys = contained_non_local_types(tcx, input_ty, in_crate); - if non_local_tys.is_empty() { - debug!("orphan_check_trait_ref: ty_is_local `{:?}`", input_ty); - return Ok(()); - } else if let ty::Param(_) = input_ty.kind() { - debug!("orphan_check_trait_ref: uncovered ty: `{:?}`", input_ty); - let local_type = trait_ref - .substs - .types() - .flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate)) - .find(|&ty| ty_is_local_constructor(tcx, ty, in_crate)); - - debug!("orphan_check_trait_ref: uncovered ty local_type: `{:?}`", local_type); - - return Err(OrphanCheckErr::UncoveredTy(input_ty, local_type)); - } - - non_local_spans.extend(non_local_tys.into_iter().map(|input_ty| (input_ty, i == 0))); - } - // If we exit above loop, never found a local type. - debug!("orphan_check_trait_ref: no local type"); - Err(OrphanCheckErr::NonLocalInputType(non_local_spans)) } -/// Returns a list of relevant non-local types for `ty`. -/// -/// This is just `ty` itself unless `ty` is `#[fundamental]`, -/// in which case we recursively look into this type. -/// -/// If `ty` is local itself, this method returns an empty `Vec`. -/// -/// # Examples -/// -/// - `u32` is not local, so this returns `[u32]`. -/// - for `Foo<u32>`, where `Foo` is a local type, this returns `[]`. -/// - `&mut u32` returns `[u32]`, as `&mut` is a fundamental type, similar to `Box`. -/// - `Box<Foo<u32>>` returns `[]`, as `Box` is a fundamental type and `Foo` is local. -fn contained_non_local_types<'tcx>( +struct OrphanChecker<'tcx> { tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, in_crate: InCrate, -) -> Vec<Ty<'tcx>> { - if ty_is_local_constructor(tcx, ty, in_crate) { - Vec::new() - } else { - match fundamental_ty_inner_tys(tcx, ty) { - Some(inner_tys) => { - inner_tys.flat_map(|ty| contained_non_local_types(tcx, ty, in_crate)).collect() - } - None => vec![ty], + in_self_ty: bool, + /// Ignore orphan check failures and exclusively search for the first + /// local type. + search_first_local_ty: bool, + non_local_tys: Vec<(Ty<'tcx>, bool)>, +} + +impl<'tcx> OrphanChecker<'tcx> { + fn new(tcx: TyCtxt<'tcx>, in_crate: InCrate) -> Self { + OrphanChecker { + tcx, + in_crate, + in_self_ty: true, + search_first_local_ty: false, + non_local_tys: Vec::new(), } } -} -/// For `#[fundamental]` ADTs and `&T` / `&mut T`, returns `Some` with the -/// type parameters of the ADT, or `T`, respectively. For non-fundamental -/// types, returns `None`. -fn fundamental_ty_inner_tys<'tcx>( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, -) -> Option<impl Iterator<Item = Ty<'tcx>>> { - let (first_ty, rest_tys) = match *ty.kind() { - ty::Ref(_, ty, _) => (ty, ty::subst::InternalSubsts::empty().types()), - ty::Adt(def, substs) if def.is_fundamental() => { - let mut types = substs.types(); - - // FIXME(eddyb) actually validate `#[fundamental]` up-front. - match types.next() { - None => { - tcx.sess.span_err( - tcx.def_span(def.did()), - "`#[fundamental]` requires at least one type parameter", - ); - - return None; - } + fn found_non_local_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx>> { + self.non_local_tys.push((t, self.in_self_ty)); + ControlFlow::CONTINUE + } - Some(first_ty) => (first_ty, types), - } + fn found_param_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx>> { + if self.search_first_local_ty { + ControlFlow::CONTINUE + } else { + ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(t)) } - _ => return None, - }; + } - Some(iter::once(first_ty).chain(rest_tys)) + fn def_id_is_local(&mut self, def_id: DefId) -> bool { + match self.in_crate { + InCrate::Local => def_id.is_local(), + InCrate::Remote => false, + } + } } -fn def_id_is_local(def_id: DefId, in_crate: InCrate) -> bool { - match in_crate { - // The type is local to *this* crate - it will not be - // local in any other crate. - InCrate::Remote => false, - InCrate::Local => def_id.is_local(), - } +enum OrphanCheckEarlyExit<'tcx> { + ParamTy(Ty<'tcx>), + LocalTy(Ty<'tcx>), } -fn ty_is_local_constructor(tcx: TyCtxt<'_>, ty: Ty<'_>, in_crate: InCrate) -> bool { - debug!("ty_is_local_constructor({:?})", ty); - - match *ty.kind() { - ty::Bool - | ty::Char - | ty::Int(..) - | ty::Uint(..) - | ty::Float(..) - | ty::Str - | ty::FnDef(..) - | ty::FnPtr(_) - | ty::Array(..) - | ty::Slice(..) - | ty::RawPtr(..) - | ty::Ref(..) - | ty::Never - | ty::Tuple(..) - | ty::Param(..) - | ty::Projection(..) => false, - - ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => match in_crate { - InCrate::Local => false, - // The inference variable might be unified with a local - // type in that remote crate. - InCrate::Remote => true, - }, - - ty::Adt(def, _) => def_id_is_local(def.did(), in_crate), - ty::Foreign(did) => def_id_is_local(did, in_crate), - ty::Opaque(..) => { - // This merits some explanation. - // Normally, opaque types are not involved when performing - // coherence checking, since it is illegal to directly - // implement a trait on an opaque type. However, we might - // end up looking at an opaque type during coherence checking - // if an opaque type gets used within another type (e.g. as - // a type parameter). This requires us to decide whether or - // not an opaque type should be considered 'local' or not. - // - // We choose to treat all opaque types as non-local, even - // those that appear within the same crate. This seems - // somewhat surprising at first, but makes sense when - // you consider that opaque types are supposed to hide - // the underlying type *within the same crate*. When an - // opaque type is used from outside the module - // where it is declared, it should be impossible to observe - // anything about it other than the traits that it implements. - // - // The alternative would be to look at the underlying type - // to determine whether or not the opaque type itself should - // be considered local. However, this could make it a breaking change - // to switch the underlying ('defining') type from a local type - // to a remote type. This would violate the rule that opaque - // types should be completely opaque apart from the traits - // that they implement, so we don't use this behavior. - false - } +impl<'tcx> TypeVisitor<'tcx> for OrphanChecker<'tcx> { + type BreakTy = OrphanCheckEarlyExit<'tcx>; + fn visit_region(&mut self, _r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { + ControlFlow::CONTINUE + } - ty::Dynamic(ref tt, ..) => { - if let Some(principal) = tt.principal() { - def_id_is_local(principal.def_id(), in_crate) - } else { - false + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + let result = match *ty.kind() { + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Str + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Array(..) + | ty::Slice(..) + | ty::RawPtr(..) + | ty::Never + | ty::Tuple(..) + | ty::Projection(..) => self.found_non_local_ty(ty), + + ty::Param(..) => self.found_param_ty(ty), + + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => match self.in_crate { + InCrate::Local => self.found_non_local_ty(ty), + // The inference variable might be unified with a local + // type in that remote crate. + InCrate::Remote => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)), + }, + + // For fundamental types, we just look inside of them. + ty::Ref(_, ty, _) => ty.visit_with(self), + ty::Adt(def, substs) => { + if self.def_id_is_local(def.did()) { + ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) + } else if def.is_fundamental() { + substs.visit_with(self) + } else { + self.found_non_local_ty(ty) + } } - } + ty::Foreign(def_id) => { + if self.def_id_is_local(def_id) { + ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) + } else { + self.found_non_local_ty(ty) + } + } + ty::Dynamic(tt, ..) => { + let principal = tt.principal().map(|p| p.def_id()); + if principal.map_or(false, |p| self.def_id_is_local(p)) { + ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) + } else { + self.found_non_local_ty(ty) + } + } + ty::Error(_) => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)), + ty::Closure(..) | ty::Generator(..) | ty::GeneratorWitness(..) => { + self.tcx.sess.delay_span_bug( + DUMMY_SP, + format!("ty_is_local invoked on closure or generator: {:?}", ty), + ); + ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) + } + ty::Opaque(..) => { + // This merits some explanation. + // Normally, opaque types are not involved when performing + // coherence checking, since it is illegal to directly + // implement a trait on an opaque type. However, we might + // end up looking at an opaque type during coherence checking + // if an opaque type gets used within another type (e.g. as + // the type of a field) when checking for auto trait or `Sized` + // impls. This requires us to decide whether or not an opaque + // type should be considered 'local' or not. + // + // We choose to treat all opaque types as non-local, even + // those that appear within the same crate. This seems + // somewhat surprising at first, but makes sense when + // you consider that opaque types are supposed to hide + // the underlying type *within the same crate*. When an + // opaque type is used from outside the module + // where it is declared, it should be impossible to observe + // anything about it other than the traits that it implements. + // + // The alternative would be to look at the underlying type + // to determine whether or not the opaque type itself should + // be considered local. However, this could make it a breaking change + // to switch the underlying ('defining') type from a local type + // to a remote type. This would violate the rule that opaque + // types should be completely opaque apart from the traits + // that they implement, so we don't use this behavior. + self.found_non_local_ty(ty) + } + }; + // A bit of a hack, the `OrphanChecker` is only used to visit a `TraitRef`, so + // the first type we visit is always the self type. + self.in_self_ty = false; + result + } - ty::Error(_) => true, - - // 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 - } + // FIXME: Constants should participate in orphan checking. + fn visit_const(&mut self, _c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { + ControlFlow::CONTINUE } } diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index e6284b1c4ac..254bc4ab663 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -185,15 +185,12 @@ pub fn is_const_evaluatable<'cx, 'tcx>( } let concrete = infcx.const_eval_resolve(param_env, uv.expand(), Some(span)); match concrete { - Err(ErrorHandled::TooGeneric) => Err(if !uv.has_infer_types_or_consts() { - infcx - .tcx - .sess - .delay_span_bug(span, &format!("unexpected `TooGeneric` for {:?}", uv)); - NotConstEvaluatable::MentionsParam - } else { - NotConstEvaluatable::MentionsInfer - }), + Err(ErrorHandled::TooGeneric) => { + Err(NotConstEvaluatable::Error(infcx.tcx.sess.delay_span_bug( + span, + format!("Missing value for constant, but no error reported?"), + ))) + } Err(ErrorHandled::Linted) => { let reported = infcx .tcx @@ -240,8 +237,11 @@ pub fn is_const_evaluatable<'cx, 'tcx>( Err(ErrorHandled::TooGeneric) => Err(if uv.has_infer_types_or_consts() { NotConstEvaluatable::MentionsInfer - } else { + } else if uv.has_param_types_or_consts() { NotConstEvaluatable::MentionsParam + } else { + let guar = infcx.tcx.sess.delay_span_bug(span, format!("Missing value for constant, but no error reported?")); + NotConstEvaluatable::Error(guar) }), Err(ErrorHandled::Linted) => { let reported = diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index 0f7dc6a1257..6c177f63887 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -15,8 +15,6 @@ use rustc_middle::ty::{self, Ty, TyCtxt}; pub trait TraitEngineExt<'tcx> { fn new(tcx: TyCtxt<'tcx>) -> Box<Self>; - - fn new_ignoring_regions(tcx: TyCtxt<'tcx>) -> Box<Self>; } impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> { @@ -27,14 +25,6 @@ impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> { Box::new(FulfillmentContext::new()) } } - - fn new_ignoring_regions(tcx: TyCtxt<'tcx>) -> Box<Self> { - if tcx.sess.opts.unstable_opts.chalk { - Box::new(ChalkFulfillmentContext::new()) - } else { - Box::new(FulfillmentContext::new_ignoring_regions()) - } - } } /// Used if you want to have pleasant experience when dealing 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 29df771b957..7ee3fe844b5 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -22,7 +22,6 @@ use rustc_hir::intravisit::Visitor; use rustc_hir::GenericParam; use rustc_hir::Item; use rustc_hir::Node; -use rustc_infer::infer::error_reporting::same_type_modulo_infer; use rustc_infer::traits::TraitEngine; use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::abstract_const::NotConstEvaluatable; @@ -302,13 +301,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { span = obligation.cause.span; } } - if let ObligationCauseCode::CompareImplMethodObligation { - impl_item_def_id, - trait_item_def_id, - } - | ObligationCauseCode::CompareImplTypeObligation { + if let ObligationCauseCode::CompareImplItemObligation { impl_item_def_id, trait_item_def_id, + kind: _, } = *obligation.cause.code() { self.report_extra_impl_obligation( @@ -535,6 +531,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } self.suggest_floating_point_literal(&obligation, &mut err, &trait_ref); + self.suggest_dereferencing_index(&obligation, &mut err, trait_predicate); let mut suggested = self.suggest_dereferences(&obligation, &mut err, trait_predicate); suggested |= self.suggest_fn_call(&obligation, &mut err, trait_predicate); @@ -635,12 +632,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { &format!( "expected a closure taking {} argument{}, but one taking {} argument{} was given", given.len(), - if given.len() == 1 { "" } else { "s" }, + pluralize!(given.len()), expected.len(), - if expected.len() == 1 { "" } else { "s" }, + pluralize!(expected.len()), ) ); - } else if !same_type_modulo_infer(given_ty, expected_ty) { + } else if !self.same_type_modulo_infer(given_ty, expected_ty) { // Print type mismatch let (expected_args, given_args) = self.cmp(given_ty, expected_ty); @@ -670,7 +667,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ); } else if !suggested { // Can't show anything else useful, try to find similar impls. - let impl_candidates = self.find_similar_impl_candidates(trait_ref); + let impl_candidates = self.find_similar_impl_candidates(trait_predicate); if !self.report_similar_impl_candidates( impl_candidates, trait_ref, @@ -705,7 +702,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { { let trait_ref = trait_pred.to_poly_trait_ref(); let impl_candidates = - self.find_similar_impl_candidates(trait_ref); + self.find_similar_impl_candidates(trait_pred); self.report_similar_impl_candidates( impl_candidates, trait_ref, @@ -789,24 +786,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { span_bug!(span, "coerce requirement gave wrong error: `{:?}`", predicate) } - ty::PredicateKind::RegionOutlives(predicate) => { - let predicate = bound_predicate.rebind(predicate); - let predicate = self.resolve_vars_if_possible(predicate); - let err = self - .region_outlives_predicate(&obligation.cause, predicate) - .err() - .unwrap(); - struct_span_err!( - self.tcx.sess, - span, - E0279, - "the requirement `{}` is not satisfied (`{}`)", - predicate, - err, - ) - } - - ty::PredicateKind::Projection(..) | ty::PredicateKind::TypeOutlives(..) => { + ty::PredicateKind::RegionOutlives(..) + | ty::PredicateKind::Projection(..) + | ty::PredicateKind::TypeOutlives(..) => { let predicate = self.resolve_vars_if_possible(obligation.predicate); struct_span_err!( self.tcx.sess, @@ -1344,7 +1326,7 @@ trait InferCtxtPrivExt<'hir, 'tcx> { fn find_similar_impl_candidates( &self, - trait_ref: ty::PolyTraitRef<'tcx>, + trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> Vec<ImplCandidate<'tcx>>; fn report_similar_impl_candidates( @@ -1713,18 +1695,22 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { fn find_similar_impl_candidates( &self, - trait_ref: ty::PolyTraitRef<'tcx>, + trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> Vec<ImplCandidate<'tcx>> { self.tcx - .all_impls(trait_ref.def_id()) + .all_impls(trait_pred.def_id()) .filter_map(|def_id| { - if self.tcx.impl_polarity(def_id) == ty::ImplPolarity::Negative { + if self.tcx.impl_polarity(def_id) == ty::ImplPolarity::Negative + || !trait_pred + .skip_binder() + .is_constness_satisfied_by(self.tcx.constness(def_id)) + { return None; } let imp = self.tcx.impl_trait_ref(def_id).unwrap(); - self.fuzzy_match_tys(trait_ref.skip_binder().self_ty(), imp.self_ty(), false) + self.fuzzy_match_tys(trait_pred.skip_binder().self_ty(), imp.self_ty(), false) .map(|similarity| ImplCandidate { trait_ref: imp, similarity }) }) .collect() 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 bca80e7ab8a..b2eb8fdf8a5 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -20,6 +20,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node}; +use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::hir::map; use rustc_middle::ty::{ self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, DefIdTree, @@ -184,7 +185,7 @@ pub trait InferCtxtExt<'tcx> { trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool; - fn get_closure_name(&self, def_id: DefId, err: &mut Diagnostic, msg: &str) -> Option<String>; + fn get_closure_name(&self, def_id: DefId, err: &mut Diagnostic, msg: &str) -> Option<Symbol>; fn suggest_fn_call( &self, @@ -253,8 +254,8 @@ pub trait InferCtxtExt<'tcx> { &self, span: Span, found_span: Option<Span>, - expected_ref: ty::PolyTraitRef<'tcx>, found: ty::PolyTraitRef<'tcx>, + expected: ty::PolyTraitRef<'tcx>, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>; fn suggest_fully_qualified_path( @@ -320,6 +321,13 @@ pub trait InferCtxtExt<'tcx> { err: &mut Diagnostic, trait_pred: ty::PolyTraitPredicate<'tcx>, ); + + fn suggest_dereferencing_index( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diagnostic, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ); } fn predicate_constraint(generics: &hir::Generics<'_>, pred: String) -> (Span, String) { @@ -378,7 +386,7 @@ fn suggest_restriction<'tcx>( replace_ty: ty::ParamTy::new(generics.count() as u32, Symbol::intern(&type_param_name)) .to_ty(tcx), }); - if !trait_pred.is_suggestable(tcx) { + if !trait_pred.is_suggestable(tcx, false) { return; } // We know we have an `impl Trait` that doesn't satisfy a required projection. @@ -417,7 +425,7 @@ fn suggest_restriction<'tcx>( Applicability::MaybeIncorrect, ); } else { - if !trait_pred.is_suggestable(tcx) { + if !trait_pred.is_suggestable(tcx, false) { return; } // Trivial case: `T` needs an extra bound: `T: Bound`. @@ -586,7 +594,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // else in the predicate. if !trait_pred.skip_binder().trait_ref.substs[1..] .iter() - .all(|g| g.is_suggestable(self.tcx)) + .all(|g| g.is_suggestable(self.tcx, false)) { return; } @@ -607,10 +615,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { "{}, {}={}>", &constraint[..constraint.len() - 1], item.name, - term.to_string() + term ); } else { - constraint.push_str(&format!("<{}={}>", item.name, term.to_string())); + constraint.push_str(&format!("<{}={}>", item.name, term)); } } @@ -737,13 +745,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { /// Given a closure's `DefId`, return the given name of the closure. /// /// This doesn't account for reassignments, but it's only used for suggestions. - fn get_closure_name(&self, def_id: DefId, err: &mut Diagnostic, msg: &str) -> Option<String> { - let get_name = |err: &mut Diagnostic, kind: &hir::PatKind<'_>| -> Option<String> { + fn get_closure_name(&self, def_id: DefId, err: &mut Diagnostic, msg: &str) -> Option<Symbol> { + let get_name = |err: &mut Diagnostic, kind: &hir::PatKind<'_>| -> Option<Symbol> { // Get the local name of this closure. This can be inaccurate because // of the possibility of reassignment, but this should be good enough. match &kind { - hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, name, None) => { - Some(format!("{}", name)) + hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, ident, None) => { + Some(ident.name) } _ => { err.note(msg); @@ -1529,13 +1537,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { &self, span: Span, found_span: Option<Span>, - expected_ref: ty::PolyTraitRef<'tcx>, found: ty::PolyTraitRef<'tcx>, + expected: ty::PolyTraitRef<'tcx>, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - pub(crate) fn build_fn_sig_string<'tcx>( + pub(crate) fn build_fn_sig_ty<'tcx>( tcx: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>, - ) -> String { + ) -> Ty<'tcx> { let inputs = trait_ref.skip_binder().substs.type_at(1); let sig = match inputs.kind() { ty::Tuple(inputs) @@ -1557,10 +1565,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { abi::Abi::Rust, ), }; - trait_ref.rebind(sig).to_string() + + tcx.mk_fn_ptr(trait_ref.rebind(sig)) } - let argument_kind = match expected_ref.skip_binder().self_ty().kind() { + let argument_kind = match expected.skip_binder().self_ty().kind() { ty::Closure(..) => "closure", ty::Generator(..) => "generator", _ => "function", @@ -1569,17 +1578,22 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { self.tcx.sess, span, E0631, - "type mismatch in {} arguments", - argument_kind + "type mismatch in {argument_kind} arguments", ); - let found_str = format!("expected signature of `{}`", build_fn_sig_string(self.tcx, found)); - err.span_label(span, found_str); + err.span_label(span, "expected due to this"); let found_span = found_span.unwrap_or(span); - let expected_str = - format!("found signature of `{}`", build_fn_sig_string(self.tcx, expected_ref)); - err.span_label(found_span, expected_str); + err.span_label(found_span, "found signature defined here"); + + let expected = build_fn_sig_ty(self.tcx, expected); + let found = build_fn_sig_ty(self.tcx, found); + + let (expected_str, found_str) = + self.tcx.infer_ctxt().enter(|infcx| infcx.cmp(expected, found)); + + let signature_kind = format!("{argument_kind} signature"); + err.note_expected_found(&signature_kind, expected_str, &signature_kind, found_str); err } @@ -1783,8 +1797,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let generator_body = generator_did .as_local() - .map(|def_id| hir.local_def_id_to_hir_id(def_id)) - .and_then(|hir_id| hir.maybe_body_owned_by(hir_id)) + .and_then(|def_id| hir.maybe_body_owned_by(def_id)) .map(|body_id| hir.body(body_id)); let is_async = match generator_did.as_local() { Some(_) => generator_body @@ -2682,11 +2695,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ) }); } - ObligationCauseCode::CompareImplMethodObligation { trait_item_def_id, .. } => { + ObligationCauseCode::CompareImplItemObligation { trait_item_def_id, kind, .. } => { let item_name = self.tcx.item_name(trait_item_def_id); let msg = format!( - "the requirement `{}` appears on the impl method `{}` but not on the \ - corresponding trait method", + "the requirement `{}` appears on the `impl`'s {kind} `{}` but not on the \ + corresponding trait's {kind}", predicate, item_name, ); let sp = self @@ -2697,7 +2710,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let mut assoc_span: MultiSpan = sp.into(); assoc_span.push_span_label( sp, - format!("this trait method doesn't have the requirement `{}`", predicate), + format!("this trait's {kind} doesn't have the requirement `{}`", predicate), ); if let Some(ident) = self .tcx @@ -2708,38 +2721,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } err.span_note(assoc_span, &msg); } - ObligationCauseCode::CompareImplTypeObligation { trait_item_def_id, .. } => { - let item_name = self.tcx.item_name(trait_item_def_id); - let msg = format!( - "the requirement `{}` appears on the associated impl type `{}` but not on the \ - corresponding associated trait type", - predicate, item_name, - ); - let sp = self.tcx.def_span(trait_item_def_id); - let mut assoc_span: MultiSpan = sp.into(); - assoc_span.push_span_label( - sp, - format!( - "this trait associated type doesn't have the requirement `{}`", - predicate, - ), - ); - if let Some(ident) = self - .tcx - .opt_associated_item(trait_item_def_id) - .and_then(|i| self.tcx.opt_item_ident(i.container.id())) - { - assoc_span.push_span_label(ident.span, "in this trait"); - } - err.span_note(assoc_span, &msg); - } - ObligationCauseCode::CompareImplConstObligation => { - err.note(&format!( - "the requirement `{}` appears on the associated impl constant \ - but not on the corresponding associated trait constant", - predicate - )); - } ObligationCauseCode::TrivialBound => { err.help("see issue #48214"); if tcx.sess.opts.unstable_features.is_nightly_build() { @@ -2784,7 +2765,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let body_hir_id = obligation.cause.body_id; let item_id = self.tcx.hir().get_parent_node(body_hir_id); - if let Some(body_id) = self.tcx.hir().maybe_body_owned_by(item_id) { + if let Some(body_id) = + self.tcx.hir().maybe_body_owned_by(self.tcx.hir().local_def_id(item_id)) + { let body = self.tcx.hir().body(body_id); if let Some(hir::GeneratorKind::Async(_)) = body.generator_kind { let future_trait = self.tcx.require_lang_item(LangItem::Future, None); @@ -2927,6 +2910,27 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ); } } + + fn suggest_dereferencing_index( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diagnostic, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) { + if let ObligationCauseCode::ImplDerivedObligation(_) = obligation.cause.code() + && self.tcx.is_diagnostic_item(sym::SliceIndex, trait_pred.skip_binder().trait_ref.def_id) + && let ty::Slice(_) = trait_pred.skip_binder().trait_ref.substs.type_at(1).kind() + && let ty::Ref(_, inner_ty, _) = trait_pred.skip_binder().self_ty().kind() + && let ty::Uint(ty::UintTy::Usize) = inner_ty.kind() + { + err.span_suggestion_verbose( + obligation.cause.span.shrink_to_lo(), + "dereference this index", + '*', + Applicability::MachineApplicable, + ); + } + } } /// Collect all the returned expressions within the input expression. diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 34b37c4e410..556ef466cd1 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -58,19 +58,6 @@ pub struct FulfillmentContext<'tcx> { relationships: FxHashMap<ty::TyVid, ty::FoundRelationships>, - // Should this fulfillment context register type-lives-for-region - // obligations on its parent infcx? In some cases, region - // obligations are either already known to hold (normalization) or - // hopefully verified elsewhere (type-impls-bound), and therefore - // should not be checked. - // - // Note that if we are normalizing a type that we already - // know is well-formed, there should be no harm setting this - // to true - all the region variables should be determinable - // using the RFC 447 rules, which don't depend on - // type-lives-for-region constraints, and because the type - // is well-formed, the constraints should hold. - register_region_obligations: bool, // Is it OK to register obligations into this infcx inside // an infcx snapshot? // @@ -103,7 +90,6 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> { FulfillmentContext { predicates: ObligationForest::new(), relationships: FxHashMap::default(), - register_region_obligations: true, usable_in_snapshot: false, } } @@ -112,30 +98,18 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> { FulfillmentContext { predicates: ObligationForest::new(), relationships: FxHashMap::default(), - register_region_obligations: true, usable_in_snapshot: true, } } - pub fn new_ignoring_regions() -> FulfillmentContext<'tcx> { - FulfillmentContext { - predicates: ObligationForest::new(), - relationships: FxHashMap::default(), - register_region_obligations: false, - usable_in_snapshot: false, - } - } - /// Attempts to select obligations using `selcx`. fn select(&mut self, selcx: &mut SelectionContext<'a, 'tcx>) -> Vec<FulfillmentError<'tcx>> { let span = debug_span!("select", obligation_forest_size = ?self.predicates.len()); let _enter = span.enter(); // Process pending obligations. - let outcome: Outcome<_, _> = self.predicates.process_obligations(&mut FulfillProcessor { - selcx, - register_region_obligations: self.register_region_obligations, - }); + let outcome: Outcome<_, _> = + self.predicates.process_obligations(&mut FulfillProcessor { selcx }); // FIXME: if we kept the original cache key, we could mark projection // obligations as complete for the projection cache here. @@ -239,7 +213,6 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { struct FulfillProcessor<'a, 'b, 'tcx> { selcx: &'a mut SelectionContext<'b, 'tcx>, - register_region_obligations: bool, } fn mk_pending(os: Vec<PredicateObligation<'_>>) -> Vec<PendingPredicateObligation<'_>> { @@ -385,19 +358,16 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { } ty::PredicateKind::RegionOutlives(data) => { - match infcx.region_outlives_predicate(&obligation.cause, Binder::dummy(data)) { - Ok(()) => ProcessResult::Changed(vec![]), - Err(_) => ProcessResult::Error(CodeSelectionError(Unimplemented)), + if infcx.considering_regions || data.has_placeholders() { + infcx.region_outlives_predicate(&obligation.cause, Binder::dummy(data)); } + + ProcessResult::Changed(vec![]) } ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(t_a, r_b)) => { - if self.register_region_obligations { - self.selcx.infcx().register_region_obligation_with_cause( - t_a, - r_b, - &obligation.cause, - ); + if infcx.considering_regions { + infcx.register_region_obligation_with_cause(t_a, r_b, &obligation.cause); } ProcessResult::Changed(vec![]) } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index a14bf72242b..d0a17f712d3 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -60,8 +60,9 @@ pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError pub use self::specialize::specialization_graph::FutureCompatOverlapError; pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind; pub use self::specialize::{specialization_graph, translate_substs, OverlapError}; -pub use self::structural_match::search_for_structural_match_violation; -pub use self::structural_match::{NonStructuralMatchTy, NonStructuralMatchTyKind}; +pub use self::structural_match::{ + search_for_adt_const_param_violation, search_for_structural_match_violation, +}; pub use self::util::{ elaborate_obligations, elaborate_predicates, elaborate_predicates_with_span, elaborate_trait_ref, elaborate_trait_refs, @@ -163,7 +164,7 @@ pub fn type_known_to_meet_bound_modulo_regions<'a, 'tcx>( // The handling of regions in this area of the code is terrible, // see issue #29149. We should be able to improve on this with // NLL. - let mut fulfill_cx = FulfillmentContext::new_ignoring_regions(); + let mut fulfill_cx = FulfillmentContext::new(); // We can use a dummy node-id here because we won't pay any mind // to region obligations that arise (there shouldn't really be any @@ -207,21 +208,21 @@ fn do_normalize_predicates<'tcx>( predicates: Vec<ty::Predicate<'tcx>>, ) -> Result<Vec<ty::Predicate<'tcx>>, ErrorGuaranteed> { let span = cause.span; - tcx.infer_ctxt().enter(|infcx| { - // FIXME. We should really... do something with these region - // obligations. But this call just continues the older - // behavior (i.e., doesn't cause any new bugs), and it would - // take some further refactoring to actually solve them. In - // particular, we would have to handle implied bounds - // properly, and that code is currently largely confined to - // regionck (though I made some efforts to extract it - // out). -nmatsakis - // - // @arielby: In any case, these obligations are checked - // by wfcheck anyway, so I'm not sure we have to check - // them here too, and we will remove this function when - // we move over to lazy normalization *anyway*. - let fulfill_cx = FulfillmentContext::new_ignoring_regions(); + // FIXME. We should really... do something with these region + // obligations. But this call just continues the older + // behavior (i.e., doesn't cause any new bugs), and it would + // take some further refactoring to actually solve them. In + // particular, we would have to handle implied bounds + // properly, and that code is currently largely confined to + // regionck (though I made some efforts to extract it + // out). -nmatsakis + // + // @arielby: In any case, these obligations are checked + // by wfcheck anyway, so I'm not sure we have to check + // them here too, and we will remove this function when + // we move over to lazy normalization *anyway*. + tcx.infer_ctxt().ignoring_regions().enter(|infcx| { + let fulfill_cx = FulfillmentContext::new(); let predicates = match fully_normalize(&infcx, fulfill_cx, cause, elaborated_env, predicates) { Ok(predicates) => predicates, diff --git a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs index ed7d16f7a54..4d3b0b4cf07 100644 --- a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs @@ -337,7 +337,7 @@ impl<'tcx> OnUnimplementedFormatString { } } // `{:1}` and `{}` are not to be used - Position::ArgumentIs(_) | Position::ArgumentImplicitlyIs(_) => { + Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => { let reported = struct_span_err!( tcx.sess, span, diff --git a/compiler/rustc_trait_selection/src/traits/relationships.rs b/compiler/rustc_trait_selection/src/traits/relationships.rs index 56bdeafeeca..8148e2b7871 100644 --- a/compiler/rustc_trait_selection/src/traits/relationships.rs +++ b/compiler/rustc_trait_selection/src/traits/relationships.rs @@ -31,14 +31,14 @@ pub(crate) fn update<'tcx, T>( obligation .predicate .kind() - .map_bound(|_| { + .rebind( // (*) binder moved here ty::PredicateKind::Trait(ty::TraitPredicate { trait_ref, constness: tpred.constness, polarity: tpred.polarity, }) - }) + ) .to_predicate(infcx.tcx), ); // Don't report overflow errors. Otherwise equivalent to may_hold. diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index da8ca6e5749..d4c9fd1c5f9 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -42,115 +42,96 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &TraitObligation<'tcx>, candidate: SelectionCandidate<'tcx>, ) -> Result<Selection<'tcx>, SelectionError<'tcx>> { - let mut obligation = obligation; - let new_obligation; - - // HACK(const_trait_impl): the surrounding environment is remapped to a non-const context - // because nested obligations might be actually `~const` then (incorrectly) requiring - // const impls. for example: - // ``` - // pub trait Super {} - // pub trait Sub: Super {} - // - // impl<A> const Super for &A where A: ~const Super {} - // impl<A> const Sub for &A where A: ~const Sub {} - // ``` - // - // The procedure to check the code above without the remapping code is as follows: - // ``` - // CheckWf(impl const Sub for &A where A: ~const Sub) // <- const env - // CheckPredicate(&A: Super) - // CheckPredicate(A: ~const Super) // <- still const env, failure - // ``` - if obligation.param_env.is_const() && !obligation.predicate.is_const_if_const() { - new_obligation = TraitObligation { - cause: obligation.cause.clone(), - param_env: obligation.param_env.without_const(), - ..*obligation - }; - obligation = &new_obligation; - } - - match candidate { + let mut impl_src = match candidate { BuiltinCandidate { has_nested } => { let data = self.confirm_builtin_candidate(obligation, has_nested); - Ok(ImplSource::Builtin(data)) + ImplSource::Builtin(data) } ParamCandidate(param) => { let obligations = self.confirm_param_candidate(obligation, param.map_bound(|t| t.trait_ref)); - Ok(ImplSource::Param(obligations, param.skip_binder().constness)) + ImplSource::Param(obligations, param.skip_binder().constness) } ImplCandidate(impl_def_id) => { - Ok(ImplSource::UserDefined(self.confirm_impl_candidate(obligation, impl_def_id))) + ImplSource::UserDefined(self.confirm_impl_candidate(obligation, impl_def_id)) } AutoImplCandidate(trait_def_id) => { let data = self.confirm_auto_impl_candidate(obligation, trait_def_id); - Ok(ImplSource::AutoImpl(data)) + ImplSource::AutoImpl(data) } ProjectionCandidate(idx) => { let obligations = self.confirm_projection_candidate(obligation, idx)?; // FIXME(jschievink): constness - Ok(ImplSource::Param(obligations, ty::BoundConstness::NotConst)) + ImplSource::Param(obligations, ty::BoundConstness::NotConst) } ObjectCandidate(idx) => { let data = self.confirm_object_candidate(obligation, idx)?; - Ok(ImplSource::Object(data)) + ImplSource::Object(data) } ClosureCandidate => { let vtable_closure = self.confirm_closure_candidate(obligation)?; - Ok(ImplSource::Closure(vtable_closure)) + ImplSource::Closure(vtable_closure) } GeneratorCandidate => { let vtable_generator = self.confirm_generator_candidate(obligation)?; - Ok(ImplSource::Generator(vtable_generator)) + ImplSource::Generator(vtable_generator) } FnPointerCandidate { .. } => { let data = self.confirm_fn_pointer_candidate(obligation)?; - Ok(ImplSource::FnPointer(data)) + ImplSource::FnPointer(data) } DiscriminantKindCandidate => { - Ok(ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData)) + ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData) } - PointeeCandidate => Ok(ImplSource::Pointee(ImplSourcePointeeData)), + PointeeCandidate => ImplSource::Pointee(ImplSourcePointeeData), TraitAliasCandidate(alias_def_id) => { let data = self.confirm_trait_alias_candidate(obligation, alias_def_id); - Ok(ImplSource::TraitAlias(data)) + ImplSource::TraitAlias(data) } BuiltinObjectCandidate => { // This indicates something like `Trait + Send: Send`. In this case, we know that // this holds because that's what the object type is telling us, and there's really // no additional obligations to prove and no types in particular to unify, etc. - Ok(ImplSource::Param(Vec::new(), ty::BoundConstness::NotConst)) + ImplSource::Param(Vec::new(), ty::BoundConstness::NotConst) } BuiltinUnsizeCandidate => { let data = self.confirm_builtin_unsize_candidate(obligation)?; - Ok(ImplSource::Builtin(data)) + ImplSource::Builtin(data) } TraitUpcastingUnsizeCandidate(idx) => { let data = self.confirm_trait_upcasting_unsize_candidate(obligation, idx)?; - Ok(ImplSource::TraitUpcasting(data)) + ImplSource::TraitUpcasting(data) } ConstDestructCandidate(def_id) => { let data = self.confirm_const_destruct_candidate(obligation, def_id)?; - Ok(ImplSource::ConstDestruct(data)) + ImplSource::ConstDestruct(data) } + }; + + if !obligation.predicate.is_const_if_const() { + // normalize nested predicates according to parent predicate's constness. + impl_src = impl_src.map(|mut o| { + o.predicate = o.predicate.without_const(self.tcx()); + o + }); } + + Ok(impl_src) } fn confirm_projection_candidate( diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index fa2d2c751d9..17f34012d1d 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -789,7 +789,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let mut param_env = obligation.param_env; fresh_trait_pred = fresh_trait_pred.map_bound(|mut pred| { - pred.remap_constness(self.tcx(), &mut param_env); + pred.remap_constness(&mut param_env); pred }); @@ -1321,7 +1321,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } let tcx = self.tcx(); let mut pred = cache_fresh_trait_pred.skip_binder(); - pred.remap_constness(tcx, &mut param_env); + pred.remap_constness(&mut param_env); if self.can_use_global_caches(param_env) { if let Some(res) = tcx.selection_cache.get(&(param_env, pred), tcx) { @@ -1375,7 +1375,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let tcx = self.tcx(); let mut pred = cache_fresh_trait_pred.skip_binder(); - pred.remap_constness(tcx, &mut param_env); + pred.remap_constness(&mut param_env); if !self.can_cache_candidate(&candidate) { debug!(?pred, ?candidate, "insert_candidate_cache - candidate is not cacheable"); diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index 2c4a453aefc..5f77aae6f22 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -207,18 +207,7 @@ fn fulfill_implication<'a, 'tcx>( // (which are packed up in penv) infcx.save_and_restore_in_snapshot_flag(|infcx| { - // If we came from `translate_substs`, we already know that the - // predicates for our impl hold (after all, we know that a more - // specialized impl holds, so our impl must hold too), and - // we only want to process the projections to determine the - // the types in our substs using RFC 447, so we can safely - // ignore region obligations, which allows us to avoid threading - // a node-id to assign them with. - // - // If we came from specialization graph construction, then - // we already make a mockery out of the region system, so - // why not ignore them a bit earlier? - let mut fulfill_cx = FulfillmentContext::new_ignoring_regions(); + let mut fulfill_cx = FulfillmentContext::new(); for oblig in obligations.chain(more_obligations) { fulfill_cx.register_predicate_obligation(&infcx, oblig); } diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs index 6c0b83fbd03..c278752e3d9 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_match.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs @@ -6,29 +6,10 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::lang_items::LangItem; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; use rustc_span::Span; use std::ops::ControlFlow; -#[derive(Debug)] -pub struct NonStructuralMatchTy<'tcx> { - pub ty: Ty<'tcx>, - pub kind: NonStructuralMatchTyKind<'tcx>, -} - -#[derive(Debug)] -pub enum NonStructuralMatchTyKind<'tcx> { - Adt(AdtDef<'tcx>), - Param, - Dynamic, - Foreign, - Opaque, - Closure, - Generator, - Projection, - Float, -} - /// This method traverses the structure of `ty`, trying to find an /// instance of an ADT (i.e. struct or enum) that doesn't implement /// the structural-match traits, or a generic type parameter @@ -54,15 +35,28 @@ pub enum NonStructuralMatchTyKind<'tcx> { /// For more background on why Rust has this requirement, and issues /// that arose when the requirement was not enforced completely, see /// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307. -/// -/// The floats_allowed flag is used to deny constants in floating point pub fn search_for_structural_match_violation<'tcx>( span: Span, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, - floats_allowed: bool, -) -> Option<NonStructuralMatchTy<'tcx>> { - ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), floats_allowed }) +) -> Option<Ty<'tcx>> { + ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), adt_const_param: false }) + .break_value() +} + +/// This method traverses the structure of `ty`, trying to find any +/// types that are not allowed to be used in a const generic. +/// +/// This is either because the type does not implement `StructuralEq` +/// and `StructuralPartialEq`, or because the type is intentionally +/// not supported in const generics (such as floats and raw pointers, +/// which are allowed in match blocks). +pub fn search_for_adt_const_param_violation<'tcx>( + span: Span, + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, +) -> Option<Ty<'tcx>> { + ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), adt_const_param: true }) .break_value() } @@ -125,7 +119,10 @@ struct Search<'tcx> { /// we will not recur on them again. seen: FxHashSet<hir::def_id::DefId>, - floats_allowed: bool, + // Additionally deny things that have been allowed in patterns, + // but are not allowed in adt const params, such as floats and + // fn ptrs. + adt_const_param: bool, } impl<'tcx> Search<'tcx> { @@ -135,7 +132,7 @@ impl<'tcx> Search<'tcx> { } impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> { - type BreakTy = NonStructuralMatchTy<'tcx>; + type BreakTy = Ty<'tcx>; fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { debug!("Search visiting ty: {:?}", ty); @@ -143,51 +140,27 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> { let (adt_def, substs) = match *ty.kind() { ty::Adt(adt_def, substs) => (adt_def, substs), ty::Param(_) => { - let kind = NonStructuralMatchTyKind::Param; - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); + return ControlFlow::Break(ty); } ty::Dynamic(..) => { - let kind = NonStructuralMatchTyKind::Dynamic; - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); + return ControlFlow::Break(ty); } ty::Foreign(_) => { - let kind = NonStructuralMatchTyKind::Foreign; - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); + return ControlFlow::Break(ty); } ty::Opaque(..) => { - let kind = NonStructuralMatchTyKind::Opaque; - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); + return ControlFlow::Break(ty); } ty::Projection(..) => { - let kind = NonStructuralMatchTyKind::Projection; - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); + return ControlFlow::Break(ty); } ty::Closure(..) => { - let kind = NonStructuralMatchTyKind::Closure; - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); + return ControlFlow::Break(ty); } ty::Generator(..) | ty::GeneratorWitness(..) => { - let kind = NonStructuralMatchTyKind::Generator; - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); - } - ty::RawPtr(..) => { - // structural-match ignores substructure of - // `*const _`/`*mut _`, so skip `super_visit_with`. - // - // For example, if you have: - // ``` - // struct NonStructural; - // #[derive(PartialEq, Eq)] - // struct T(*const NonStructural); - // const C: T = T(std::ptr::null()); - // ``` - // - // Even though `NonStructural` does not implement `PartialEq`, - // structural equality on `T` does not recur into the raw - // pointer. Therefore, one can still use `C` in a pattern. - return ControlFlow::CONTINUE; + return ControlFlow::Break(ty); } - ty::FnDef(..) | ty::FnPtr(..) => { + ty::FnDef(..) => { // Types of formals and return in `fn(_) -> _` are also irrelevant; // so we do not recur into them via `super_visit_with` return ControlFlow::CONTINUE; @@ -206,14 +179,41 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> { return ControlFlow::CONTINUE; } + ty::FnPtr(..) => { + if !self.adt_const_param { + return ControlFlow::CONTINUE; + } else { + return ControlFlow::Break(ty); + } + } + + ty::RawPtr(..) => { + if !self.adt_const_param { + // structural-match ignores substructure of + // `*const _`/`*mut _`, so skip `super_visit_with`. + // + // For example, if you have: + // ``` + // struct NonStructural; + // #[derive(PartialEq, Eq)] + // struct T(*const NonStructural); + // const C: T = T(std::ptr::null()); + // ``` + // + // Even though `NonStructural` does not implement `PartialEq`, + // structural equality on `T` does not recur into the raw + // pointer. Therefore, one can still use `C` in a pattern. + return ControlFlow::CONTINUE; + } else { + return ControlFlow::Break(ty); + } + } + ty::Float(_) => { - if self.floats_allowed { + if !self.adt_const_param { return ControlFlow::CONTINUE; } else { - return ControlFlow::Break(NonStructuralMatchTy { - ty, - kind: NonStructuralMatchTyKind::Float, - }); + return ControlFlow::Break(ty); } } @@ -239,8 +239,7 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> { if !self.type_marked_structural(ty) { debug!("Search found ty: {:?}", ty); - let kind = NonStructuralMatchTyKind::Adt(adt_def); - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); + return ControlFlow::Break(ty); } // structural-match does not care about the diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 6b758ba63cd..414857f0acc 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -85,7 +85,7 @@ pub fn trait_obligations<'a, 'tcx>( infcx: &InferCtxt<'a, 'tcx>, param_env: ty::ParamEnv<'tcx>, body_id: hir::HirId, - trait_ref: &ty::TraitRef<'tcx>, + trait_pred: &ty::TraitPredicate<'tcx>, span: Span, item: &'tcx hir::Item<'tcx>, ) -> Vec<traits::PredicateObligation<'tcx>> { @@ -98,7 +98,7 @@ pub fn trait_obligations<'a, 'tcx>( recursion_depth: 0, item: Some(item), }; - wf.compute_trait_ref(trait_ref, Elaborate::All); + wf.compute_trait_pred(trait_pred, Elaborate::All); debug!(obligations = ?wf.out); wf.normalize(infcx) } @@ -123,7 +123,7 @@ pub fn predicate_obligations<'a, 'tcx>( // It's ok to skip the binder here because wf code is prepared for it match predicate.kind().skip_binder() { ty::PredicateKind::Trait(t) => { - wf.compute_trait_ref(&t.trait_ref, Elaborate::None); + wf.compute_trait_pred(&t, Elaborate::None); } ty::PredicateKind::RegionOutlives(..) => {} ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty, _reg)) => { @@ -301,11 +301,18 @@ impl<'tcx> WfPredicates<'tcx> { } /// Pushes the obligations required for `trait_ref` to be WF into `self.out`. - fn compute_trait_ref(&mut self, trait_ref: &ty::TraitRef<'tcx>, elaborate: Elaborate) { + fn compute_trait_pred(&mut self, trait_pred: &ty::TraitPredicate<'tcx>, elaborate: Elaborate) { let tcx = self.tcx; - let obligations = self.nominal_obligations(trait_ref.def_id, trait_ref.substs); + let trait_ref = &trait_pred.trait_ref; - debug!("compute_trait_ref obligations {:?}", obligations); + // if the trait predicate is not const, the wf obligations should not be const as well. + let obligations = if trait_pred.constness == ty::BoundConstness::NotConst { + self.nominal_obligations_without_const(trait_ref.def_id, trait_ref.substs) + } else { + self.nominal_obligations(trait_ref.def_id, trait_ref.substs) + }; + + debug!("compute_trait_pred obligations {:?}", obligations); let param_env = self.param_env; let depth = self.recursion_depth; @@ -685,10 +692,11 @@ impl<'tcx> WfPredicates<'tcx> { } #[instrument(level = "debug", skip(self))] - fn nominal_obligations( + fn nominal_obligations_inner( &mut self, def_id: DefId, substs: SubstsRef<'tcx>, + remap_constness: bool, ) -> Vec<traits::PredicateObligation<'tcx>> { let predicates = self.tcx.predicates_of(def_id); let mut origins = vec![def_id; predicates.predicates.len()]; @@ -703,19 +711,38 @@ impl<'tcx> WfPredicates<'tcx> { debug_assert_eq!(predicates.predicates.len(), origins.len()); iter::zip(iter::zip(predicates.predicates, predicates.spans), origins.into_iter().rev()) - .map(|((pred, span), origin_def_id)| { + .map(|((mut pred, span), origin_def_id)| { let code = if span.is_dummy() { traits::MiscObligation } else { traits::BindingObligation(origin_def_id, span) }; let cause = self.cause(code); + if remap_constness { + pred = pred.without_const(self.tcx); + } traits::Obligation::with_depth(cause, self.recursion_depth, self.param_env, pred) }) .filter(|pred| !pred.has_escaping_bound_vars()) .collect() } + fn nominal_obligations( + &mut self, + def_id: DefId, + substs: SubstsRef<'tcx>, + ) -> Vec<traits::PredicateObligation<'tcx>> { + self.nominal_obligations_inner(def_id, substs, false) + } + + fn nominal_obligations_without_const( + &mut self, + def_id: DefId, + substs: SubstsRef<'tcx>, + ) -> Vec<traits::PredicateObligation<'tcx>> { + self.nominal_obligations_inner(def_id, substs, true) + } + fn from_object_ty( &mut self, ty: Ty<'tcx>, diff --git a/compiler/rustc_traits/src/evaluate_obligation.rs b/compiler/rustc_traits/src/evaluate_obligation.rs index 3fc141471b9..49c9ba45963 100644 --- a/compiler/rustc_traits/src/evaluate_obligation.rs +++ b/compiler/rustc_traits/src/evaluate_obligation.rs @@ -1,4 +1,4 @@ -use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::infer::{DefiningAnchor, TyCtxtInferExt}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; use rustc_span::source_map::DUMMY_SP; @@ -16,7 +16,9 @@ fn evaluate_obligation<'tcx>( canonical_goal: CanonicalPredicateGoal<'tcx>, ) -> Result<EvaluationResult, OverflowError> { debug!("evaluate_obligation(canonical_goal={:#?})", canonical_goal); - tcx.infer_ctxt().enter_with_canonical( + // HACK This bubble is required for this tests to pass: + // impl-trait/issue99642.rs + tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bubble).enter_with_canonical( DUMMY_SP, &canonical_goal, |ref infcx, goal, _canonical_inference_vars| { diff --git a/compiler/rustc_traits/src/implied_outlives_bounds.rs b/compiler/rustc_traits/src/implied_outlives_bounds.rs index c7cac8fca89..4d4d55de5f4 100644 --- a/compiler/rustc_traits/src/implied_outlives_bounds.rs +++ b/compiler/rustc_traits/src/implied_outlives_bounds.rs @@ -47,7 +47,7 @@ fn compute_implied_outlives_bounds<'tcx>( // process it next. Because the resulting predicates aren't always // guaranteed to be a subset of the original type, so we need to store the // WF args we've computed in a set. - let mut checked_wf_args = rustc_data_structures::stable_set::FxHashSet::default(); + let mut checked_wf_args = rustc_data_structures::fx::FxHashSet::default(); let mut wf_args = vec![ty.into()]; let mut implied_bounds = vec![]; diff --git a/compiler/rustc_traits/src/type_op.rs b/compiler/rustc_traits/src/type_op.rs index f8bac1d7b26..d895b647db0 100644 --- a/compiler/rustc_traits/src/type_op.rs +++ b/compiler/rustc_traits/src/type_op.rs @@ -2,7 +2,7 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_infer::infer::at::ToTrace; use rustc_infer::infer::canonical::{Canonical, QueryResponse}; -use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; +use rustc_infer::infer::{DefiningAnchor, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::TraitEngineExt as _; use rustc_middle::ty::query::Providers; use rustc_middle::ty::subst::{GenericArg, Subst, UserSelfTy, UserSubsts}; @@ -258,10 +258,15 @@ fn type_op_prove_predicate<'tcx>( tcx: TyCtxt<'tcx>, canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, ProvePredicate<'tcx>>>, ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, ()>>, NoSolution> { - tcx.infer_ctxt().enter_canonical_trait_query(&canonicalized, |infcx, fulfill_cx, key| { - type_op_prove_predicate_with_cause(infcx, fulfill_cx, key, ObligationCause::dummy()); - Ok(()) - }) + // HACK This bubble is required for this test to pass: + // impl-trait/issue-99642.rs + tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bubble).enter_canonical_trait_query( + &canonicalized, + |infcx, fulfill_cx, key| { + type_op_prove_predicate_with_cause(infcx, fulfill_cx, key, ObligationCause::dummy()); + Ok(()) + }, + ) } /// The core of the `type_op_prove_predicate` query: for diagnostics purposes in NLL HRTB errors, diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 5e58f237982..979e997f244 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -280,6 +280,11 @@ fn resolve_associated_item<'tcx>( return Ok(None); } + // If the item does not have a value, then we cannot return an instance. + if !leaf_def.item.defaultness.has_value() { + return Ok(None); + } + let substs = tcx.erase_regions(substs); // Check if we just resolved an associated `const` declaration from diff --git a/compiler/rustc_ty_utils/src/representability.rs b/compiler/rustc_ty_utils/src/representability.rs index 7efc82efd15..eded7891682 100644 --- a/compiler/rustc_ty_utils/src/representability.rs +++ b/compiler/rustc_ty_utils/src/representability.rs @@ -1,5 +1,5 @@ //! Check whether a type is representable. -use rustc_data_structures::stable_map::FxHashMap; +use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Span; diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index d542a9b5997..b1af3051719 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -207,9 +207,14 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { constness, ); - let body_id = hir_id.map_or(hir::CRATE_HIR_ID, |id| { - tcx.hir().maybe_body_owned_by(id).map_or(id, |body| body.hir_id) - }); + let body_id = + local_did.and_then(|id| tcx.hir().maybe_body_owned_by(id).map(|body| body.hir_id)); + let body_id = match body_id { + Some(id) => id, + None if hir_id.is_some() => hir_id.unwrap(), + _ => hir::CRATE_HIR_ID, + }; + let cause = traits::ObligationCause::misc(tcx.def_span(def_id), body_id); traits::normalize_param_env_or_error(tcx, unnormalized_env, cause) } diff --git a/compiler/rustc_type_ir/src/codec.rs b/compiler/rustc_type_ir/src/codec.rs index 6a9ea790a30..ee249050cc6 100644 --- a/compiler/rustc_type_ir/src/codec.rs +++ b/compiler/rustc_type_ir/src/codec.rs @@ -1,6 +1,6 @@ use crate::Interner; -use rustc_data_structures::stable_map::FxHashMap; +use rustc_data_structures::fx::FxHashMap; use rustc_serialize::{Decoder, Encoder}; /// The shorthand encoding uses an enum's variant index `usize` diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index fd6376ef6ee..791e9e0f5a3 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -310,6 +310,7 @@ impl DebruijnIndex { /// for<'a> fn(for<'b> fn(&'a x)) /// /// you would need to shift the index for `'a` into a new binder. + #[inline] #[must_use] pub fn shifted_in(self, amount: u32) -> DebruijnIndex { DebruijnIndex::from_u32(self.as_u32() + amount) @@ -317,18 +318,21 @@ impl DebruijnIndex { /// Update this index in place by shifting it "in" through /// `amount` number of binders. + #[inline] pub fn shift_in(&mut self, amount: u32) { *self = self.shifted_in(amount); } /// Returns the resulting index when this value is moved out from /// `amount` number of new binders. + #[inline] #[must_use] pub fn shifted_out(self, amount: u32) -> DebruijnIndex { DebruijnIndex::from_u32(self.as_u32() - amount) } /// Update in place by shifting out from `amount` binders. + #[inline] pub fn shift_out(&mut self, amount: u32) { *self = self.shifted_out(amount); } @@ -353,6 +357,7 @@ impl DebruijnIndex { /// If we invoke `shift_out_to_binder` and the region is in fact /// bound by one of the binders we are shifting out of, that is an /// error (and should fail an assertion failure). + #[inline] pub fn shifted_out_to_binder(self, to_binder: DebruijnIndex) -> Self { self.shifted_out(to_binder.as_u32() - INNERMOST.as_u32()) } diff --git a/compiler/rustc_typeck/src/astconv/errors.rs b/compiler/rustc_typeck/src/astconv/errors.rs index c873cf27e42..99a8101dc96 100644 --- a/compiler/rustc_typeck/src/astconv/errors.rs +++ b/compiler/rustc_typeck/src/astconv/errors.rs @@ -8,7 +8,7 @@ use rustc_middle::ty; use rustc_session::parse::feature_err; use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::symbol::{sym, Ident}; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::{Span, Symbol, DUMMY_SP}; use std::collections::BTreeSet; @@ -17,7 +17,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// the type parameter's name as a placeholder. pub(crate) fn complain_about_missing_type_params( &self, - missing_type_params: Vec<String>, + missing_type_params: Vec<Symbol>, def_id: DefId, span: Span, empty_generic_args: bool, diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs index 612dc384521..40aa27a29e9 100644 --- a/compiler/rustc_typeck/src/astconv/generics.rs +++ b/compiler/rustc_typeck/src/astconv/generics.rs @@ -86,7 +86,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let param_type = tcx.infer_ctxt().enter(|infcx| { infcx.resolve_numeric_literals_with_default(tcx.type_of(param.def_id)) }); - if param_type.is_suggestable(tcx) { + if param_type.is_suggestable(tcx, false) { err.span_suggestion( tcx.def_span(src_def_id), "consider changing this type parameter to be a `const` generic", diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 9e4da058052..444f0fdd45a 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -221,14 +221,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { tcx.mk_region(ty::ReLateBound(debruijn, br)) } - Some(rl::Region::LateBoundAnon(debruijn, index, anon_index)) => { - let br = ty::BoundRegion { - var: ty::BoundVar::from_u32(index), - kind: ty::BrAnon(anon_index), - }; - tcx.mk_region(ty::ReLateBound(debruijn, br)) - } - Some(rl::Region::EarlyBound(index, id)) => { let name = lifetime_name(id.expect_local()); tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { def_id: id, index, name })) @@ -382,7 +374,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { def_id: DefId, generic_args: &'a GenericArgs<'a>, span: Span, - missing_type_params: Vec<String>, + missing_type_params: Vec<Symbol>, inferred_params: Vec<Span>, infer_args: bool, is_object: bool, @@ -514,7 +506,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // defaults. This will lead to an ICE if we are not // careful! if self.default_needs_object_self(param) { - self.missing_type_params.push(param.name.to_string()); + self.missing_type_params.push(param.name); tcx.ty_error().into() } else { // This is a default type parameter. @@ -1150,17 +1142,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .expect("missing associated type"); if !assoc_item.vis.is_accessible_from(def_scope, tcx) { - let kind = match assoc_item.kind { - ty::AssocKind::Type => "type", - ty::AssocKind::Const => "const", - _ => unreachable!(), - }; tcx.sess .struct_span_err( binding.span, - &format!("associated {kind} `{}` is private", binding.item_name), + &format!("{} `{}` is private", assoc_item.kind, binding.item_name), ) - .span_label(binding.span, &format!("private associated {kind}")) + .span_label(binding.span, &format!("private {}", assoc_item.kind)) .emit(); } tcx.check_stability(assoc_item.def_id, Some(hir_ref_id), binding.span, None); @@ -1247,7 +1234,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } match binding.kind { - ConvertedBindingKind::Equality(term) => { + ConvertedBindingKind::Equality(mut term) => { // "Desugar" a constraint like `T: Iterator<Item = u32>` this to // the "projection predicate" for: // @@ -1258,18 +1245,28 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { (hir::def::DefKind::AssocTy, ty::Term::Ty(_)) | (hir::def::DefKind::AssocConst, ty::Term::Const(_)) => (), (_, _) => { - let got = if let ty::Term::Ty(_) = term { "type" } else { "const" }; + let got = if let ty::Term::Ty(_) = term { "type" } else { "constant" }; let expected = def_kind.descr(assoc_item_def_id); tcx.sess .struct_span_err( binding.span, - &format!("mismatch in bind of {expected}, got {got}"), + &format!("expected {expected} bound, found {got}"), ) .span_note( tcx.def_span(assoc_item_def_id), - &format!("{expected} defined here does not match {got}"), + &format!("{expected} defined here"), ) .emit(); + term = match def_kind { + hir::def::DefKind::AssocTy => tcx.ty_error().into(), + hir::def::DefKind::AssocConst => tcx + .const_error( + tcx.bound_type_of(assoc_item_def_id) + .subst(tcx, projection_ty.skip_binder().substs), + ) + .into(), + _ => unreachable!(), + }; } } bounds.projection_bounds.push(( @@ -2676,7 +2673,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { span, ty, opt_sugg: Some((span, Applicability::MachineApplicable)) - .filter(|_| ty.is_suggestable(tcx)), + .filter(|_| ty.is_suggestable(tcx, false)), }); ty diff --git a/compiler/rustc_typeck/src/check/_match.rs b/compiler/rustc_typeck/src/check/_match.rs index c733f0d3c86..df315b8a3bc 100644 --- a/compiler/rustc_typeck/src/check/_match.rs +++ b/compiler/rustc_typeck/src/check/_match.rs @@ -9,7 +9,6 @@ use rustc_span::Span; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, - StatementAsExpression, }; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -40,8 +39,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let scrut_diverges = self.diverges.replace(Diverges::Maybe); // #55810: Type check patterns first so we get types for all bindings. + let scrut_span = scrut.span.find_ancestor_inside(expr.span).unwrap_or(scrut.span); for arm in arms { - self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut.span), true); + self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut_span), true); } // Now typecheck the blocks. @@ -75,8 +75,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let mut other_arms = vec![]; // Used only for diagnostics. - let mut prior_arm_ty = None; - for (i, arm) in arms.iter().enumerate() { + let mut prior_arm = None; + for arm in arms { if let Some(g) = &arm.guard { self.diverges.set(Diverges::Maybe); match g { @@ -96,21 +96,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let opt_suggest_box_span = self.opt_suggest_box_span(arm_ty, orig_expected); - let (arm_span, semi_span) = - self.get_appropriate_arm_semicolon_removal_span(&arms, i, prior_arm_ty, arm_ty); - let (span, code) = match i { + let (arm_block_id, arm_span) = if let hir::ExprKind::Block(blk, _) = arm.body.kind { + (Some(blk.hir_id), self.find_block_span(blk)) + } else { + (None, arm.body.span) + }; + + let (span, code) = match prior_arm { // The reason for the first arm to fail is not that the match arms diverge, // but rather that there's a prior obligation that doesn't hold. - 0 => (arm_span, ObligationCauseCode::BlockTailExpression(arm.body.hir_id)), - _ => ( + None => (arm_span, ObligationCauseCode::BlockTailExpression(arm.body.hir_id)), + Some((prior_arm_block_id, prior_arm_ty, prior_arm_span)) => ( expr.span, ObligationCauseCode::MatchExpressionArm(Box::new(MatchExpressionArmCause { + arm_block_id, arm_span, + arm_ty, + prior_arm_block_id, + prior_arm_ty, + prior_arm_span, scrut_span: scrut.span, - semi_span, source: match_src, prior_arms: other_arms.clone(), - last_ty: prior_arm_ty.unwrap(), scrut_hir_id: scrut.hir_id, opt_suggest_box_span, })), @@ -139,7 +146,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ret_ty = ret_coercion.borrow().expected_ty(); let ret_ty = self.inh.infcx.shallow_resolve(ret_ty); self.can_coerce(arm_ty, ret_ty) - && prior_arm_ty.map_or(true, |t| self.can_coerce(t, ret_ty)) + && prior_arm.map_or(true, |(_, t, _)| self.can_coerce(t, ret_ty)) // The match arms need to unify for the case of `impl Trait`. && !matches!(ret_ty.kind(), ty::Opaque(..)) } @@ -181,7 +188,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if other_arms.len() > 5 { other_arms.remove(0); } - prior_arm_ty = Some(arm_ty); + + prior_arm = Some((arm_block_id, arm_ty, arm_span)); } // If all of the arms in the `match` diverge, @@ -207,28 +215,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match_ty } - fn get_appropriate_arm_semicolon_removal_span( - &self, - arms: &'tcx [hir::Arm<'tcx>], - i: usize, - prior_arm_ty: Option<Ty<'tcx>>, - arm_ty: Ty<'tcx>, - ) -> (Span, Option<(Span, StatementAsExpression)>) { - let arm = &arms[i]; - let (arm_span, mut semi_span) = if let hir::ExprKind::Block(blk, _) = &arm.body.kind { - self.find_block_span(blk, prior_arm_ty) - } else { - (arm.body.span, None) - }; - if semi_span.is_none() && i > 0 { - if let hir::ExprKind::Block(blk, _) = &arms[i - 1].body.kind { - let (_, semi_span_prev) = self.find_block_span(blk, Some(arm_ty)); - semi_span = semi_span_prev; - } - } - (arm_span, semi_span) - } - /// When the previously checked expression (the scrutinee) diverges, /// warn the user about the match arms being unreachable. fn warn_arms_when_scrutinee_diverges(&self, arms: &'tcx [hir::Arm<'tcx>]) { @@ -313,7 +299,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { else_ty: Ty<'tcx>, opt_suggest_box_span: Option<Span>, ) -> ObligationCause<'tcx> { - let mut outer_sp = if self.tcx.sess.source_map().is_multiline(span) { + let mut outer_span = if self.tcx.sess.source_map().is_multiline(span) { // The `if`/`else` isn't in one line in the output, include some context to make it // clear it is an if/else expression: // ``` @@ -339,69 +325,67 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None }; - let mut remove_semicolon = None; - let error_sp = if let ExprKind::Block(block, _) = &else_expr.kind { - let (error_sp, semi_sp) = self.find_block_span(block, Some(then_ty)); - remove_semicolon = semi_sp; - if block.expr.is_none() && block.stmts.is_empty() { - // Avoid overlapping spans that aren't as readable: - // ``` - // 2 | let x = if true { - // | _____________- - // 3 | | 3 - // | | - expected because of this - // 4 | | } else { - // | |____________^ - // 5 | || - // 6 | || }; - // | || ^ - // | ||_____| - // | |______if and else have incompatible types - // | expected integer, found `()` - // ``` - // by not pointing at the entire expression: - // ``` - // 2 | let x = if true { - // | ------- `if` and `else` have incompatible types - // 3 | 3 - // | - expected because of this - // 4 | } else { - // | ____________^ - // 5 | | - // 6 | | }; - // | |_____^ expected integer, found `()` - // ``` - if outer_sp.is_some() { - outer_sp = Some(self.tcx.sess.source_map().guess_head_span(span)); - } + let (error_sp, else_id) = if let ExprKind::Block(block, _) = &else_expr.kind { + let block = block.innermost_block(); + + // Avoid overlapping spans that aren't as readable: + // ``` + // 2 | let x = if true { + // | _____________- + // 3 | | 3 + // | | - expected because of this + // 4 | | } else { + // | |____________^ + // 5 | || + // 6 | || }; + // | || ^ + // | ||_____| + // | |______if and else have incompatible types + // | expected integer, found `()` + // ``` + // by not pointing at the entire expression: + // ``` + // 2 | let x = if true { + // | ------- `if` and `else` have incompatible types + // 3 | 3 + // | - expected because of this + // 4 | } else { + // | ____________^ + // 5 | | + // 6 | | }; + // | |_____^ expected integer, found `()` + // ``` + if block.expr.is_none() && block.stmts.is_empty() + && let Some(outer_span) = &mut outer_span + { + *outer_span = self.tcx.sess.source_map().guess_head_span(*outer_span); } - error_sp + + (self.find_block_span(block), block.hir_id) } else { - // shouldn't happen unless the parser has done something weird - else_expr.span + (else_expr.span, else_expr.hir_id) }; - // Compute `Span` of `then` part of `if`-expression. - let then_sp = if let ExprKind::Block(block, _) = &then_expr.kind { - let (then_sp, semi_sp) = self.find_block_span(block, Some(else_ty)); - remove_semicolon = remove_semicolon.or(semi_sp); + let then_id = if let ExprKind::Block(block, _) = &then_expr.kind { + let block = block.innermost_block(); + // Exclude overlapping spans if block.expr.is_none() && block.stmts.is_empty() { - outer_sp = None; // same as in `error_sp`; cleanup output + outer_span = None; } - then_sp + block.hir_id } else { - // shouldn't happen unless the parser has done something weird - then_expr.span + then_expr.hir_id }; // Finally construct the cause: self.cause( error_sp, ObligationCauseCode::IfExpression(Box::new(IfExpressionCause { - then: then_sp, - else_sp: error_sp, - outer: outer_sp, - semicolon: remove_semicolon, + else_id, + then_id, + then_ty, + else_ty, + outer_span, opt_suggest_box_span, })), ) @@ -482,22 +466,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn find_block_span( - &self, - block: &'tcx hir::Block<'tcx>, - expected_ty: Option<Ty<'tcx>>, - ) -> (Span, Option<(Span, StatementAsExpression)>) { - if let Some(expr) = &block.expr { - (expr.span, None) - } else if let Some(stmt) = block.stmts.last() { - // possibly incorrect trailing `;` in the else arm - (stmt.span, expected_ty.and_then(|ty| self.could_remove_semicolon(block, ty))) - } else { - // empty block; point at its entirety - (block.span, None) - } - } - // When we have a `match` as a tail expression in a `fn` with a returned `impl Trait` // we check if the different arms would work with boxed trait objects instead and // provide a structured suggestion in that case. @@ -521,7 +489,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Binder::dummy(ty::PredicateKind::Trait(ty::TraitPredicate { trait_ref: ty::TraitRef { def_id: t.def_id(), - substs: self.infcx.tcx.mk_substs_trait(outer_ty, &[]), + substs: self.tcx.mk_substs_trait(outer_ty, &[]), }, constness: t.constness, polarity: t.polarity, @@ -529,9 +497,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let obl = Obligation::new( o.cause.clone(), self.param_env, - pred.to_predicate(self.infcx.tcx), + pred.to_predicate(self.tcx), ); - suggest_box &= self.infcx.predicate_must_hold_modulo_regions(&obl); + suggest_box &= self.predicate_must_hold_modulo_regions(&obl); if !suggest_box { // We've encountered some obligation that didn't hold, so the // return expression can't just be boxed. We don't need to diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_typeck/src/check/callee.rs index 00c8aa3a1bb..0836f15a122 100644 --- a/compiler/rustc_typeck/src/check/callee.rs +++ b/compiler/rustc_typeck/src/check/callee.rs @@ -376,7 +376,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.param_env, *predicate, ); - let result = self.infcx.evaluate_obligation(&obligation); + let result = self.evaluate_obligation(&obligation); self.tcx .sess .struct_span_err( diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_typeck/src/check/cast.rs index 66dd5582490..7aaddc2bd7a 100644 --- a/compiler/rustc_typeck/src/check/cast.rs +++ b/compiler/rustc_typeck/src/check/cast.rs @@ -69,7 +69,7 @@ enum PointerKind<'tcx> { /// No metadata attached, ie pointer to sized type or foreign type Thin, /// A trait object - Vtable(Option<DefId>), + VTable(Option<DefId>), /// Slice Length, /// The unsize info of this projection @@ -102,7 +102,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(match *t.kind() { ty::Slice(_) | ty::Str => Some(PointerKind::Length), - ty::Dynamic(ref tty, ..) => Some(PointerKind::Vtable(tty.principal_def_id())), + ty::Dynamic(ref tty, ..) => Some(PointerKind::VTable(tty.principal_def_id())), ty::Adt(def, substs) if def.is_struct() => match def.non_enum_variant().fields.last() { None => Some(PointerKind::Thin), Some(f) => { @@ -951,7 +951,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { match fcx.pointer_kind(m_cast.ty, self.span)? { None => Err(CastError::UnknownCastPtrKind), Some(PointerKind::Thin) => Ok(CastKind::AddrPtrCast), - Some(PointerKind::Vtable(_)) => Err(CastError::IntToFatCast(Some("a vtable"))), + Some(PointerKind::VTable(_)) => Err(CastError::IntToFatCast(Some("a vtable"))), Some(PointerKind::Length) => Err(CastError::IntToFatCast(Some("a length"))), Some( PointerKind::OfProjection(_) diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs index 69f3f03cfa9..9497d5c4528 100644 --- a/compiler/rustc_typeck/src/check/check.rs +++ b/compiler/rustc_typeck/src/check/check.rs @@ -14,7 +14,7 @@ use rustc_hir::lang_items::LangItem; use rustc_hir::{ItemKind, Node, PathSegment}; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt}; +use rustc_infer::infer::{DefiningAnchor, RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::Obligation; use rustc_lint::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS; use rustc_middle::hir::nested_filter; @@ -731,52 +731,52 @@ fn check_opaque_meets_bounds<'tcx>( }; let param_env = tcx.param_env(defining_use_anchor); - tcx.infer_ctxt().with_opaque_type_inference(defining_use_anchor).enter(move |infcx| { - let ocx = ObligationCtxt::new(&infcx); - let opaque_ty = tcx.mk_opaque(def_id.to_def_id(), substs); + tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bind(defining_use_anchor)).enter( + move |infcx| { + let ocx = ObligationCtxt::new(&infcx); + let opaque_ty = tcx.mk_opaque(def_id.to_def_id(), substs); - let misc_cause = traits::ObligationCause::misc(span, hir_id); + let misc_cause = traits::ObligationCause::misc(span, hir_id); - match infcx.at(&misc_cause, param_env).eq(opaque_ty, hidden_type) { - Ok(infer_ok) => ocx.register_infer_ok_obligations(infer_ok), - Err(ty_err) => { - tcx.sess.delay_span_bug( - span, - &format!("could not unify `{hidden_type}` with revealed type:\n{ty_err}"), - ); + match infcx.at(&misc_cause, param_env).eq(opaque_ty, hidden_type) { + Ok(infer_ok) => ocx.register_infer_ok_obligations(infer_ok), + Err(ty_err) => { + tcx.sess.delay_span_bug( + span, + &format!("could not unify `{hidden_type}` with revealed type:\n{ty_err}"), + ); + } } - } - // Additionally 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(hidden_type.into())).to_predicate(tcx); - ocx.register_obligation(Obligation::new(misc_cause, param_env, predicate)); - - // Check that all obligations are satisfied by the implementation's - // version. - let errors = ocx.select_all_or_error(); - if !errors.is_empty() { - infcx.report_fulfillment_errors(&errors, None, false); - } - - match origin { - // Checked when type checking the function containing them. - hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => {} - // Can have different predicates to their defining use - hir::OpaqueTyOrigin::TyAlias => { - let outlives_environment = OutlivesEnvironment::new(param_env); - infcx.check_region_obligations_and_report_errors( - defining_use_anchor, - &outlives_environment, - ); + // Additionally 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(hidden_type.into())) + .to_predicate(tcx); + ocx.register_obligation(Obligation::new(misc_cause, param_env, predicate)); + + // Check that all obligations are satisfied by the implementation's + // version. + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + infcx.report_fulfillment_errors(&errors, None, false); } - } - - // Clean up after ourselves - let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); - }); + match origin { + // Checked when type checking the function containing them. + hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => {} + // Can have different predicates to their defining use + hir::OpaqueTyOrigin::TyAlias => { + let outlives_environment = OutlivesEnvironment::new(param_env); + infcx.check_region_obligations_and_report_errors( + defining_use_anchor, + &outlives_environment, + ); + } + } + // Clean up after ourselves + let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); + }, + ); } fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) { diff --git a/compiler/rustc_typeck/src/check/closure.rs b/compiler/rustc_typeck/src/check/closure.rs index 1681e6af812..fee872155f5 100644 --- a/compiler/rustc_typeck/src/check/closure.rs +++ b/compiler/rustc_typeck/src/check/closure.rs @@ -96,7 +96,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.typeck_root_def_id(expr_def_id.to_def_id()), ); - let tupled_upvars_ty = self.infcx.next_ty_var(TypeVariableOrigin { + let tupled_upvars_ty = self.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::ClosureSynthetic, span: self.tcx.hir().span(expr.hir_id), }); @@ -141,7 +141,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Create a type variable (for now) to represent the closure kind. // It will be unified during the upvar inference phase (`upvar.rs`) - None => self.infcx.next_ty_var(TypeVariableOrigin { + None => self.next_ty_var(TypeVariableOrigin { // FIXME(eddyb) distinguish closure kind inference variables from the rest. kind: TypeVariableOriginKind::ClosureSynthetic, span: expr.span, @@ -182,9 +182,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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), - ), + pred.0 + .kind() + .rebind(pred.rebind(proj_predicate).subst(self.tcx, substs)), ), _ => None, }); @@ -531,7 +531,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // // [c1]: https://github.com/rust-lang/rust/pull/45072#issuecomment-341089706 // [c2]: https://github.com/rust-lang/rust/pull/45072#issuecomment-341096796 - self.infcx.commit_if_ok(|_| { + self.commit_if_ok(|_| { let mut all_obligations = vec![]; // The liberated version of this signature should be a subtype @@ -544,7 +544,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected_sigs.liberated_sig.inputs(), // `liberated_sig` is E'. ) { // Instantiate (this part of..) S to S', i.e., with fresh variables. - let supplied_ty = self.infcx.replace_bound_vars_with_fresh_vars( + let supplied_ty = self.replace_bound_vars_with_fresh_vars( hir_ty.span, LateBoundRegionConversionTime::FnCall, supplied_sig.inputs().rebind(supplied_ty), @@ -557,7 +557,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { all_obligations.extend(obligations); } - let supplied_output_ty = self.infcx.replace_bound_vars_with_fresh_vars( + let supplied_output_ty = self.replace_bound_vars_with_fresh_vars( decl.output.span(), LateBoundRegionConversionTime::FnCall, supplied_sig.output(), diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs index 9c9a2096ae9..639cab98f17 100644 --- a/compiler/rustc_typeck/src/check/coercion.rs +++ b/compiler/rustc_typeck/src/check/coercion.rs @@ -241,13 +241,13 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { make_adjustments: impl FnOnce(Ty<'tcx>) -> Vec<Adjustment<'tcx>>, ) -> CoerceResult<'tcx> { debug!("coerce_from_inference_variable(a={:?}, b={:?})", a, b); - assert!(a.is_ty_var() && self.infcx.shallow_resolve(a) == a); - assert!(self.infcx.shallow_resolve(b) == b); + assert!(a.is_ty_var() && self.shallow_resolve(a) == a); + assert!(self.shallow_resolve(b) == b); if b.is_ty_var() { // Two unresolved type variables: create a `Coerce` predicate. let target_ty = if self.use_lub { - self.infcx.next_ty_var(TypeVariableOrigin { + self.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::LatticeVariable, span: self.cause.span, }) @@ -991,7 +991,7 @@ impl<'a, 'tcx> FnCtxt<'a, '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()?, + self.tcx.lang_items().deref_mut_trait()?, expr_ty, ty::List::empty(), self.param_env, diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs index 3fb8e5080f3..e3ac23686b6 100644 --- a/compiler/rustc_typeck/src/check/compare_method.rs +++ b/compiler/rustc_typeck/src/check/compare_method.rs @@ -2,7 +2,7 @@ use super::potentially_plural_count; use crate::check::regionck::OutlivesEnvironmentExt; use crate::check::wfcheck; use crate::errors::LifetimesOrBoundsMismatchOnTrait; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; @@ -90,9 +90,10 @@ fn compare_predicate_entailment<'tcx>( let mut cause = ObligationCause::new( impl_m_span, impl_m_hir_id, - ObligationCauseCode::CompareImplMethodObligation { + ObligationCauseCode::CompareImplItemObligation { impl_item_def_id: impl_m.def_id.expect_local(), trait_item_def_id: trait_m.def_id, + kind: impl_m.kind, }, ); @@ -223,9 +224,10 @@ fn compare_predicate_entailment<'tcx>( let cause = ObligationCause::new( span, impl_m_hir_id, - ObligationCauseCode::CompareImplMethodObligation { + ObligationCauseCode::CompareImplItemObligation { impl_item_def_id: impl_m.def_id.expect_local(), trait_item_def_id: trait_m.def_id, + kind: impl_m.kind, }, ); ocx.register_obligation(traits::Obligation::new(cause, param_env, predicate)); @@ -1079,7 +1081,11 @@ pub(crate) fn compare_const_impl<'tcx>( let mut cause = ObligationCause::new( impl_c_span, impl_c_hir_id, - ObligationCauseCode::CompareImplConstObligation, + ObligationCauseCode::CompareImplItemObligation { + impl_item_def_id: impl_c.def_id.expect_local(), + trait_item_def_id: trait_c.def_id, + kind: impl_c.kind, + }, ); // There is no "body" here, so just pass dummy id. @@ -1148,8 +1154,10 @@ pub(crate) fn compare_const_impl<'tcx>( } let outlives_environment = OutlivesEnvironment::new(param_env); - infcx - .resolve_regions_and_report_errors(impl_c.def_id.expect_local(), &outlives_environment); + infcx.check_region_obligations_and_report_errors( + impl_c.def_id.expect_local(), + &outlives_environment, + ); }); } @@ -1212,15 +1220,6 @@ fn compare_type_predicate_entailment<'tcx>( // `ObligationCause` (and the `FnCtxt`). This is what // `regionck_item` expects. let impl_ty_hir_id = tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local()); - let cause = ObligationCause::new( - impl_ty_span, - impl_ty_hir_id, - ObligationCauseCode::CompareImplTypeObligation { - impl_item_def_id: impl_ty.def_id.expect_local(), - trait_item_def_id: trait_ty.def_id, - }, - ); - debug!("compare_type_predicate_entailment: trait_to_impl_substs={:?}", trait_to_impl_substs); // The predicates declared by the impl definition, the trait and the @@ -1239,7 +1238,7 @@ fn compare_type_predicate_entailment<'tcx>( Reveal::UserFacing, hir::Constness::NotConst, ); - let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause.clone()); + let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause); tcx.infer_ctxt().enter(|infcx| { let ocx = ObligationCtxt::new(&infcx); @@ -1247,12 +1246,25 @@ fn compare_type_predicate_entailment<'tcx>( let mut selcx = traits::SelectionContext::new(&infcx); - for predicate in impl_ty_own_bounds.predicates { + assert_eq!(impl_ty_own_bounds.predicates.len(), impl_ty_own_bounds.spans.len()); + for (span, predicate) in + std::iter::zip(impl_ty_own_bounds.spans, impl_ty_own_bounds.predicates) + { + let cause = ObligationCause::misc(span, impl_ty_hir_id); let traits::Normalized { value: predicate, obligations } = - traits::normalize(&mut selcx, param_env, normalize_cause.clone(), predicate); + traits::normalize(&mut selcx, param_env, cause, predicate); + let cause = ObligationCause::new( + span, + impl_ty_hir_id, + ObligationCauseCode::CompareImplItemObligation { + impl_item_def_id: impl_ty.def_id.expect_local(), + trait_item_def_id: trait_ty.def_id, + kind: impl_ty.kind, + }, + ); ocx.register_obligations(obligations); - ocx.register_obligation(traits::Obligation::new(cause.clone(), param_env, predicate)); + ocx.register_obligation(traits::Obligation::new(cause, param_env, predicate)); } // Check that all obligations are satisfied by the implementation's @@ -1460,6 +1472,7 @@ pub fn check_type_bounds<'tcx>( .map(|e| e.map_bound(|e| *e).transpose_tuple2()) .map(|(bound, span)| { debug!(?bound); + // this is where opaque type is found let concrete_ty_bound = bound.subst(tcx, rebased_substs); debug!("check_type_bounds: concrete_ty_bound = {:?}", concrete_ty_bound); @@ -1481,7 +1494,6 @@ pub fn check_type_bounds<'tcx>( ocx.register_obligations(obligations); ocx.register_obligation(obligation); } - // Check that all obligations are satisfied by the implementation's // version. let errors = ocx.select_all_or_error(); diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index a2d8765289c..58b0399c5c9 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -287,6 +287,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr_ty: Ty<'tcx>, ) { if let ty::Adt(expected_adt, substs) = expected.kind() { + if let hir::ExprKind::Field(base, ident) = expr.kind { + let base_ty = self.typeck_results.borrow().expr_ty(base); + if self.can_eq(self.param_env, base_ty, expected).is_ok() + && let Some(base_span) = base.span.find_ancestor_inside(expr.span) + { + err.span_suggestion_verbose( + expr.span.with_lo(base_span.hi()), + format!("consider removing the tuple struct field `{ident}`"), + "", + Applicability::MaybeIncorrect, + ); + return + } + } + // If the expression is of type () and it's the return expression of a block, // we suggest adding a separate return expression instead. // (To avoid things like suggesting `Ok(while .. { .. })`.) @@ -815,7 +830,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, ref expr), _, &ty::Ref(_, checked, _), - ) if self.infcx.can_sub(self.param_env, checked, expected).is_ok() => { + ) if self.can_sub(self.param_env, checked, expected).is_ok() => { // We have `&T`, check if what was expected was `T`. If so, // we may want to suggest removing a `&`. if sm.is_imported(expr.span) { @@ -959,7 +974,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // For this suggestion to make sense, the type would need to be `Copy`, // or we have to be moving out of a `Box<T>` - if self.infcx.type_is_copy_modulo_regions(self.param_env, expected, sp) + if self.type_is_copy_modulo_regions(self.param_env, expected, sp) // FIXME(compiler-errors): We can actually do this if the checked_ty is // `steps` layers of boxes, not just one, but this is easier and most likely. || (checked_ty.is_box() && steps == 1) diff --git a/compiler/rustc_typeck/src/check/dropck.rs b/compiler/rustc_typeck/src/check/dropck.rs index 72095c40807..321064ec0fc 100644 --- a/compiler/rustc_typeck/src/check/dropck.rs +++ b/compiler/rustc_typeck/src/check/dropck.rs @@ -318,8 +318,8 @@ impl<'tcx> TypeRelation<'tcx> for SimpleEqRelation<'tcx> { // Anonymizing the LBRs is necessary to solve (Issue #59497). // After we do so, it should be totally fine to skip the binders. - let anon_a = self.tcx.anonymize_late_bound_regions(a); - let anon_b = self.tcx.anonymize_late_bound_regions(b); + let anon_a = self.tcx.anonymize_bound_vars(a); + let anon_b = self.tcx.anonymize_bound_vars(b); self.relate(anon_a.skip_binder(), anon_b.skip_binder())?; Ok(a) diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 2d22e9bc76e..e20c6a2d99a 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -766,7 +766,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If this didn't hold, we would not have to report an error in // the first place. - assert_ne!(hir::HirId::make_owner(encl_item_id), encl_body_owner_id); + assert_ne!(encl_item_id, encl_body_owner_id); let encl_body_id = self.tcx.hir().body_owned_by(encl_body_owner_id); let encl_body = self.tcx.hir().body(encl_body_id); @@ -1803,7 +1803,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .source_map() .span_to_snippet(range_end.expr.span) .map(|s| format!(" from `{s}`")) - .unwrap_or(String::new()); + .unwrap_or_default(); err.span_suggestion( range_start.span.shrink_to_hi(), &format!("to set the remaining fields{instead}, separate the last named field with a comma"), @@ -2235,7 +2235,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { base: &'tcx hir::Expr<'tcx>, ty: Ty<'tcx>, ) { - let output_ty = match self.infcx.get_impl_future_output_ty(ty) { + let output_ty = match self.get_impl_future_output_ty(ty) { Some(output_ty) => self.resolve_vars_if_possible(output_ty), _ => return, }; @@ -2362,7 +2362,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false }; let expr_snippet = - self.tcx.sess.source_map().span_to_snippet(expr.span).unwrap_or(String::new()); + self.tcx.sess.source_map().span_to_snippet(expr.span).unwrap_or_default(); let is_wrapped = expr_snippet.starts_with('(') && expr_snippet.ends_with(')'); let after_open = expr.span.lo() + rustc_span::BytePos(1); let before_close = expr.span.hi() - rustc_span::BytePos(1); @@ -2648,6 +2648,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some((index_ty, element_ty)) => { // two-phase not needed because index_ty is never mutable self.demand_coerce(idx, idx_t, index_ty, None, AllowTwoPhase::No); + self.select_obligations_where_possible(false, |errors| { + self.point_at_index_if_possible(errors, idx.span) + }); element_ty } None => { @@ -2691,6 +2694,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + fn point_at_index_if_possible( + &self, + errors: &mut Vec<traits::FulfillmentError<'tcx>>, + span: Span, + ) { + for error in errors { + match error.obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Trait(predicate) + if self.tcx.is_diagnostic_item(sym::SliceIndex, predicate.trait_ref.def_id) => { + } + _ => continue, + } + error.obligation.cause.span = span; + } + } + fn check_expr_yield( &self, value: &'tcx hir::Expr<'tcx>, diff --git a/compiler/rustc_typeck/src/check/fallback.rs b/compiler/rustc_typeck/src/check/fallback.rs index 15788f410f1..4059b3403b1 100644 --- a/compiler/rustc_typeck/src/check/fallback.rs +++ b/compiler/rustc_typeck/src/check/fallback.rs @@ -1,9 +1,8 @@ use crate::check::FnCtxt; use rustc_data_structures::{ - fx::FxHashMap, + fx::{FxHashMap, FxHashSet}, graph::WithSuccessors, graph::{iterate::DepthFirstSearch, vec_graph::VecGraph}, - stable_set::FxHashSet, }; use rustc_middle::ty::{self, Ty}; @@ -219,9 +218,9 @@ impl<'tcx> FnCtxt<'_, 'tcx> { .diverging_type_vars .borrow() .iter() - .map(|&ty| self.infcx.shallow_resolve(ty)) + .map(|&ty| self.shallow_resolve(ty)) .filter_map(|ty| ty.ty_vid()) - .map(|vid| self.infcx.root_var(vid)) + .map(|vid| self.root_var(vid)) .collect(); debug!( "calculate_diverging_fallback: diverging_type_vars={:?}", @@ -237,7 +236,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { let mut diverging_vids = vec![]; let mut non_diverging_vids = vec![]; for unsolved_vid in unsolved_vids { - let root_vid = self.infcx.root_var(unsolved_vid); + let root_vid = self.root_var(unsolved_vid); debug!( "calculate_diverging_fallback: unsolved_vid={:?} root_vid={:?} diverges={:?}", unsolved_vid, @@ -272,7 +271,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // variables. (Note that this set consists of "root variables".) let mut roots_reachable_from_non_diverging = DepthFirstSearch::new(&coercion_graph); for &non_diverging_vid in &non_diverging_vids { - let root_vid = self.infcx.root_var(non_diverging_vid); + let root_vid = self.root_var(non_diverging_vid); if roots_reachable_from_diverging.visited(root_vid) { continue; } @@ -295,7 +294,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { diverging_fallback.reserve(diverging_vids.len()); for &diverging_vid in &diverging_vids { let diverging_ty = self.tcx.mk_ty_var(diverging_vid); - let root_vid = self.infcx.root_var(diverging_vid); + let root_vid = self.root_var(diverging_vid); let can_reach_non_diverging = coercion_graph .depth_first_search(root_vid) .any(|n| roots_reachable_from_non_diverging.visited(n)); @@ -303,7 +302,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { let mut relationship = ty::FoundRelationships { self_in_trait: false, output: false }; for (vid, rel) in relationships.iter() { - if self.infcx.root_var(*vid) == root_vid { + if self.root_var(*vid) == root_vid { relationship.self_in_trait |= rel.self_in_trait; relationship.output |= rel.output; } @@ -388,12 +387,12 @@ impl<'tcx> FnCtxt<'_, 'tcx> { }) .collect(); debug!("create_coercion_graph: coercion_edges={:?}", coercion_edges); - let num_ty_vars = self.infcx.num_ty_vars(); + let num_ty_vars = self.num_ty_vars(); VecGraph::new(num_ty_vars, coercion_edges) } /// If `ty` is an unresolved type variable, returns its root vid. fn root_vid(&self, ty: Ty<'tcx>) -> Option<ty::TyVid> { - Some(self.infcx.root_var(self.infcx.shallow_resolve(ty).ty_vid()?)) + Some(self.root_var(self.shallow_resolve(ty).ty_vid()?)) } } diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs index d079aeb4801..d1c10a3b63c 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs @@ -30,17 +30,15 @@ use rustc_middle::ty::{ }; use rustc_session::lint; use rustc_span::hygiene::DesugaringKind; -use rustc_span::source_map::{original_sp, DUMMY_SP}; use rustc_span::symbol::{kw, sym, Ident}; -use rustc_span::{self, BytePos, Span}; +use rustc_span::{Span, DUMMY_SP}; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; use rustc_trait_selection::traits::{ - self, ObligationCause, ObligationCauseCode, StatementAsExpression, TraitEngine, TraitEngineExt, + self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt, }; use std::collections::hash_map::Entry; -use std::iter; use std::slice; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -187,12 +185,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !method.substs.is_empty() { let method_generics = self.tcx.generics_of(method.def_id); if !method_generics.params.is_empty() { - let user_type_annotation = self.infcx.probe(|_| { + let user_type_annotation = self.probe(|_| { let user_substs = UserSubsts { substs: InternalSubsts::for_item(self.tcx, method.def_id, |param, _| { let i = param.index as usize; if i < method_generics.parent_count { - self.infcx.var_for_def(DUMMY_SP, param) + self.var_for_def(DUMMY_SP, param) } else { method.substs[i] } @@ -200,7 +198,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { user_self_ty: None, // not relevant here }; - self.infcx.canonicalize_user_type_annotation(UserType::TypeOf( + self.canonicalize_user_type_annotation(UserType::TypeOf( method.def_id, user_substs, )) @@ -238,7 +236,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("fcx {}", self.tag()); if Self::can_contain_user_lifetime_bounds((substs, user_self_ty)) { - let canonicalized = self.infcx.canonicalize_user_type_annotation(UserType::TypeOf( + let canonicalized = self.canonicalize_user_type_annotation(UserType::TypeOf( def_id, UserSubsts { substs, user_self_ty }, )); @@ -482,7 +480,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("to_ty_saving_user_provided_ty: ty={:?}", ty); if Self::can_contain_user_lifetime_bounds(ty) { - let c_ty = self.infcx.canonicalize_response(UserType::Ty(ty)); + let c_ty = self.canonicalize_response(UserType::Ty(ty)); debug!("to_ty_saving_user_provided_ty: c_ty={:?}", c_ty); self.typeck_results.borrow_mut().user_provided_types_mut().insert(ast_ty.hir_id, c_ty); } @@ -766,7 +764,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let ty::subst::GenericArgKind::Type(ty) = ty.unpack() && let ty::Opaque(def_id, _) = *ty.kind() && let Some(def_id) = def_id.as_local() - && self.infcx.opaque_type_origin(def_id, DUMMY_SP).is_some() { + && self.opaque_type_origin(def_id, DUMMY_SP).is_some() { return None; } } @@ -828,7 +826,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { self.tcx.bound_type_of(def_id) }; - let substs = self.infcx.fresh_substs_for_item(span, def_id); + let substs = self.fresh_substs_for_item(span, def_id); let ty = item_ty.subst(self.tcx, substs); self.write_resolution(hir_id, Ok((def_kind, def_id))); @@ -1059,84 +1057,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { )); } - pub(in super::super) fn could_remove_semicolon( - &self, - blk: &'tcx hir::Block<'tcx>, - expected_ty: Ty<'tcx>, - ) -> Option<(Span, StatementAsExpression)> { - // Be helpful when the user wrote `{... expr;}` and - // taking the `;` off is enough to fix the error. - let last_stmt = blk.stmts.last()?; - let hir::StmtKind::Semi(ref last_expr) = last_stmt.kind else { - return None; - }; - let last_expr_ty = self.node_ty(last_expr.hir_id); - let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) { - (ty::Opaque(last_def_id, _), ty::Opaque(exp_def_id, _)) - if last_def_id == exp_def_id => - { - StatementAsExpression::CorrectType - } - (ty::Opaque(last_def_id, last_bounds), ty::Opaque(exp_def_id, exp_bounds)) => { - debug!( - "both opaque, likely future {:?} {:?} {:?} {:?}", - last_def_id, last_bounds, exp_def_id, exp_bounds - ); - - let last_local_id = last_def_id.as_local()?; - let exp_local_id = exp_def_id.as_local()?; - - match ( - &self.tcx.hir().expect_item(last_local_id).kind, - &self.tcx.hir().expect_item(exp_local_id).kind, - ) { - ( - hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: last_bounds, .. }), - hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: exp_bounds, .. }), - ) if iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| { - match (left, right) { - ( - hir::GenericBound::Trait(tl, ml), - hir::GenericBound::Trait(tr, mr), - ) if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id() - && ml == mr => - { - true - } - ( - hir::GenericBound::LangItemTrait(langl, _, _, argsl), - hir::GenericBound::LangItemTrait(langr, _, _, argsr), - ) if langl == langr => { - // FIXME: consider the bounds! - debug!("{:?} {:?}", argsl, argsr); - true - } - _ => false, - } - }) => - { - StatementAsExpression::NeedsBoxing - } - _ => StatementAsExpression::CorrectType, - } - } - _ => StatementAsExpression::CorrectType, - }; - if (matches!(last_expr_ty.kind(), ty::Error(_)) - || self.can_sub(self.param_env, last_expr_ty, expected_ty).is_err()) - && matches!(needs_box, StatementAsExpression::CorrectType) - { - return None; - } - let span = if last_stmt.span.from_expansion() { - let mac_call = original_sp(last_stmt.span, blk.span); - self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)? - } else { - last_stmt.span.with_lo(last_stmt.span.hi() - BytePos(1)) - }; - Some((span, needs_box)) - } - // Instantiates the given path, which must refer to an item with the given // number of type parameters and type. #[instrument(skip(self, span), level = "debug")] diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index 41c38f558b6..33a3f825ac2 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -15,7 +15,7 @@ use crate::check::{ use crate::structured_errors::StructuredDiagnostic; use rustc_ast as ast; -use rustc_errors::{Applicability, Diagnostic, DiagnosticId, MultiSpan}; +use rustc_errors::{pluralize, Applicability, Diagnostic, DiagnosticId, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; @@ -31,9 +31,7 @@ use rustc_middle::ty::{self, DefIdTree, IsSuggestable, Ty}; use rustc_session::Session; use rustc_span::symbol::Ident; use rustc_span::{self, Span}; -use rustc_trait_selection::traits::{ - self, ObligationCauseCode, SelectionContext, StatementAsExpression, -}; +use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext}; use std::iter; use std::slice; @@ -60,7 +58,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("FnCtxt::check_asm: {} deferred checks", deferred_asm_checks.len()); for (asm, hir_id) in deferred_asm_checks.drain(..) { let enclosing_id = self.tcx.hir().enclosing_body_owner(hir_id); - InlineAsmCtxt::new_in_fn(self).check_asm(asm, enclosing_id); + InlineAsmCtxt::new_in_fn(self) + .check_asm(asm, self.tcx.hir().local_def_id_to_hir_id(enclosing_id)); } } @@ -483,6 +482,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.set_tainted_by_errors(); let tcx = self.tcx; + // Get the argument span in the context of the call span so that + // suggestions and labels are (more) correct when an arg is a + // macro invocation. + let normalize_span = |span: Span| -> Span { + let normalized_span = span.find_ancestor_inside(error_span).unwrap_or(span); + // Sometimes macros mess up the spans, so do not normalize the + // arg span to equal the error span, because that's less useful + // than pointing out the arg expr in the wrong context. + if normalized_span.source_equal(error_span) { span } else { normalized_span } + }; + // Precompute the provided types and spans, since that's all we typically need for below let provided_arg_tys: IndexVec<ProvidedIdx, (Ty<'tcx>, Span)> = provided_args .iter() @@ -492,7 +502,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .borrow() .expr_ty_adjusted_opt(*expr) .unwrap_or_else(|| tcx.ty_error()); - (self.resolve_vars_if_possible(ty), expr.span) + (self.resolve_vars_if_possible(ty), normalize_span(expr.span)) }) .collect(); let callee_expr = match &call_expr.peel_blocks().kind { @@ -575,6 +585,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If so, we might have just forgotten to wrap some args in a tuple. if let Some(ty::Tuple(tys)) = formal_and_expected_inputs.get(mismatch_idx.into()).map(|tys| tys.1.kind()) + // If the tuple is unit, we're not actually wrapping any arguments. + && !tys.is_empty() && provided_arg_tys.len() == formal_and_expected_inputs.len() - 1 + tys.len() { // Wrap up the N provided arguments starting at this position in a tuple. @@ -600,11 +612,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Take some care with spans, so we don't suggest wrapping a macro's // innards in parenthesis, for example. if satisfied - && let Some(lo) = - provided_args[mismatch_idx.into()].span.find_ancestor_inside(error_span) - && let Some(hi) = provided_args[(mismatch_idx + tys.len() - 1).into()] - .span - .find_ancestor_inside(error_span) + && let Some((_, lo)) = + provided_arg_tys.get(ProvidedIdx::from_usize(mismatch_idx)) + && let Some((_, hi)) = + provided_arg_tys.get(ProvidedIdx::from_usize(mismatch_idx + tys.len() - 1)) { let mut err; if tys.len() == 1 { @@ -612,7 +623,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // so don't do anything special here. err = self.report_and_explain_type_error( TypeTrace::types( - &self.misc(lo), + &self.misc(*lo), true, formal_and_expected_inputs[mismatch_idx.into()].1, provided_arg_tys[mismatch_idx.into()].0, @@ -635,7 +646,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "argument" ), potentially_plural_count(provided_args.len(), "argument"), - if provided_args.len() == 1 { "was" } else { "were" } + pluralize!("was", provided_args.len()) ), DiagnosticId::Error(err_code.to_owned()), ); @@ -760,7 +771,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if c_variadic { "at least " } else { "" }, potentially_plural_count(formal_and_expected_inputs.len(), "argument"), potentially_plural_count(provided_args.len(), "argument"), - if provided_args.len() == 1 { "was" } else { "were" } + pluralize!("was", provided_args.len()) ), DiagnosticId::Error(err_code.to_owned()), ) @@ -1052,7 +1063,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let suggestion_text = if let Some(provided_idx) = provided_idx && let (_, provided_span) = provided_arg_tys[*provided_idx] && let Ok(arg_text) = - source_map.span_to_snippet(provided_span.source_callsite()) + source_map.span_to_snippet(provided_span) { arg_text } else { @@ -1060,7 +1071,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (_, expected_ty) = formal_and_expected_inputs[expected_idx]; if expected_ty.is_unit() { "()".to_string() - } else if expected_ty.is_suggestable(tcx) { + } else if expected_ty.is_suggestable(tcx, false) { format!("/* {} */", expected_ty) } else { "/* value */".to_string() @@ -1224,7 +1235,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Does the expected pattern type originate from an expression and what is the span? let (origin_expr, ty_span) = match (decl.ty, decl.init) { (Some(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type. - (_, Some(init)) => (true, Some(init.span)), // No explicit type; so use the scrutinee. + (_, Some(init)) => { + (true, Some(init.span.find_ancestor_inside(decl.span).unwrap_or(init.span))) + } // No explicit type; so use the scrutinee. _ => (false, None), // We have `let $pat;`, so the expected type is unconstrained. }; @@ -1410,7 +1423,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self.misc(sp), &mut |err| { if let Some(expected_ty) = expected.only_has_type(self) { - self.consider_hint_about_removing_semicolon(blk, expected_ty, err); + if !self.consider_removing_semicolon(blk, expected_ty, err) { + self.consider_returning_binding(blk, expected_ty, err); + } if expected_ty == self.tcx.types.bool { // If this is caused by a missing `let` in a `while let`, // silence this redundant error, as we already emit E0070. @@ -1478,42 +1493,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty } - /// A common error is to add an extra semicolon: - /// - /// ```compile_fail,E0308 - /// fn foo() -> usize { - /// 22; - /// } - /// ``` - /// - /// This routine checks if the final statement in a block is an - /// expression with an explicit semicolon whose type is compatible - /// with `expected_ty`. If so, it suggests removing the semicolon. - fn consider_hint_about_removing_semicolon( - &self, - blk: &'tcx hir::Block<'tcx>, - expected_ty: Ty<'tcx>, - err: &mut Diagnostic, - ) { - if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) { - if let StatementAsExpression::NeedsBoxing = boxed { - err.span_suggestion_verbose( - span_semi, - "consider removing this semicolon and boxing the expression", - "", - Applicability::HasPlaceholders, - ); - } else { - err.span_suggestion_short( - span_semi, - "remove this semicolon", - "", - Applicability::MachineApplicable, - ); - } - } - } - fn parent_item_span(&self, id: hir::HirId) -> Option<Span> { let node = self.tcx.hir().get_by_def_id(self.tcx.hir().get_parent_item(id)); match node { @@ -1544,21 +1523,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// ``` fn get_expr_coercion_span(&self, expr: &hir::Expr<'_>) -> rustc_span::Span { let check_in_progress = |elem: &hir::Expr<'_>| { - self.in_progress_typeck_results - .and_then(|typeck_results| typeck_results.borrow().node_type_opt(elem.hir_id)) - .and_then(|ty| { - if ty.is_never() { - None - } else { - Some(match elem.kind { - // Point at the tail expression when possible. - hir::ExprKind::Block(block, _) => { - block.expr.map_or(block.span, |e| e.span) - } - _ => elem.span, - }) - } - }) + self.typeck_results.borrow().node_type_opt(elem.hir_id).filter(|ty| !ty.is_never()).map( + |_| match elem.kind { + // Point at the tail expression when possible. + hir::ExprKind::Block(block, _) => block.expr.map_or(block.span, |e| e.span), + _ => elem.span, + }, + ) }; if let hir::ExprKind::If(_, _, Some(el)) = expr.kind { @@ -1791,19 +1762,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .flat_map(|a| a.args.iter()) { if let hir::GenericArg::Type(hir_ty) = &arg { - if let hir::TyKind::Path(hir::QPath::TypeRelative(..)) = - &hir_ty.kind - { - // Avoid ICE with associated types. As this is best - // effort only, it's ok to ignore the case. It - // would trigger in `is_send::<T::AssocType>();` - // from `typeck-default-trait-impl-assoc-type.rs`. - } else { - let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, hir_ty); - let ty = self.resolve_vars_if_possible(ty); - if ty == predicate.self_ty() { - error.obligation.cause.span = hir_ty.span; - } + let ty = self.resolve_vars_if_possible( + self.typeck_results.borrow().node_type(hir_ty.hir_id), + ); + if ty == predicate.self_ty() { + error.obligation.cause.span = hir_ty.span; } } } diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs index 80feac18412..097fff6418e 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs @@ -11,12 +11,12 @@ use rustc_hir::{ Expr, ExprKind, GenericBound, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate, }; use rustc_infer::infer::{self, TyCtxtInferExt}; -use rustc_infer::traits; +use rustc_infer::traits::{self, StatementAsExpression}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Binder, IsSuggestable, Subst, ToPredicate, Ty}; use rustc_span::symbol::sym; use rustc_span::Span; -use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn suggest_semicolon_at_end(&self, span: Span, err: &mut Diagnostic) { @@ -506,7 +506,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(found)); // Only suggest changing the return type for methods that // haven't set a return type at all (and aren't `fn main()` or an impl). - match (&fn_decl.output, found.is_suggestable(self.tcx), can_suggest, expected.is_unit()) { + match ( + &fn_decl.output, + found.is_suggestable(self.tcx, false), + can_suggest, + expected.is_unit(), + ) { (&hir::FnRetTy::DefaultReturn(span), true, true, true) => { err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found }); true @@ -864,4 +869,43 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } } + + /// A common error is to add an extra semicolon: + /// + /// ```compile_fail,E0308 + /// fn foo() -> usize { + /// 22; + /// } + /// ``` + /// + /// This routine checks if the final statement in a block is an + /// expression with an explicit semicolon whose type is compatible + /// with `expected_ty`. If so, it suggests removing the semicolon. + pub(crate) fn consider_removing_semicolon( + &self, + blk: &'tcx hir::Block<'tcx>, + expected_ty: Ty<'tcx>, + err: &mut Diagnostic, + ) -> bool { + if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) { + if let StatementAsExpression::NeedsBoxing = boxed { + err.span_suggestion_verbose( + span_semi, + "consider removing this semicolon and boxing the expression", + "", + Applicability::HasPlaceholders, + ); + } else { + err.span_suggestion_short( + span_semi, + "remove this semicolon", + "", + Applicability::MachineApplicable, + ); + } + true + } else { + 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 14b226d91cb..518cd734236 100644 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs +++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs @@ -17,8 +17,7 @@ use self::record_consumed_borrow::find_consumed_and_borrowed; use crate::check::FnCtxt; use hir::def_id::DefId; use hir::{Body, HirId, HirIdMap, Node}; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_index::bit_set::BitSet; use rustc_index::vec::IndexVec; 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 da2db3f2e30..a2c23db162b 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 @@ -6,7 +6,7 @@ use hir::{ intravisit::{self, Visitor}, Body, Expr, ExprKind, Guard, HirId, LoopIdError, }; -use rustc_data_structures::{fx::FxHashMap, stable_set::FxHashSet}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_index::vec::IndexVec; use rustc_middle::{ 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 67cc46f21f0..ded0888c33e 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 @@ -4,7 +4,7 @@ use crate::{ expr_use_visitor::{self, ExprUseVisitor}, }; use hir::{def_id::DefId, Body, HirId, HirIdMap}; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_middle::hir::place::{PlaceBase, Projection, ProjectionKind}; use rustc_middle::ty::{ParamEnv, TyCtxt}; @@ -72,9 +72,8 @@ impl<'tcx> ExprUseDelegate<'tcx> { } fn mark_consumed(&mut self, consumer: HirId, target: TrackedValue) { - if !self.places.consumed.contains_key(&consumer) { - self.places.consumed.insert(consumer, <_>::default()); - } + self.places.consumed.entry(consumer).or_insert_with(|| <_>::default()); + debug!(?consumer, ?target, "mark_consumed"); self.places.consumed.get_mut(&consumer).map(|places| places.insert(target)); } diff --git a/compiler/rustc_typeck/src/check/inherited.rs b/compiler/rustc_typeck/src/check/inherited.rs index 4afbc00b37c..99895dc1224 100644 --- a/compiler/rustc_typeck/src/check/inherited.rs +++ b/compiler/rustc_typeck/src/check/inherited.rs @@ -1,5 +1,4 @@ use super::callee::DeferredCallResolution; -use super::MaybeInProgressTables; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; @@ -29,7 +28,7 @@ use std::ops::Deref; pub struct Inherited<'a, 'tcx> { pub(super) infcx: InferCtxt<'a, 'tcx>, - pub(super) typeck_results: super::MaybeInProgressTables<'a, 'tcx>, + pub(super) typeck_results: &'a RefCell<ty::TypeckResults<'tcx>>, pub(super) locals: RefCell<HirIdMap<super::LocalTy<'tcx>>>, @@ -86,7 +85,10 @@ impl<'tcx> Inherited<'_, 'tcx> { let hir_owner = tcx.hir().local_def_id_to_hir_id(def_id).owner; InheritedBuilder { - infcx: tcx.infer_ctxt().with_fresh_in_progress_typeck_results(hir_owner), + infcx: tcx + .infer_ctxt() + .ignoring_regions() + .with_fresh_in_progress_typeck_results(hir_owner), def_id, } } @@ -105,15 +107,14 @@ impl<'tcx> InheritedBuilder<'tcx> { impl<'a, 'tcx> Inherited<'a, 'tcx> { fn new(infcx: InferCtxt<'a, 'tcx>, def_id: LocalDefId) -> Self { let tcx = infcx.tcx; - let item_id = tcx.hir().local_def_id_to_hir_id(def_id); - let body_id = tcx.hir().maybe_body_owned_by(item_id); + let body_id = tcx.hir().maybe_body_owned_by(def_id); + let typeck_results = + infcx.in_progress_typeck_results.expect("building `FnCtxt` without typeck results"); Inherited { - typeck_results: MaybeInProgressTables { - maybe_typeck_results: infcx.in_progress_typeck_results, - }, + typeck_results, infcx, - fulfillment_cx: RefCell::new(<dyn TraitEngine<'_>>::new_ignoring_regions(tcx)), + fulfillment_cx: RefCell::new(<dyn TraitEngine<'_>>::new(tcx)), locals: RefCell::new(Default::default()), deferred_sized_obligations: RefCell::new(Vec::new()), deferred_call_resolutions: RefCell::new(Default::default()), diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs index 7fe710cf8f4..3f2a0da8d65 100644 --- a/compiler/rustc_typeck/src/check/intrinsic.rs +++ b/compiler/rustc_typeck/src/check/intrinsic.rs @@ -400,6 +400,10 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { sym::const_eval_select => (4, vec![param(0), param(1), param(2)], param(3)), + sym::vtable_size | sym::vtable_align => { + (0, vec![tcx.mk_imm_ptr(tcx.mk_unit())], tcx.types.usize) + } + other => { tcx.sess.emit_err(UnrecognizedIntrinsicFunction { span: it.span, name: other }); return; diff --git a/compiler/rustc_typeck/src/check/intrinsicck.rs b/compiler/rustc_typeck/src/check/intrinsicck.rs index 0adf0d28aae..df94abbafb1 100644 --- a/compiler/rustc_typeck/src/check/intrinsicck.rs +++ b/compiler/rustc_typeck/src/check/intrinsicck.rs @@ -1,5 +1,5 @@ use rustc_ast::InlineAsmTemplatePiece; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_index::vec::Idx; diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs index 416d33c7aa0..8f5f3657fc9 100644 --- a/compiler/rustc_typeck/src/check/method/probe.rs +++ b/compiler/rustc_typeck/src/check/method/probe.rs @@ -343,7 +343,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { OP: FnOnce(ProbeContext<'a, 'tcx>) -> Result<R, MethodError<'tcx>>, { let mut orig_values = OriginalQueryValues::default(); - let param_env_and_self_ty = self.infcx.canonicalize_query( + let param_env_and_self_ty = self.canonicalize_query( ParamEnvAnd { param_env: self.param_env, value: self_ty }, &mut orig_values, ); @@ -351,7 +351,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let steps = if mode == Mode::MethodCall { self.tcx.method_autoderef_steps(param_env_and_self_ty) } else { - self.infcx.probe(|_| { + self.probe(|_| { // Mode::Path - the deref steps is "trivial". This turns // our CanonicalQuery into a "trivial" QueryResponse. This // is a bit inefficient, but I don't think that writing diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index 7f96e421a9a..56fcd9e0a89 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -865,27 +865,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .join("\n"); let actual_prefix = actual.prefix_string(self.tcx); info!("unimplemented_traits.len() == {}", unimplemented_traits.len()); - let (primary_message, label) = if unimplemented_traits.len() == 1 - && unimplemented_traits_only - { - unimplemented_traits - .into_iter() - .next() - .map(|(_, (trait_ref, obligation))| { - if trait_ref.self_ty().references_error() - || actual.references_error() - { - // Avoid crashing. - return (None, None); - } - let OnUnimplementedNote { message, label, .. } = - self.infcx.on_unimplemented_note(trait_ref, &obligation); - (message, label) - }) - .unwrap_or((None, None)) - } else { - (None, None) - }; + let (primary_message, label) = + if unimplemented_traits.len() == 1 && unimplemented_traits_only { + unimplemented_traits + .into_iter() + .next() + .map(|(_, (trait_ref, obligation))| { + if trait_ref.self_ty().references_error() + || actual.references_error() + { + // Avoid crashing. + return (None, None); + } + let OnUnimplementedNote { message, label, .. } = + self.on_unimplemented_note(trait_ref, &obligation); + (message, label) + }) + .unwrap_or((None, None)) + } else { + (None, None) + }; let primary_message = primary_message.unwrap_or_else(|| format!( "the {item_kind} `{item_name}` exists for {actual_prefix} `{ty_str}`, but its trait bounds were not satisfied" )); @@ -1123,7 +1122,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { add a `use` for {one_of_them}:", an = if candidates.len() == 1 { "an" } else { "" }, s = pluralize!(candidates.len()), - were = if candidates.len() == 1 { "was" } else { "were" }, + were = pluralize!("was", candidates.len()), one_of_them = if candidates.len() == 1 { "it" } else { "one_of_them" }, ); self.suggest_use_candidates(&mut err, help, candidates); @@ -1648,7 +1647,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call: &hir::Expr<'_>, span: Span, ) { - let output_ty = match self.infcx.get_impl_future_output_ty(ty) { + let output_ty = match self.get_impl_future_output_ty(ty) { Some(output_ty) => self.resolve_vars_if_possible(output_ty).skip_binder(), _ => return, }; diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index 2b037c3fd2b..17c2e4868aa 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -87,10 +87,10 @@ mod op; mod pat; mod place_op; mod region; -mod regionck; +pub mod regionck; pub mod rvalue_scopes; mod upvar; -mod wfcheck; +pub mod wfcheck; pub mod writeback; use check::{check_abi, check_fn, check_mod_item_types}; @@ -128,8 +128,7 @@ use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; use rustc_trait_selection::traits::error_reporting::recursive_type_with_infinite_size_error; use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor; - -use std::cell::{Ref, RefCell, RefMut}; +use std::cell::RefCell; use crate::require_c_abi_if_c_variadic; use crate::util::common::indenter; @@ -900,32 +899,6 @@ enum TupleArgumentsFlag { TupleArguments, } -/// A wrapper for `InferCtxt`'s `in_progress_typeck_results` field. -#[derive(Copy, Clone)] -struct MaybeInProgressTables<'a, 'tcx> { - maybe_typeck_results: Option<&'a RefCell<ty::TypeckResults<'tcx>>>, -} - -impl<'a, 'tcx> MaybeInProgressTables<'a, 'tcx> { - fn borrow(self) -> Ref<'a, ty::TypeckResults<'tcx>> { - match self.maybe_typeck_results { - Some(typeck_results) => typeck_results.borrow(), - None => bug!( - "MaybeInProgressTables: inh/fcx.typeck_results.borrow() with no typeck results" - ), - } - } - - fn borrow_mut(self) -> RefMut<'a, ty::TypeckResults<'tcx>> { - match self.maybe_typeck_results { - Some(typeck_results) => typeck_results.borrow_mut(), - None => bug!( - "MaybeInProgressTables: inh/fcx.typeck_results.borrow_mut() with no typeck results" - ), - } - } -} - 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 a2daf6886f1..920b3e68808 100644 --- a/compiler/rustc_typeck/src/check/op.rs +++ b/compiler/rustc_typeck/src/check/op.rs @@ -475,7 +475,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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) { + if self.type_is_copy_modulo_regions(self.param_env, *lhs_deref_ty, lhs_expr.span) { suggest_deref_binop(*lhs_deref_ty); } } @@ -523,7 +523,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => None, }; - self.infcx.suggest_restricting_param_bound( + self.suggest_restricting_param_bound( &mut err, trait_pred, proj_pred, @@ -630,18 +630,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let rm_borrow_msg = "remove the borrow to obtain an owned `String`"; let to_owned_msg = "create an owned `String` from a string reference"; - let string_type = self.tcx.get_diagnostic_item(sym::String); - let is_std_string = |ty: Ty<'tcx>| match ty.ty_adt_def() { - Some(ty_def) => Some(ty_def.did()) == string_type, - None => false, + let is_std_string = |ty: Ty<'tcx>| { + ty.ty_adt_def() + .map_or(false, |ty_def| self.tcx.is_diagnostic_item(sym::String, ty_def.did())) }; match (lhs_ty.kind(), rhs_ty.kind()) { (&Ref(_, l_ty, _), &Ref(_, r_ty, _)) // &str or &String + &str, &String or &&str - if (*l_ty.kind() == Str || is_std_string(l_ty)) && ( - *r_ty.kind() == Str || is_std_string(r_ty) || - &format!("{:?}", rhs_ty) == "&&str" - ) => + if (*l_ty.kind() == Str || is_std_string(l_ty)) + && (*r_ty.kind() == Str + || is_std_string(r_ty) + || matches!( + r_ty.kind(), Ref(_, inner_ty, _) if *inner_ty.kind() == Str + )) => { if let IsAssign::No = is_assign { // Do not supply this message if `&str += &str` err.span_label(op.span, "`+` cannot be used to concatenate two `&str` strings"); @@ -739,7 +740,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { error.obligation.predicate.to_opt_poly_trait_pred() }); for pred in predicates { - self.infcx.suggest_restricting_param_bound( + self.suggest_restricting_param_bound( &mut err, pred, None, diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index c7318cd6e53..837c323553c 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -72,22 +72,6 @@ struct TopInfo<'tcx> { /// found type `std::result::Result<_, _>` /// ``` span: Option<Span>, - /// This refers to the parent pattern. Used to provide extra diagnostic information on errors. - /// ```text - /// error[E0308]: mismatched types - /// --> $DIR/const-in-struct-pat.rs:8:17 - /// | - /// L | struct f; - /// | --------- unit struct defined here - /// ... - /// L | let Thing { f } = t; - /// | ^ - /// | | - /// | expected struct `std::string::String`, found struct `f` - /// | `f` is interpreted as a unit struct, not a new binding - /// | help: bind the struct field to a different name instead: `f: other_f` - /// ``` - parent_pat: Option<&'tcx Pat<'tcx>>, } impl<'tcx> FnCtxt<'_, 'tcx> { @@ -147,7 +131,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Option<Span>, origin_expr: bool, ) { - let info = TopInfo { expected, origin_expr, span, parent_pat: None }; + let info = TopInfo { expected, origin_expr, span }; self.check_pat(pat, expected, INITIAL_BM, info); } @@ -190,9 +174,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_pat_struct(pat, qpath, fields, has_rest_pat, expected, def_bm, ti) } PatKind::Or(pats) => { - let parent_pat = Some(pat); for pat in pats { - self.check_pat(pat, expected, def_bm, TopInfo { parent_pat, ..ti }); + self.check_pat(pat, expected, def_bm, ti); } expected } @@ -621,7 +604,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if let Some(p) = sub { - self.check_pat(p, expected, def_bm, TopInfo { parent_pat: Some(pat), ..ti }); + self.check_pat(p, expected, def_bm, ti); } local_ty @@ -782,7 +765,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let Some((variant, pat_ty)) = self.check_struct_path(qpath, pat.hir_id) else { let err = self.tcx.ty_error(); for field in fields { - let ti = TopInfo { parent_pat: Some(pat), ..ti }; + let ti = ti; self.check_pat(field.pat, err, def_bm, ti); } return err; @@ -799,11 +782,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn check_pat_path<'b>( + fn check_pat_path( &self, - pat: &Pat<'_>, + pat: &Pat<'tcx>, qpath: &hir::QPath<'_>, - path_resolution: (Res, Option<Ty<'tcx>>, &'b [hir::PathSegment<'b>]), + path_resolution: (Res, Option<Ty<'tcx>>, &'tcx [hir::PathSegment<'tcx>]), expected: Ty<'tcx>, ti: TopInfo<'tcx>, ) -> Ty<'tcx> { @@ -837,7 +820,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(err) = self.demand_suptype_with_origin(&self.pattern_cause(ti, pat.span), expected, pat_ty) { - self.emit_bad_pat_path(err, pat.span, res, pat_res, pat_ty, segments, ti.parent_pat); + self.emit_bad_pat_path(err, pat, res, pat_res, pat_ty, segments); } pat_ty } @@ -876,16 +859,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } - fn emit_bad_pat_path<'b>( + fn emit_bad_pat_path( &self, mut e: DiagnosticBuilder<'_, ErrorGuaranteed>, - pat_span: Span, + pat: &hir::Pat<'tcx>, res: Res, pat_res: Res, pat_ty: Ty<'tcx>, - segments: &'b [hir::PathSegment<'b>], - parent_pat: Option<&Pat<'_>>, + segments: &'tcx [hir::PathSegment<'tcx>], ) { + let pat_span = pat.span; if let Some(span) = self.tcx.hir().res_span(pat_res) { e.span_label(span, &format!("{} defined here", res.descr())); if let [hir::PathSegment { ident, .. }] = &*segments { @@ -898,8 +881,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { res.descr(), ), ); - match parent_pat { - Some(Pat { kind: hir::PatKind::Struct(..), .. }) => { + match self.tcx.hir().get(self.tcx.hir().get_parent_node(pat.hir_id)) { + hir::Node::Pat(Pat { kind: hir::PatKind::Struct(..), .. }) => { e.span_suggestion_verbose( ident.span.shrink_to_hi(), "bind the struct field to a different name instead", @@ -960,9 +943,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Ty<'tcx> { let tcx = self.tcx; let on_error = || { - let parent_pat = Some(pat); for pat in subpats { - self.check_pat(pat, tcx.ty_error(), def_bm, TopInfo { parent_pat, ..ti }); + self.check_pat(pat, tcx.ty_error(), def_bm, ti); } }; let report_unexpected_res = |res: Res| { @@ -1046,7 +1028,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; for (i, subpat) in subpats.iter().enumerate_and_adjust(variant.fields.len(), ddpos) { let field_ty = self.field_ty(subpat.span, &variant.fields[i], substs); - self.check_pat(subpat, field_ty, def_bm, TopInfo { parent_pat: Some(pat), ..ti }); + self.check_pat(subpat, field_ty, def_bm, ti); self.tcx.check_stability( variant.fields[i].did, @@ -1324,7 +1306,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; - self.check_pat(field.pat, field_ty, def_bm, TopInfo { parent_pat: Some(pat), ..ti }); + self.check_pat(field.pat, field_ty, def_bm, ti); } let mut unmentioned_fields = variant @@ -1936,7 +1918,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let err = tcx.ty_error(); (err, err) }; - self.check_pat(inner, inner_ty, def_bm, TopInfo { parent_pat: Some(pat), ..ti }); + self.check_pat(inner, inner_ty, def_bm, ti); rptr_ty } diff --git a/compiler/rustc_typeck/src/check/region.rs b/compiler/rustc_typeck/src/check/region.rs index 0c33a243e10..0081e9049ee 100644 --- a/compiler/rustc_typeck/src/check/region.rs +++ b/compiler/rustc_typeck/src/check/region.rs @@ -814,8 +814,7 @@ pub fn region_scope_tree(tcx: TyCtxt<'_>, def_id: DefId) -> &ScopeTree { return tcx.region_scope_tree(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) { + let scope_tree = if let Some(body_id) = tcx.hir().maybe_body_owned_by(def_id.expect_local()) { let mut visitor = RegionResolutionVisitor { tcx, scope_tree: ScopeTree::default(), diff --git a/compiler/rustc_typeck/src/check/regionck.rs b/compiler/rustc_typeck/src/check/regionck.rs index 1c3c5f999bc..d49a6138f7a 100644 --- a/compiler/rustc_typeck/src/check/regionck.rs +++ b/compiler/rustc_typeck/src/check/regionck.rs @@ -1,5 +1,5 @@ use crate::outlives::outlives_bounds::InferCtxtExt as _; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::InferCtxt; diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 3bd3e2d8091..d72e215934a 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -49,8 +49,7 @@ use rustc_span::sym; use rustc_span::{BytePos, Pos, Span, Symbol}; use rustc_trait_selection::infer::InferCtxtExt; -use rustc_data_structures::stable_map::FxHashMap; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_index::vec::Idx; use rustc_target::abi::VariantIdx; @@ -949,7 +948,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let root_var_min_capture_list = min_captures.and_then(|m| m.get(&var_hir_id))?; - let ty = self.infcx.resolve_vars_if_possible(self.node_ty(var_hir_id)); + let ty = self.resolve_vars_if_possible(self.node_ty(var_hir_id)); let ty = match closure_clause { hir::CaptureBy::Value => ty, // For move closure the capture kind should be by value @@ -1065,7 +1064,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { closure_clause: hir::CaptureBy, var_hir_id: hir::HirId, ) -> Option<FxHashSet<UpvarMigrationInfo>> { - let ty = self.infcx.resolve_vars_if_possible(self.node_ty(var_hir_id)); + let ty = self.resolve_vars_if_possible(self.node_ty(var_hir_id)); if !ty.has_significant_drop(self.tcx, self.tcx.param_env(closure_def_id.expect_local())) { debug!("does not have significant drop"); diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index 6df59ea1096..faab862cc3c 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -2,14 +2,13 @@ use crate::check::regionck::OutlivesEnvironmentExt; use crate::constrained_generic_params::{identify_constrained_generic_params, Parameter}; use rustc_ast as ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed}; +use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; use rustc_hir::ItemKind; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; +use rustc_infer::infer::outlives::env::{OutlivesEnvironment, RegionBoundPairs}; use rustc_infer::infer::outlives::obligations::TypeOutlives; -use rustc_infer::infer::region_constraints::GenericKind; use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::Normalized; use rustc_middle::ty::query::Providers; @@ -183,7 +182,7 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) { // We match on both `ty::ImplPolarity` and `ast::ImplPolarity` just to get the `!` span. match (tcx.impl_polarity(def_id), impl_.polarity) { (ty::ImplPolarity::Positive, _) => { - check_impl(tcx, item, impl_.self_ty, &impl_.of_trait); + check_impl(tcx, item, impl_.self_ty, &impl_.of_trait, impl_.constness); } (ty::ImplPolarity::Negative, ast::ImplPolarity::Negative(span)) => { // FIXME(#27579): what amount of WF checking do we need for neg impls? @@ -474,7 +473,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe unsatisfied_bounds.sort(); if !unsatisfied_bounds.is_empty() { - let plural = if unsatisfied_bounds.len() > 1 { "s" } else { "" }; + let plural = pluralize!(unsatisfied_bounds.len()); let mut err = tcx.sess.struct_span_err( gat_item_hir.span, &format!("missing required bound{} on `{}`", plural, gat_item_hir.ident), @@ -689,10 +688,7 @@ fn resolve_regions_with_wf_tys<'tcx>( id: hir::HirId, param_env: ty::ParamEnv<'tcx>, wf_tys: &FxHashSet<Ty<'tcx>>, - add_constraints: impl for<'a> FnOnce( - &'a InferCtxt<'a, 'tcx>, - &'a Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>, - ), + add_constraints: impl for<'a> FnOnce(&'a InferCtxt<'a, 'tcx>, &'a RegionBoundPairs<'tcx>), ) -> bool { // Unfortunately, we have to use a new `InferCtxt` each call, because // region constraints get added and solved there and we need to test each @@ -848,29 +844,13 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { let ty = tcx.type_of(tcx.hir().local_def_id(param.hir_id)); if tcx.features().adt_const_params { - let err = match ty.peel_refs().kind() { - ty::FnPtr(_) => Some("function pointers"), - ty::RawPtr(_) => Some("raw pointers"), - _ => None, - }; - - if let Some(unsupported_type) = err { - tcx.sess.span_err( - hir_ty.span, - &format!( - "using {} as const generic parameters is forbidden", - unsupported_type - ), - ); - } - if let Some(non_structural_match_ty) = - traits::search_for_structural_match_violation(param.span, tcx, ty, false) + traits::search_for_adt_const_param_violation(param.span, tcx, ty) { // We use the same error code in both branches, because this is really the same // issue: we just special-case the message for type parameters to make it // clearer. - match ty.peel_refs().kind() { + match non_structural_match_ty.kind() { ty::Param(_) => { // Const parameters may not have type parameters as their types, // because we cannot be sure that the type parameter derives `PartialEq` @@ -902,6 +882,24 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { .note("floats do not derive `Eq` or `Ord`, which are required for const parameters") .emit(); } + ty::FnPtr(_) => { + struct_span_err!( + tcx.sess, + hir_ty.span, + E0741, + "using function pointers as const generic parameters is forbidden", + ) + .emit(); + } + ty::RawPtr(_) => { + struct_span_err!( + tcx.sess, + hir_ty.span, + E0741, + "using raw pointers as const generic parameters is forbidden", + ) + .emit(); + } _ => { let mut diag = struct_span_err!( tcx.sess, @@ -909,10 +907,10 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { E0741, "`{}` must be annotated with `#[derive(PartialEq, Eq)]` to be used as \ the type of a const parameter", - non_structural_match_ty.ty, + non_structural_match_ty, ); - if ty == non_structural_match_ty.ty { + if ty == non_structural_match_ty { diag.span_label( hir_ty.span, format!("`{ty}` doesn't derive both `PartialEq` and `Eq`"), @@ -1242,6 +1240,7 @@ fn check_impl<'tcx>( item: &'tcx hir::Item<'tcx>, ast_self_ty: &hir::Ty<'_>, ast_trait_ref: &Option<hir::TraitRef<'_>>, + constness: hir::Constness, ) { enter_wf_checking_ctxt(tcx, item.span, item.def_id, |wfcx| { match *ast_trait_ref { @@ -1251,11 +1250,19 @@ fn check_impl<'tcx>( // won't hold). let trait_ref = tcx.impl_trait_ref(item.def_id).unwrap(); let trait_ref = wfcx.normalize(ast_trait_ref.path.span, None, trait_ref); + let trait_pred = ty::TraitPredicate { + trait_ref, + constness: match constness { + hir::Constness::Const => ty::BoundConstness::ConstIfConst, + hir::Constness::NotConst => ty::BoundConstness::NotConst, + }, + polarity: ty::ImplPolarity::Positive, + }; let obligations = traits::wf::trait_obligations( wfcx.infcx, wfcx.param_env, wfcx.body_id, - &trait_ref, + &trait_pred, ast_trait_ref.path.span, item, ); @@ -1913,7 +1920,7 @@ impl<'a, 'tcx> WfCheckingCtxt<'a, 'tcx> { } } -pub(super) fn impl_implied_bounds<'tcx>( +pub fn impl_implied_bounds<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, impl_def_id: LocalDefId, diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs index 23ac638b2f4..fa6053ac395 100644 --- a/compiler/rustc_typeck/src/check/writeback.rs +++ b/compiler/rustc_typeck/src/check/writeback.rs @@ -5,7 +5,7 @@ use crate::check::FnCtxt; use hir::def_id::LocalDefId; -use rustc_data_structures::stable_map::FxHashMap; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -748,7 +748,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> { // (e.g. keep `for<'a>` named `for<'a>`). // This allows NLL to generate error messages that // refer to the higher-ranked lifetime names written by the user. - EraseEarlyRegions { tcx: self.infcx.tcx }.fold_ty(t) + EraseEarlyRegions { tcx: self.tcx }.fold_ty(t) } Err(_) => { debug!("Resolver::fold_ty: input type `{:?}` not fully resolvable", t); @@ -766,7 +766,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> { fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { match self.infcx.fully_resolve(ct) { - Ok(ct) => self.infcx.tcx.erase_regions(ct), + Ok(ct) => self.tcx.erase_regions(ct), Err(_) => { debug!("Resolver::fold_const: input const `{:?}` not fully resolvable", ct); self.report_const_error(ct); diff --git a/compiler/rustc_typeck/src/coherence/builtin.rs b/compiler/rustc_typeck/src/coherence/builtin.rs index a92c37ff143..1e404fda035 100644 --- a/compiler/rustc_typeck/src/coherence/builtin.rs +++ b/compiler/rustc_typeck/src/coherence/builtin.rs @@ -116,8 +116,8 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) { // why this field does not implement Copy. This is useful because sometimes // it is not immediately clear why Copy is not implemented for a field, since // all we point at is the field itself. - tcx.infer_ctxt().enter(|infcx| { - let mut fulfill_cx = traits::FulfillmentContext::new_ignoring_regions(); + tcx.infer_ctxt().ignoring_regions().enter(|infcx| { + let mut fulfill_cx = traits::FulfillmentContext::new(); fulfill_cx.register_bound( &infcx, param_env, @@ -349,7 +349,7 @@ fn visit_implementation_of_dispatch_from_dyn<'tcx>(tcx: TyCtxt<'tcx>, impl_did: // Finally, resolve all regions. let outlives_env = OutlivesEnvironment::new(param_env); - infcx.resolve_regions_and_report_errors(impl_did, &outlives_env); + infcx.check_region_obligations_and_report_errors(impl_did, &outlives_env); } } _ => { @@ -606,7 +606,7 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn // Finally, resolve all regions. let outlives_env = OutlivesEnvironment::new(param_env); - infcx.resolve_regions_and_report_errors(impl_did, &outlives_env); + infcx.check_region_obligations_and_report_errors(impl_did, &outlives_env); CoerceUnsizedInfo { custom_kind: kind } }) diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls.rs b/compiler/rustc_typeck/src/coherence/inherent_impls.rs index 7a9b874b5e4..52aad636fd8 100644 --- a/compiler/rustc_typeck/src/coherence/inherent_impls.rs +++ b/compiler/rustc_typeck/src/coherence/inherent_impls.rs @@ -219,8 +219,9 @@ impl<'tcx> InherentCollect<'tcx> { | ty::RawPtr(_) | ty::Ref(..) | ty::Never + | ty::FnPtr(_) | ty::Tuple(..) => self.check_primitive_impl(item.def_id, self_ty, items, ty.span), - ty::FnPtr(_) | ty::Projection(..) | ty::Opaque(..) | ty::Param(_) => { + ty::Projection(..) | ty::Opaque(..) | ty::Param(_) => { let mut err = struct_span_err!( self.tcx.sess, ty.span, diff --git a/compiler/rustc_typeck/src/coherence/orphan.rs b/compiler/rustc_typeck/src/coherence/orphan.rs index 697ef7bc022..1608550aa6a 100644 --- a/compiler/rustc_typeck/src/coherence/orphan.rs +++ b/compiler/rustc_typeck/src/coherence/orphan.rs @@ -3,7 +3,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::struct_span_err; -use rustc_errors::ErrorGuaranteed; +use rustc_errors::{Diagnostic, ErrorGuaranteed}; use rustc_hir as hir; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::ty::subst::GenericArgKind; @@ -107,6 +107,7 @@ fn do_orphan_check_impl<'tcx>( Err(err) => emit_orphan_check_error( tcx, sp, + item.span, tr.path.span, trait_ref.self_ty(), impl_.self_ty.span, @@ -207,6 +208,7 @@ fn do_orphan_check_impl<'tcx>( fn emit_orphan_check_error<'tcx>( tcx: TyCtxt<'tcx>, sp: Span, + full_impl_span: Span, trait_span: Span, self_ty: Ty<'tcx>, self_ty_span: Span, @@ -247,8 +249,20 @@ fn emit_orphan_check_error<'tcx>( ty::Slice(_) => (this, " because slices are always foreign"), ty::Array(..) => (this, " because arrays are always foreign"), ty::Tuple(..) => (this, " because tuples are always foreign"), + ty::RawPtr(ptr_ty) => { + emit_newtype_suggestion_for_raw_ptr( + full_impl_span, + self_ty, + self_ty_span, + ptr_ty, + &mut err, + ); + + (format!("`{}`", ty), " because raw pointers are always foreign") + } _ => (format!("`{}`", ty), ""), }; + let msg = format!("{} is not defined in the current crate{}", ty, postfix); if *is_target_ty { // Point at `D<A>` in `impl<A, B> for C<B> in D<A>` @@ -330,6 +344,27 @@ fn emit_orphan_check_error<'tcx>( }) } +fn emit_newtype_suggestion_for_raw_ptr( + full_impl_span: Span, + self_ty: Ty<'_>, + self_ty_span: Span, + ptr_ty: &ty::TypeAndMut<'_>, + diag: &mut Diagnostic, +) { + if !self_ty.needs_subst() { + let mut_key = if ptr_ty.mutbl == rustc_middle::mir::Mutability::Mut { "mut " } else { "" }; + let msg_sugg = "consider introducing a new wrapper type".to_owned(); + let sugg = vec![ + ( + full_impl_span.shrink_to_lo(), + format!("struct WrapperType(*{}{});\n\n", mut_key, ptr_ty.ty), + ), + (self_ty_span, "WrapperType".to_owned()), + ]; + diag.multipart_suggestion(msg_sugg, sugg, rustc_errors::Applicability::MaybeIncorrect); + } +} + /// Lint impls of auto traits if they are likely to have /// unsound or surprising effects on auto impls. fn lint_auto_trait_impl<'tcx>( diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 6ec741269e8..36111637a56 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -1346,16 +1346,8 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<S match self.tcx.named_region(lt.hir_id) { Some(rl::Region::Static | rl::Region::EarlyBound(..)) => {} - Some( - rl::Region::LateBound(debruijn, _, _) - | rl::Region::LateBoundAnon(debruijn, _, _), - ) if debruijn < self.outer_index => {} - Some( - rl::Region::LateBound(..) - | rl::Region::LateBoundAnon(..) - | rl::Region::Free(..), - ) - | None => { + Some(rl::Region::LateBound(debruijn, _, _)) if debruijn < self.outer_index => {} + Some(rl::Region::LateBound(..) | rl::Region::Free(..)) | None => { self.has_late_bound_regions = Some(lt.span); } } @@ -1929,7 +1921,7 @@ fn infer_return_ty_for_fn_sig<'tcx>( visitor.visit_ty(ty); let mut diag = bad_placeholder(tcx, visitor.0, "return type"); let ret_ty = fn_sig.skip_binder().output(); - if ret_ty.is_suggestable(tcx) { + if ret_ty.is_suggestable(tcx, false) { diag.span_suggestion( ty.span, "replace with the correct return type", @@ -1938,7 +1930,12 @@ fn infer_return_ty_for_fn_sig<'tcx>( ); } else if matches!(ret_ty.kind(), ty::FnDef(..)) { let fn_sig = ret_ty.fn_sig(tcx); - if fn_sig.skip_binder().inputs_and_output.iter().all(|t| t.is_suggestable(tcx)) { + if fn_sig + .skip_binder() + .inputs_and_output + .iter() + .all(|t| t.is_suggestable(tcx, false)) + { diag.span_suggestion( ty.span, "replace with the correct return type", @@ -2085,10 +2082,17 @@ fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> { // from the trait itself that *shouldn't* be shown as the source of // an obligation and instead be skipped. Otherwise we'd use // `tcx.def_span(def_id);` + + let constness = if tcx.has_attr(def_id, sym::const_trait) { + ty::BoundConstness::ConstIfConst + } else { + ty::BoundConstness::NotConst + }; + let span = rustc_span::DUMMY_SP; result.predicates = tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(std::iter::once(( - ty::TraitRef::identity(tcx, def_id).without_const().to_predicate(tcx), + ty::TraitRef::identity(tcx, def_id).with_constness(constness).to_predicate(tcx), span, )))); } @@ -2770,6 +2774,12 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs { } } else if attr.has_name(sym::rustc_allocator_nounwind) { codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND; + } else if attr.has_name(sym::rustc_reallocator) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::REALLOCATOR; + } else if attr.has_name(sym::rustc_deallocator) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::DEALLOCATOR; + } else if attr.has_name(sym::rustc_allocator_zeroed) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED; } else if attr.has_name(sym::naked) { codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED; } else if attr.has_name(sym::no_mangle) { @@ -2813,7 +2823,37 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs { ) .emit(); } - None => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED, + None => { + // Unfortunately, unconditionally using `llvm.used` causes + // issues in handling `.init_array` with the gold linker, + // but using `llvm.compiler.used` caused a nontrival amount + // of unintentional ecosystem breakage -- particularly on + // Mach-O targets. + // + // As a result, we emit `llvm.compiler.used` only on ELF + // targets. This is somewhat ad-hoc, but actually follows + // our pre-LLVM 13 behavior (prior to the ecosystem + // breakage), and seems to match `clang`'s behavior as well + // (both before and after LLVM 13), possibly because they + // have similar compatibility concerns to us. See + // https://github.com/rust-lang/rust/issues/47384#issuecomment-1019080146 + // and following comments for some discussion of this, as + // well as the comments in `rustc_codegen_llvm` where these + // flags are handled. + // + // Anyway, to be clear: this is still up in the air + // somewhat, and is subject to change in the future (which + // is a good thing, because this would ideally be a bit + // more firmed up). + let is_like_elf = !(tcx.sess.target.is_like_osx + || tcx.sess.target.is_like_windows + || tcx.sess.target.is_like_wasm); + codegen_fn_attrs.flags |= if is_like_elf { + CodegenFnAttrFlags::USED + } else { + CodegenFnAttrFlags::USED_LINKER + }; + } } } else if attr.has_name(sym::cmse_nonsecure_entry) { if !matches!(tcx.fn_sig(did).abi(), abi::Abi::C { .. }) { @@ -2939,6 +2979,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs { codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY; } else if item.has_name(sym::memtag) { codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMTAG; + } else if item.has_name(sym::shadow_call_stack) { + codegen_fn_attrs.no_sanitize |= SanitizerSet::SHADOWCALLSTACK; } else if item.has_name(sym::thread) { codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD; } else if item.has_name(sym::hwaddress) { @@ -2946,7 +2988,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs { } else { tcx.sess .struct_span_err(item.span(), "invalid argument for `no_sanitize`") - .note("expected one of: `address`, `cfi`, `hwaddress`, `memory`, `memtag`, or `thread`") + .note("expected one of: `address`, `cfi`, `hwaddress`, `memory`, `memtag`, `shadow-call-stack`, or `thread`") .emit(); } } @@ -3123,9 +3165,11 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs { // #73631: closures inherit `#[target_feature]` annotations 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()) + if tcx.def_kind(owner_id).has_codegen_attrs() { + codegen_fn_attrs + .target_features + .extend(tcx.codegen_fn_attrs(owner_id).target_features.iter().copied()); + } } // If a function uses #[target_feature] it can't be inlined into general diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs index faa4f3700bb..534ddfa9531 100644 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ b/compiler/rustc_typeck/src/collect/type_of.rs @@ -100,7 +100,7 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< ExprKind::MethodCall(segment, ..) | ExprKind::Path(QPath::TypeRelative(_, segment)), .. }) => { - let body_owner = tcx.hir().local_def_id(tcx.hir().enclosing_body_owner(hir_id)); + let body_owner = tcx.hir().enclosing_body_owner(hir_id); let tables = tcx.typeck(body_owner); // This may fail in case the method/path does not actually exist. // As there is no relevant param for `def_id`, we simply return @@ -134,7 +134,7 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< | ExprKind::Struct(&QPath::Resolved(_, path), ..), .. }) => { - let body_owner = tcx.hir().local_def_id(tcx.hir().enclosing_body_owner(hir_id)); + let body_owner = tcx.hir().enclosing_body_owner(hir_id); let _tables = tcx.typeck(body_owner); &*path } @@ -335,37 +335,11 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { tcx.mk_adt(def, substs) } ItemKind::OpaqueTy(OpaqueTy { origin: hir::OpaqueTyOrigin::TyAlias, .. }) => { - find_opaque_ty_constraints(tcx, def_id) + find_opaque_ty_constraints_for_tait(tcx, def_id) } // Opaque types desugared from `impl Trait`. ItemKind::OpaqueTy(OpaqueTy { origin: hir::OpaqueTyOrigin::FnReturn(owner) | hir::OpaqueTyOrigin::AsyncFn(owner), .. }) => { - let concrete_ty = tcx - .mir_borrowck(owner) - .concrete_opaque_types - .get(&def_id) - .copied() - .map(|concrete| concrete.ty) - .unwrap_or_else(|| { - let table = tcx.typeck(owner); - if let Some(_) = table.tainted_by_errors { - // Some error in the - // owner fn prevented us from populating - // the `concrete_opaque_types` table. - tcx.ty_error() - } else { - table.concrete_opaque_types.get(&def_id).copied().unwrap_or_else(|| { - // We failed to resolve the opaque type or it - // resolves to itself. We interpret this as the - // no values of the hidden type ever being constructed, - // so we can just make the hidden type be `!`. - // For backwards compatibility reasons, we fall back to - // `()` until we the diverging default is changed. - Some(tcx.mk_diverging_default()) - }).expect("RPIT always have a hidden type from typeck") - } - }); - debug!("concrete_ty = {:?}", concrete_ty); - concrete_ty + find_opaque_ty_constraints_for_rpit(tcx, def_id, owner) } ItemKind::Trait(..) | ItemKind::TraitAlias(..) @@ -519,7 +493,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { /// fn b<T>() -> Foo<T, u32> { .. } /// ``` /// -fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { +fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { use rustc_hir::{Expr, ImplItem, Item, TraitItem}; struct ConstraintLocator<'tcx> { @@ -538,9 +512,9 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { impl ConstraintLocator<'_> { #[instrument(skip(self), level = "debug")] - fn check(&mut self, def_id: LocalDefId) { + fn check(&mut self, item_def_id: LocalDefId) { // Don't try to check items that cannot possibly constrain the type. - if !self.tcx.has_typeck_results(def_id) { + if !self.tcx.has_typeck_results(item_def_id) { debug!("no constraint: no typeck results"); return; } @@ -555,26 +529,20 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { // // because we again need to reveal `Foo` so we can check whether the // // constant does not contain interior mutability. // ``` - let tables = self.tcx.typeck(def_id); + let tables = self.tcx.typeck(item_def_id); if let Some(_) = tables.tainted_by_errors { self.found = Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: self.tcx.ty_error() }); return; } - if tables.concrete_opaque_types.get(&self.def_id).is_none() { + if !tables.concrete_opaque_types.contains_key(&self.def_id) { debug!("no constraints in typeck results"); return; } // Use borrowck to get the type with unerased regions. - let concrete_opaque_types = &self.tcx.mir_borrowck(def_id).concrete_opaque_types; + let concrete_opaque_types = &self.tcx.mir_borrowck(item_def_id).concrete_opaque_types; debug!(?concrete_opaque_types); - for &(def_id, concrete_type) in concrete_opaque_types { - if def_id != self.def_id { - // Ignore constraints for other opaque types. - continue; - } - + if let Some(&concrete_type) = concrete_opaque_types.get(&self.def_id) { debug!(?concrete_type, "found constraint"); - if let Some(prev) = self.found { if concrete_type.ty != prev.ty && !(concrete_type, prev).references_error() { prev.report_mismatch(&concrete_type, self.tcx); @@ -666,6 +634,122 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { } } +fn find_opaque_ty_constraints_for_rpit( + tcx: TyCtxt<'_>, + def_id: LocalDefId, + owner_def_id: LocalDefId, +) -> Ty<'_> { + use rustc_hir::{Expr, ImplItem, Item, TraitItem}; + + struct ConstraintChecker<'tcx> { + tcx: TyCtxt<'tcx>, + + /// def_id of the opaque type whose defining uses are being checked + def_id: LocalDefId, + + found: ty::OpaqueHiddenType<'tcx>, + } + + impl ConstraintChecker<'_> { + #[instrument(skip(self), level = "debug")] + fn check(&self, def_id: LocalDefId) { + // Use borrowck to get the type with unerased regions. + let concrete_opaque_types = &self.tcx.mir_borrowck(def_id).concrete_opaque_types; + debug!(?concrete_opaque_types); + for &(def_id, concrete_type) in concrete_opaque_types { + if def_id != self.def_id { + // Ignore constraints for other opaque types. + continue; + } + + debug!(?concrete_type, "found constraint"); + + if concrete_type.ty != self.found.ty + && !(concrete_type, self.found).references_error() + { + self.found.report_mismatch(&concrete_type, self.tcx); + } + } + } + } + + impl<'tcx> intravisit::Visitor<'tcx> for ConstraintChecker<'tcx> { + type NestedFilter = nested_filter::OnlyBodies; + + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() + } + fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { + if let hir::ExprKind::Closure { .. } = ex.kind { + let def_id = self.tcx.hir().local_def_id(ex.hir_id); + self.check(def_id); + } + intravisit::walk_expr(self, ex); + } + fn visit_item(&mut self, it: &'tcx Item<'tcx>) { + trace!(?it.def_id); + // The opaque type itself or its children are not within its reveal scope. + if it.def_id != self.def_id { + self.check(it.def_id); + intravisit::walk_item(self, it); + } + } + fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) { + trace!(?it.def_id); + // The opaque type itself or its children are not within its reveal scope. + if it.def_id != self.def_id { + self.check(it.def_id); + intravisit::walk_impl_item(self, it); + } + } + fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) { + trace!(?it.def_id); + self.check(it.def_id); + intravisit::walk_trait_item(self, it); + } + } + + let concrete = tcx.mir_borrowck(owner_def_id).concrete_opaque_types.get(&def_id).copied(); + + if let Some(concrete) = concrete { + let scope = tcx.hir().local_def_id_to_hir_id(owner_def_id); + debug!(?scope); + let mut locator = ConstraintChecker { def_id: def_id, tcx, found: concrete }; + + match tcx.hir().get(scope) { + Node::Item(it) => intravisit::walk_item(&mut locator, it), + Node::ImplItem(it) => intravisit::walk_impl_item(&mut locator, it), + Node::TraitItem(it) => intravisit::walk_trait_item(&mut locator, it), + other => bug!("{:?} is not a valid scope for an opaque type item", other), + } + } + + concrete.map(|concrete| concrete.ty).unwrap_or_else(|| { + let table = tcx.typeck(owner_def_id); + if let Some(_) = table.tainted_by_errors { + // Some error in the + // owner fn prevented us from populating + // the `concrete_opaque_types` table. + tcx.ty_error() + } else { + table + .concrete_opaque_types + .get(&def_id) + .copied() + .unwrap_or_else(|| { + // We failed to resolve the opaque type or it + // resolves to itself. We interpret this as the + // no values of the hidden type ever being constructed, + // so we can just make the hidden type be `!`. + // For backwards compatibility reasons, we fall back to + // `()` until we the diverging default is changed. + Some(tcx.mk_diverging_default()) + }) + .expect("RPIT always have a hidden type from typeck") + } + }) +} + fn infer_placeholder_type<'a>( tcx: TyCtxt<'a>, def_id: LocalDefId, diff --git a/compiler/rustc_typeck/src/errors.rs b/compiler/rustc_typeck/src/errors.rs index 4cdec615d82..0438ac02ea9 100644 --- a/compiler/rustc_typeck/src/errors.rs +++ b/compiler/rustc_typeck/src/errors.rs @@ -244,7 +244,7 @@ pub struct UnconstrainedOpaqueType { pub struct MissingTypeParams { pub span: Span, pub def_span: Span, - pub missing_type_params: Vec<String>, + pub missing_type_params: Vec<Symbol>, pub empty_generic_args: bool, } @@ -285,7 +285,15 @@ impl<'a> SessionDiagnostic<'a> for MissingTypeParams { err.span_suggestion( self.span, rustc_errors::fluent::typeck::suggestion, - format!("{}<{}>", snippet, self.missing_type_params.join(", ")), + format!( + "{}<{}>", + snippet, + self.missing_type_params + .iter() + .map(|n| n.to_string()) + .collect::<Vec<_>>() + .join(", ") + ), Applicability::HasPlaceholders, ); suggested = true; 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 6ece955de64..74abb71a18e 100644 --- a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs @@ -65,6 +65,8 @@ //! cause use after frees with purely safe code in the same way as specializing //! on traits with methods can. +use crate::check::regionck::OutlivesEnvironmentExt; +use crate::check::wfcheck::impl_implied_bounds; use crate::constrained_generic_params as cgp; use crate::errors::SubstsOnOverriddenImpl; @@ -148,9 +150,15 @@ fn get_impl_substs<'tcx>( let impl2_substs = translate_substs(infcx, param_env, impl1_def_id.to_def_id(), impl1_substs, impl2_node); - // Conservatively use an empty `ParamEnv`. - let outlives_env = OutlivesEnvironment::new(ty::ParamEnv::empty()); - infcx.resolve_regions_and_report_errors(impl1_def_id, &outlives_env); + let mut outlives_env = OutlivesEnvironment::new(param_env); + let implied_bounds = + impl_implied_bounds(infcx.tcx, param_env, impl1_def_id, tcx.def_span(impl1_def_id)); + outlives_env.add_implied_bounds( + infcx, + implied_bounds, + tcx.hir().local_def_id_to_hir_id(impl1_def_id), + ); + infcx.check_region_obligations_and_report_errors(impl1_def_id, &outlives_env); let Ok(impl2_substs) = infcx.fully_resolve(impl2_substs) else { let span = tcx.def_span(impl1_def_id); tcx.sess.emit_err(SubstsOnOverriddenImpl { span }); 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 469f7d1172a..99729391e02 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 @@ -420,12 +420,10 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { let provided_lt_args = self.num_provided_lifetime_args(); let provided_type_or_const_args = self.num_provided_type_or_const_args(); - let get_verb = |num_args| if num_args == 1 { "was" } else { "were" }; - let (provided_args_str, verb) = match self.gen_args_info { MissingLifetimes { .. } | ExcessLifetimes { .. } => ( format!("{} lifetime argument{}", provided_lt_args, pluralize!(provided_lt_args)), - get_verb(provided_lt_args), + pluralize!("was", provided_lt_args), ), MissingTypesOrConsts { .. } | ExcessTypesOrConsts { .. } => ( format!( @@ -433,7 +431,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { provided_type_or_const_args, pluralize!(provided_type_or_const_args) ), - get_verb(provided_type_or_const_args), + pluralize!("was", provided_type_or_const_args), ), }; |
