diff options
Diffstat (limited to 'compiler')
168 files changed, 8271 insertions, 1595 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 49aa1fc1735..dee3a16f9b1 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -96,6 +96,7 @@ pub struct Path { /// The segments in the path: the things separated by `::`. /// Global paths begin with `kw::PathRoot`. pub segments: Vec<PathSegment>, + pub tokens: Option<TokenStream>, } impl PartialEq<Symbol> for Path { @@ -117,7 +118,7 @@ impl Path { // Convert a span and an identifier to the corresponding // one-segment path. pub fn from_ident(ident: Ident) -> Path { - Path { segments: vec![PathSegment::from_ident(ident)], span: ident.span } + Path { segments: vec![PathSegment::from_ident(ident)], span: ident.span, tokens: None } } pub fn is_global(&self) -> bool { @@ -540,6 +541,7 @@ pub struct Block { /// Distinguishes between `unsafe { ... }` and `{ ... }`. pub rules: BlockCheckMode, pub span: Span, + pub tokens: Option<TokenStream>, } /// A match pattern. @@ -586,7 +588,7 @@ impl Pat { _ => return None, }; - Some(P(Ty { kind, id: self.id, span: self.span })) + Some(P(Ty { kind, id: self.id, span: self.span, tokens: None })) } /// Walk top-down and call `it` in each place where a pattern occurs @@ -916,6 +918,7 @@ pub struct Stmt { pub id: NodeId, pub kind: StmtKind, pub span: Span, + pub tokens: Option<TokenStream>, } impl Stmt { @@ -1068,7 +1071,7 @@ pub struct Expr { // `Expr` is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(target_arch = "x86_64")] -rustc_data_structures::static_assert_size!(Expr, 104); +rustc_data_structures::static_assert_size!(Expr, 112); impl Expr { /// Returns `true` if this expression would be valid somewhere that expects a value; @@ -1168,7 +1171,7 @@ impl Expr { _ => return None, }; - Some(P(Ty { kind, id: self.id, span: self.span })) + Some(P(Ty { kind, id: self.id, span: self.span, tokens: None })) } pub fn precedence(&self) -> ExprPrecedence { @@ -1866,6 +1869,7 @@ pub struct Ty { pub id: NodeId, pub kind: TyKind, pub span: Span, + pub tokens: Option<TokenStream>, } #[derive(Clone, Encodable, Decodable, Debug)] @@ -2144,7 +2148,7 @@ impl Param { /// Builds a `Param` object from `ExplicitSelf`. pub fn from_self(attrs: AttrVec, eself: ExplicitSelf, eself_ident: Ident) -> Param { let span = eself.span.to(eself_ident.span); - let infer_ty = P(Ty { id: DUMMY_NODE_ID, kind: TyKind::ImplicitSelf, span }); + let infer_ty = P(Ty { id: DUMMY_NODE_ID, kind: TyKind::ImplicitSelf, span, tokens: None }); let param = |mutbl, ty| Param { attrs, pat: P(Pat { @@ -2167,6 +2171,7 @@ impl Param { id: DUMMY_NODE_ID, kind: TyKind::Rptr(lt, MutTy { ty: infer_ty, mutbl }), span, + tokens: None, }), ), } @@ -2289,12 +2294,15 @@ impl FnRetTy { /// Module declaration. /// /// E.g., `mod foo;` or `mod foo { .. }`. -#[derive(Clone, Encodable, Decodable, Debug, Default)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct Mod { /// A span from the first token past `{` to the last token until `}`. /// For `mod foo;`, the inner span ranges from the first token /// to the last token in the external file. pub inner: Span, + /// `unsafe` keyword accepted syntactically for macro DSLs, but not + /// semantically by Rust. + pub unsafety: Unsafe, pub items: Vec<P<Item>>, /// `true` for `mod foo { .. }`; `false` for `mod foo;`. pub inline: bool, @@ -2302,9 +2310,12 @@ pub struct Mod { /// Foreign module declaration. /// -/// E.g., `extern { .. }` or `extern C { .. }`. +/// E.g., `extern { .. }` or `extern "C" { .. }`. #[derive(Clone, Encodable, Decodable, Debug)] pub struct ForeignMod { + /// `unsafe` keyword accepted syntactically for macro DSLs, but not + /// semantically by Rust. + pub unsafety: Unsafe, pub abi: Option<StrLit>, pub items: Vec<P<ForeignItem>>, } @@ -2410,6 +2421,7 @@ impl<D: Decoder> rustc_serialize::Decodable<D> for AttrId { pub struct AttrItem { pub path: Path, pub args: MacArgs, + pub tokens: Option<TokenStream>, } /// A list of attributes. @@ -2479,7 +2491,12 @@ pub enum CrateSugar { JustCrate, } -pub type Visibility = Spanned<VisibilityKind>; +#[derive(Clone, Encodable, Decodable, Debug)] +pub struct Visibility { + pub kind: VisibilityKind, + pub span: Span, + pub tokens: Option<TokenStream>, +} #[derive(Clone, Encodable, Decodable, Debug)] pub enum VisibilityKind { diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 12d6f7cc33d..2782869fb88 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::mut_visit::visit_clobber; use crate::ptr::P; use crate::token::{self, CommentKind, Token}; -use crate::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndJoint}; +use crate::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndSpacing}; use rustc_index::bit_set::GrowableBitSet; use rustc_span::source_map::{BytePos, Spanned}; @@ -330,7 +330,7 @@ crate fn mk_attr_id() -> AttrId { } pub fn mk_attr(style: AttrStyle, path: Path, args: MacArgs, span: Span) -> Attribute { - mk_attr_from_item(style, AttrItem { path, args }, span) + mk_attr_from_item(style, AttrItem { path, args, tokens: None }, span) } pub fn mk_attr_from_item(style: AttrStyle, item: AttrItem, span: Span) -> Attribute { @@ -361,7 +361,7 @@ pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool { } impl MetaItem { - fn token_trees_and_joints(&self) -> Vec<TreeAndJoint> { + fn token_trees_and_spacings(&self) -> Vec<TreeAndSpacing> { let mut idents = vec![]; let mut last_pos = BytePos(0 as u32); for (i, segment) in self.path.segments.iter().enumerate() { @@ -374,7 +374,7 @@ impl MetaItem { idents.push(TokenTree::Token(Token::from_ast_ident(segment.ident)).into()); last_pos = segment.ident.span.hi(); } - idents.extend(self.kind.token_trees_and_joints(self.span)); + idents.extend(self.kind.token_trees_and_spacings(self.span)); idents } @@ -415,7 +415,7 @@ impl MetaItem { } } let span = span.with_hi(segments.last().unwrap().ident.span.hi()); - Path { span, segments } + Path { span, segments, tokens: None } } Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. })) => match *nt { token::Nonterminal::NtMeta(ref item) => return item.meta(item.path.span), @@ -447,7 +447,7 @@ impl MetaItemKind { if i > 0 { tts.push(TokenTree::token(token::Comma, span).into()); } - tts.extend(item.token_trees_and_joints()) + tts.extend(item.token_trees_and_spacings()) } MacArgs::Delimited( DelimSpan::from_single(span), @@ -458,7 +458,7 @@ impl MetaItemKind { } } - fn token_trees_and_joints(&self, span: Span) -> Vec<TreeAndJoint> { + fn token_trees_and_spacings(&self, span: Span) -> Vec<TreeAndSpacing> { match *self { MetaItemKind::Word => vec![], MetaItemKind::NameValue(ref lit) => { @@ -470,7 +470,7 @@ impl MetaItemKind { if i > 0 { tokens.push(TokenTree::token(token::Comma, span).into()); } - tokens.extend(item.token_trees_and_joints()) + tokens.extend(item.token_trees_and_spacings()) } vec![ TokenTree::Delimited( @@ -553,9 +553,9 @@ impl NestedMetaItem { } } - fn token_trees_and_joints(&self) -> Vec<TreeAndJoint> { + fn token_trees_and_spacings(&self) -> Vec<TreeAndSpacing> { match *self { - NestedMetaItem::MetaItem(ref item) => item.token_trees_and_joints(), + NestedMetaItem::MetaItem(ref item) => item.token_trees_and_spacings(), NestedMetaItem::Literal(ref lit) => vec![lit.token_tree().into()], } } diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 3ef83ef3fc9..425ef83b57a 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -14,7 +14,7 @@ use crate::tokenstream::*; use rustc_data_structures::map_in_place::MapInPlace; use rustc_data_structures::sync::Lrc; -use rustc_span::source_map::{respan, Spanned}; +use rustc_span::source_map::Spanned; use rustc_span::symbol::Ident; use rustc_span::Span; @@ -451,7 +451,7 @@ pub fn noop_visit_ty_constraint<T: MutVisitor>( } pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) { - let Ty { id, kind, span } = ty.deref_mut(); + let Ty { id, kind, span, tokens: _ } = ty.deref_mut(); vis.visit_id(id); match kind { TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err | TyKind::Never | TyKind::CVarArgs => {} @@ -490,7 +490,7 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) { } pub fn noop_visit_foreign_mod<T: MutVisitor>(foreign_mod: &mut ForeignMod, vis: &mut T) { - let ForeignMod { abi: _, items } = foreign_mod; + let ForeignMod { unsafety: _, abi: _, items } = foreign_mod; items.flat_map_in_place(|item| vis.flat_map_foreign_item(item)); } @@ -513,7 +513,7 @@ pub fn noop_visit_ident<T: MutVisitor>(Ident { name: _, span }: &mut Ident, vis: vis.visit_span(span); } -pub fn noop_visit_path<T: MutVisitor>(Path { segments, span }: &mut Path, vis: &mut T) { +pub fn noop_visit_path<T: MutVisitor>(Path { segments, span, tokens: _ }: &mut Path, vis: &mut T) { vis.visit_span(span); for PathSegment { ident, id, args } in segments { vis.visit_ident(ident); @@ -579,7 +579,7 @@ pub fn noop_visit_local<T: MutVisitor>(local: &mut P<Local>, vis: &mut T) { pub fn noop_visit_attribute<T: MutVisitor>(attr: &mut Attribute, vis: &mut T) { let Attribute { kind, id: _, style: _, span } = attr; match kind { - AttrKind::Normal(AttrItem { path, args }) => { + AttrKind::Normal(AttrItem { path, args, tokens: _ }) => { vis.visit_path(path); visit_mac_args(args, vis); } @@ -709,7 +709,7 @@ pub fn noop_visit_interpolated<T: MutVisitor>(nt: &mut token::Nonterminal, vis: token::NtLifetime(ident) => vis.visit_ident(ident), token::NtLiteral(expr) => vis.visit_expr(expr), token::NtMeta(item) => { - let AttrItem { path, args } = item.deref_mut(); + let AttrItem { path, args, tokens: _ } = item.deref_mut(); vis.visit_path(path); visit_mac_args(args, vis); } @@ -871,7 +871,7 @@ pub fn noop_visit_mt<T: MutVisitor>(MutTy { ty, mutbl: _ }: &mut MutTy, vis: &mu } pub fn noop_visit_block<T: MutVisitor>(block: &mut P<Block>, vis: &mut T) { - let Block { id, stmts, rules: _, span } = block.deref_mut(); + let Block { id, stmts, rules: _, span, tokens: _ } = block.deref_mut(); vis.visit_id(id); stmts.flat_map_in_place(|stmt| vis.flat_map_stmt(stmt)); vis.visit_span(span); @@ -970,18 +970,21 @@ pub fn noop_visit_fn_header<T: MutVisitor>(header: &mut FnHeader, vis: &mut T) { vis.visit_asyncness(asyncness); } -pub fn noop_visit_mod<T: MutVisitor>(Mod { inner, items, inline: _ }: &mut Mod, vis: &mut T) { +pub fn noop_visit_mod<T: MutVisitor>(module: &mut Mod, vis: &mut T) { + let Mod { inner, unsafety: _, items, inline: _ } = module; vis.visit_span(inner); items.flat_map_in_place(|item| vis.flat_map_item(item)); } pub fn noop_visit_crate<T: MutVisitor>(krate: &mut Crate, vis: &mut T) { visit_clobber(krate, |Crate { module, attrs, span, proc_macros }| { + let item_vis = + Visibility { kind: VisibilityKind::Public, span: span.shrink_to_lo(), tokens: None }; let item = P(Item { ident: Ident::invalid(), attrs, id: DUMMY_NODE_ID, - vis: respan(span.shrink_to_lo(), VisibilityKind::Public), + vis: item_vis, span, kind: ItemKind::Mod(module), tokens: None, @@ -990,7 +993,7 @@ pub fn noop_visit_crate<T: MutVisitor>(krate: &mut Crate, vis: &mut T) { let len = items.len(); if len == 0 { - let module = Mod { inner: span, items: vec![], inline: true }; + let module = Mod { inner: span, unsafety: Unsafe::No, items: vec![], inline: true }; Crate { module, attrs: vec![], span, proc_macros } } else if len == 1 { let Item { attrs, span, kind, .. } = items.into_iter().next().unwrap().into_inner(); @@ -1283,12 +1286,15 @@ pub fn noop_filter_map_expr<T: MutVisitor>(mut e: P<Expr>, vis: &mut T) -> Optio } pub fn noop_flat_map_stmt<T: MutVisitor>( - Stmt { kind, mut span, mut id }: Stmt, + Stmt { kind, mut span, mut id, tokens }: Stmt, vis: &mut T, ) -> SmallVec<[Stmt; 1]> { vis.visit_id(&mut id); vis.visit_span(&mut span); - noop_flat_map_stmt_kind(kind, vis).into_iter().map(|kind| Stmt { id, kind, span }).collect() + noop_flat_map_stmt_kind(kind, vis) + .into_iter() + .map(|kind| Stmt { id, kind, span, tokens: tokens.clone() }) + .collect() } pub fn noop_flat_map_stmt_kind<T: MutVisitor>( @@ -1313,13 +1319,13 @@ pub fn noop_flat_map_stmt_kind<T: MutVisitor>( } } -pub fn noop_visit_vis<T: MutVisitor>(Spanned { node, span }: &mut Visibility, vis: &mut T) { - match node { +pub fn noop_visit_vis<T: MutVisitor>(visibility: &mut Visibility, vis: &mut T) { + match &mut visibility.kind { VisibilityKind::Public | VisibilityKind::Crate(_) | VisibilityKind::Inherited => {} VisibilityKind::Restricted { path, id } => { vis.visit_path(path); vis.visit_id(id); } } - vis.visit_span(span); + vis.visit_span(&mut visibility.span); } diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index c6cc890b47f..d5b3e87adc3 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -173,6 +173,7 @@ pub fn ident_can_begin_expr(name: Symbol, span: Span, is_raw: bool) -> bool { kw::Move, kw::Return, kw::True, + kw::Try, kw::Unsafe, kw::While, kw::Yield, @@ -699,7 +700,7 @@ pub enum Nonterminal { // `Nonterminal` is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(target_arch = "x86_64")] -rustc_data_structures::static_assert_size!(Nonterminal, 40); +rustc_data_structures::static_assert_size!(Nonterminal, 48); #[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable)] pub enum NonterminalKind { @@ -809,9 +810,19 @@ impl Nonterminal { if let ExpnKind::Macro(_, macro_name) = orig_span.ctxt().outer_expn_data().kind { let filename = source_map.span_to_filename(orig_span); if let FileName::Real(RealFileName::Named(path)) = filename { - if (path.ends_with("time-macros-impl/src/lib.rs") - && macro_name == sym::impl_macros) - || (path.ends_with("js-sys/src/lib.rs") && macro_name == sym::arrays) + let matches_prefix = |prefix| { + // Check for a path that ends with 'prefix*/src/lib.rs' + let mut iter = path.components().rev(); + iter.next().and_then(|p| p.as_os_str().to_str()) == Some("lib.rs") + && iter.next().and_then(|p| p.as_os_str().to_str()) == Some("src") + && iter + .next() + .and_then(|p| p.as_os_str().to_str()) + .map_or(false, |p| p.starts_with(prefix)) + }; + + if (macro_name == sym::impl_macros && matches_prefix("time-macros-impl")) + || (macro_name == sym::arrays && matches_prefix("js-sys")) { let snippet = source_map.span_to_snippet(orig_span); if snippet.as_deref() == Ok("$name") { diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 151acddae84..f201f0b5c66 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -83,7 +83,7 @@ impl TokenTree { } pub fn joint(self) -> TokenStream { - TokenStream::new(vec![(self, Joint)]) + TokenStream::new(vec![(self, Spacing::Joint)]) } pub fn token(kind: TokenKind, span: Span) -> TokenTree { @@ -125,22 +125,20 @@ where /// instead of a representation of the abstract syntax tree. /// Today's `TokenTree`s can still contain AST via `token::Interpolated` for back-compat. #[derive(Clone, Debug, Default, Encodable, Decodable)] -pub struct TokenStream(pub Lrc<Vec<TreeAndJoint>>); +pub struct TokenStream(pub Lrc<Vec<TreeAndSpacing>>); -pub type TreeAndJoint = (TokenTree, IsJoint); +pub type TreeAndSpacing = (TokenTree, Spacing); // `TokenStream` is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(target_arch = "x86_64")] rustc_data_structures::static_assert_size!(TokenStream, 8); #[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable)] -pub enum IsJoint { +pub enum Spacing { + Alone, Joint, - NonJoint, } -use IsJoint::*; - impl TokenStream { /// Given a `TokenStream` with a `Stream` of only two arguments, return a new `TokenStream` /// separating the two arguments with a comma for diagnostic suggestions. @@ -153,7 +151,7 @@ impl TokenStream { let sp = match (&ts, &next) { (_, (TokenTree::Token(Token { kind: token::Comma, .. }), _)) => continue, ( - (TokenTree::Token(token_left), NonJoint), + (TokenTree::Token(token_left), Spacing::Alone), (TokenTree::Token(token_right), _), ) if ((token_left.is_ident() && !token_left.is_reserved_ident()) || token_left.is_lit()) @@ -162,11 +160,11 @@ impl TokenStream { { token_left.span } - ((TokenTree::Delimited(sp, ..), NonJoint), _) => sp.entire(), + ((TokenTree::Delimited(sp, ..), Spacing::Alone), _) => sp.entire(), _ => continue, }; let sp = sp.shrink_to_hi(); - let comma = (TokenTree::token(token::Comma, sp), NonJoint); + let comma = (TokenTree::token(token::Comma, sp), Spacing::Alone); suggestion = Some((pos, comma, sp)); } } @@ -184,19 +182,19 @@ impl TokenStream { impl From<TokenTree> for TokenStream { fn from(tree: TokenTree) -> TokenStream { - TokenStream::new(vec![(tree, NonJoint)]) + TokenStream::new(vec![(tree, Spacing::Alone)]) } } -impl From<TokenTree> for TreeAndJoint { - fn from(tree: TokenTree) -> TreeAndJoint { - (tree, NonJoint) +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<TreeAndJoint>>()) + TokenStream::new(iter.into_iter().map(Into::into).collect::<Vec<TreeAndSpacing>>()) } } @@ -209,7 +207,7 @@ impl PartialEq<TokenStream> for TokenStream { } impl TokenStream { - pub fn new(streams: Vec<TreeAndJoint>) -> TokenStream { + pub fn new(streams: Vec<TreeAndSpacing>) -> TokenStream { TokenStream(Lrc::new(streams)) } @@ -320,11 +318,11 @@ impl TokenStreamBuilder { // If `self` is not empty and the last tree within the last stream is a // token tree marked with `Joint`... if let Some(TokenStream(ref mut last_stream_lrc)) = self.0.last_mut() { - if let Some((TokenTree::Token(last_token), Joint)) = last_stream_lrc.last() { + if let Some((TokenTree::Token(last_token), Spacing::Joint)) = last_stream_lrc.last() { // ...and `stream` is not empty and the first tree within it is // a token tree... let TokenStream(ref mut stream_lrc) = stream; - if let Some((TokenTree::Token(token), is_joint)) = stream_lrc.first() { + if let Some((TokenTree::Token(token), spacing)) = stream_lrc.first() { // ...and the two tokens can be glued together... if let Some(glued_tok) = last_token.glue(&token) { // ...then do so, by overwriting the last token @@ -337,8 +335,7 @@ impl TokenStreamBuilder { // Overwrite the last token tree with the merged // token. let last_vec_mut = Lrc::make_mut(last_stream_lrc); - *last_vec_mut.last_mut().unwrap() = - (TokenTree::Token(glued_tok), *is_joint); + *last_vec_mut.last_mut().unwrap() = (TokenTree::Token(glued_tok), *spacing); // Remove the first token tree from `stream`. (This // is almost always the only tree in `stream`.) @@ -375,7 +372,7 @@ impl Iterator for Cursor { type Item = TokenTree; fn next(&mut self) -> Option<TokenTree> { - self.next_with_joint().map(|(tree, _)| tree) + self.next_with_spacing().map(|(tree, _)| tree) } } @@ -384,7 +381,7 @@ impl Cursor { Cursor { stream, index: 0 } } - pub fn next_with_joint(&mut self) -> Option<TreeAndJoint> { + pub fn next_with_spacing(&mut self) -> Option<TreeAndSpacing> { if self.index < self.stream.len() { self.index += 1; Some(self.stream.0[self.index - 1].clone()) diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 234ce280f97..86fd87f6c42 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -879,7 +879,7 @@ pub fn walk_arm<'a, V: Visitor<'a>>(visitor: &mut V, arm: &'a Arm) { } pub fn walk_vis<'a, V: Visitor<'a>>(visitor: &mut V, vis: &'a Visibility) { - if let VisibilityKind::Restricted { ref path, id } = vis.node { + if let VisibilityKind::Restricted { ref path, id } = vis.kind { visitor.visit_path(path, id); } } diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index f3309afec7d..6d41b7836b1 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -251,7 +251,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ItemKind::ExternCrate(orig_name) => hir::ItemKind::ExternCrate(orig_name), ItemKind::Use(ref use_tree) => { // Start with an empty prefix. - let prefix = Path { segments: vec![], span: use_tree.span }; + let prefix = Path { segments: vec![], span: use_tree.span, tokens: None }; self.lower_use_tree(use_tree, &prefix, id, vis, ident, attrs) } @@ -488,7 +488,7 @@ impl<'hir> LoweringContext<'_, 'hir> { *ident = tree.ident(); // First, apply the prefix to the path. - let mut path = Path { segments, span: path.span }; + let mut path = Path { segments, span: path.span, tokens: None }; // Correctly resolve `self` imports. if path.segments.len() > 1 @@ -540,8 +540,11 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ItemKind::Use(path, hir::UseKind::Single) } UseTreeKind::Glob => { - let path = - self.lower_path(id, &Path { segments, span: path.span }, ParamMode::Explicit); + let path = self.lower_path( + id, + &Path { segments, span: path.span, tokens: None }, + ParamMode::Explicit, + ); hir::ItemKind::Use(path, hir::UseKind::Glob) } UseTreeKind::Nested(ref trees) => { @@ -569,7 +572,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // for that we return the `{}` import (called the // `ListStem`). - let prefix = Path { segments, span: prefix.span.to(path.span) }; + let prefix = Path { segments, span: prefix.span.to(path.span), tokens: None }; // Add all the nested `PathListItem`s to the HIR. for &(ref use_tree, id) in trees { @@ -927,7 +930,7 @@ impl<'hir> LoweringContext<'_, 'hir> { v: &Visibility, explicit_owner: Option<NodeId>, ) -> hir::Visibility<'hir> { - let node = match v.node { + let node = match v.kind { VisibilityKind::Public => hir::VisibilityKind::Public, VisibilityKind::Crate(sugar) => hir::VisibilityKind::Crate(sugar), VisibilityKind::Restricted { ref path, id } => { diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 586355fe613..a28d022c661 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -967,6 +967,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { AttrKind::Normal(ref item) => AttrKind::Normal(AttrItem { path: item.path.clone(), args: self.lower_mac_args(&item.args), + tokens: None, }), AttrKind::DocComment(comment_kind, data) => AttrKind::DocComment(comment_kind, data), }; @@ -1106,6 +1107,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { id: node_id, kind: TyKind::ImplTrait(impl_trait_node_id, bounds.clone()), span: constraint.span, + tokens: None, }, itctx, ); diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index a01dd8c939c..31c05325d1d 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -198,13 +198,13 @@ impl<'a> AstValidator<'a> { } fn invalid_visibility(&self, vis: &Visibility, note: Option<&str>) { - if let VisibilityKind::Inherited = vis.node { + if let VisibilityKind::Inherited = vis.kind { return; } let mut err = struct_span_err!(self.session, vis.span, E0449, "unnecessary visibility qualifier"); - if vis.node.is_pub() { + if vis.kind.is_pub() { err.span_label(vis.span, "`pub` not permitted here because it's implied"); } if let Some(note) = note { @@ -990,12 +990,15 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.error_item_without_body(item.span, "function", msg, " { <body> }"); } } - ItemKind::ForeignMod(_) => { + ItemKind::ForeignMod(ForeignMod { unsafety, .. }) => { let old_item = mem::replace(&mut self.extern_mod, Some(item)); self.invalid_visibility( &item.vis, Some("place qualifiers on individual foreign items instead"), ); + if let Unsafe::Yes(span) = unsafety { + self.err_handler().span_err(span, "extern block cannot be declared unsafe"); + } visit::walk_item(self, item); self.extern_mod = old_item; return; // Avoid visiting again. @@ -1029,7 +1032,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> { walk_list!(self, visit_attribute, &item.attrs); return; } - ItemKind::Mod(Mod { inline, .. }) => { + ItemKind::Mod(Mod { inline, unsafety, .. }) => { + if let Unsafe::Yes(span) = unsafety { + self.err_handler().span_err(span, "module cannot be declared unsafe"); + } // Ensure that `path` attributes on modules are recorded as used (cf. issue #35584). if !inline && !self.session.contains_name(&item.attrs, sym::path) { self.check_mod_file_item_asciionly(item.ident); diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 0ee8ef55e61..40643da2881 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -594,7 +594,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } fn visit_vis(&mut self, vis: &'a ast::Visibility) { - if let ast::VisibilityKind::Crate(ast::CrateSugar::JustCrate) = vis.node { + if let ast::VisibilityKind::Crate(ast::CrateSugar::JustCrate) = vis.kind { gate_feature_post!( &self, crate_visibility_modifier, @@ -608,6 +608,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { pub fn check_crate(krate: &ast::Crate, sess: &Session) { maybe_stage_features(sess, krate); + check_incompatible_features(sess); let mut visitor = PostExpansionVisitor { sess, features: &sess.features_untracked() }; let spans = sess.parse_sess.gated_spans.spans.borrow(); @@ -677,3 +678,36 @@ fn maybe_stage_features(sess: &Session, krate: &ast::Crate) { } } } + +fn check_incompatible_features(sess: &Session) { + let features = sess.features_untracked(); + + let declared_features = features + .declared_lang_features + .iter() + .copied() + .map(|(name, span, _)| (name, span)) + .chain(features.declared_lib_features.iter().copied()); + + for (f1, f2) in rustc_feature::INCOMPATIBLE_FEATURES + .iter() + .filter(|&&(f1, f2)| features.enabled(f1) && features.enabled(f2)) + { + if let Some((f1_name, f1_span)) = declared_features.clone().find(|(name, _)| name == f1) { + if let Some((f2_name, f2_span)) = declared_features.clone().find(|(name, _)| name == f2) + { + let spans = vec![f1_span, f2_span]; + sess.struct_span_err( + spans.clone(), + &format!( + "features `{}` and `{}` are incompatible, using them at the same time \ + is not allowed", + f1_name, f2_name + ), + ) + .help("remove one of these features") + .emit(); + } + } + } +} diff --git a/compiler/rustc_ast_pretty/src/pprust.rs b/compiler/rustc_ast_pretty/src/pprust.rs index 9743a000429..d16b541c699 100644 --- a/compiler/rustc_ast_pretty/src/pprust.rs +++ b/compiler/rustc_ast_pretty/src/pprust.rs @@ -1139,7 +1139,11 @@ impl<'a> State<'a> { self.print_fn_full(sig, item.ident, gen, &item.vis, def, body, &item.attrs); } ast::ItemKind::Mod(ref _mod) => { - self.head(visibility_qualified(&item.vis, "mod")); + self.head(to_string(|s| { + s.print_visibility(&item.vis); + s.print_unsafety(_mod.unsafety); + s.word("mod"); + })); self.print_ident(item.ident); if _mod.inline || self.is_expanded { @@ -1154,7 +1158,10 @@ impl<'a> State<'a> { } } ast::ItemKind::ForeignMod(ref nmod) => { - self.head("extern"); + self.head(to_string(|s| { + s.print_unsafety(nmod.unsafety); + s.word("extern"); + })); if let Some(abi) = nmod.abi { self.print_literal(&abi.as_lit()); self.nbsp(); @@ -1352,7 +1359,7 @@ impl<'a> State<'a> { } crate fn print_visibility(&mut self, vis: &ast::Visibility) { - match vis.node { + match vis.kind { ast::VisibilityKind::Public => self.word_nbsp("pub"), ast::VisibilityKind::Crate(sugar) => match sugar { ast::CrateSugar::PubCrate => self.word_nbsp("pub(crate)"), diff --git a/compiler/rustc_ast_pretty/src/pprust/tests.rs b/compiler/rustc_ast_pretty/src/pprust/tests.rs index fdbf3feb900..b1a73a0bf02 100644 --- a/compiler/rustc_ast_pretty/src/pprust/tests.rs +++ b/compiler/rustc_ast_pretty/src/pprust/tests.rs @@ -1,7 +1,6 @@ use super::*; use rustc_ast as ast; -use rustc_span::source_map::respan; use rustc_span::symbol::Ident; use rustc_span::with_default_session_globals; @@ -45,7 +44,11 @@ fn test_variant_to_string() { let var = ast::Variant { ident, - vis: respan(rustc_span::DUMMY_SP, ast::VisibilityKind::Inherited), + vis: ast::Visibility { + span: rustc_span::DUMMY_SP, + kind: ast::VisibilityKind::Inherited, + tokens: None, + }, attrs: Vec::new(), id: ast::DUMMY_NODE_ID, data: ast::VariantData::Unit(ast::DUMMY_NODE_ID), diff --git a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs index 34e2accc615..5ed8b69d92a 100644 --- a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs +++ b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs @@ -15,7 +15,7 @@ pub fn inject(mut krate: ast::Crate, parse_sess: &ParseSess, attrs: &[String]) - ); let start_span = parser.token.span; - let AttrItem { path, args } = match parser.parse_attr_item() { + let AttrItem { path, args, tokens: _ } = match parser.parse_attr_item() { Ok(ai) => ai, Err(mut err) => { err.emit(); diff --git a/compiler/rustc_builtin_macros/src/concat_idents.rs b/compiler/rustc_builtin_macros/src/concat_idents.rs index 8223cdda072..c4d1c6eee31 100644 --- a/compiler/rustc_builtin_macros/src/concat_idents.rs +++ b/compiler/rustc_builtin_macros/src/concat_idents.rs @@ -61,6 +61,7 @@ pub fn expand_concat_idents<'cx>( id: ast::DUMMY_NODE_ID, kind: ast::TyKind::Path(None, ast::Path::from_ident(self.ident)), span: self.ident.span, + tokens: None, })) } } diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs index 120e859f2b1..d84b3956475 100644 --- a/compiler/rustc_builtin_macros/src/deriving/debug.rs +++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs @@ -133,5 +133,5 @@ fn stmt_let_underscore(cx: &mut ExtCtxt<'_>, sp: Span, expr: P<ast::Expr>) -> as span: sp, attrs: ast::AttrVec::new(), }); - ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span: sp } + ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span: sp, tokens: None } } diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 849e8b136e1..d235caec103 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -187,7 +187,6 @@ use rustc_ast::{GenericArg, GenericParamKind, VariantData}; use rustc_attr as attr; use rustc_data_structures::map_in_place::MapInPlace; use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_span::source_map::respan; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; @@ -532,7 +531,11 @@ impl<'a> TraitDef<'a> { id: ast::DUMMY_NODE_ID, span: self.span, ident, - vis: respan(self.span.shrink_to_lo(), ast::VisibilityKind::Inherited), + vis: ast::Visibility { + span: self.span.shrink_to_lo(), + kind: ast::VisibilityKind::Inherited, + tokens: None, + }, attrs: Vec::new(), kind: ast::AssocItemKind::TyAlias( ast::Defaultness::Final, @@ -933,7 +936,11 @@ impl<'a> MethodDef<'a> { id: ast::DUMMY_NODE_ID, attrs: self.attributes.clone(), span: trait_.span, - vis: respan(trait_lo_sp, ast::VisibilityKind::Inherited), + vis: ast::Visibility { + span: trait_lo_sp, + kind: ast::VisibilityKind::Inherited, + tokens: None, + }, ident: method_ident, kind: ast::AssocItemKind::Fn(def, sig, fn_generics, Some(body_block)), tokens: None, diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs index 7e3fd131d44..9c8e0fc2f01 100644 --- a/compiler/rustc_builtin_macros/src/deriving/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -75,6 +75,7 @@ fn call_intrinsic( id: ast::DUMMY_NODE_ID, rules: ast::BlockCheckMode::Unsafe(ast::CompilerGenerated), span, + tokens: None, })) } diff --git a/compiler/rustc_builtin_macros/src/format_foreign.rs b/compiler/rustc_builtin_macros/src/format_foreign.rs index 85cf4c42e94..b39423b86e7 100644 --- a/compiler/rustc_builtin_macros/src/format_foreign.rs +++ b/compiler/rustc_builtin_macros/src/format_foreign.rs @@ -166,14 +166,14 @@ pub mod printf { let cap = self.span.len() + if has_options { 2 } else { 0 }; let mut s = String::with_capacity(cap); - s.push_str("{"); + s.push('{'); if let Some(arg) = self.parameter { write!(s, "{}", arg.checked_sub(1)?).ok()?; } if has_options { - s.push_str(":"); + s.push(':'); let align = if let Some(fill) = fill { s.push_str(fill); @@ -191,11 +191,11 @@ pub mod printf { } if alt { - s.push_str("#"); + s.push('#'); } if zero_fill { - s.push_str("0"); + s.push('0'); } if let Some(width) = width { @@ -203,7 +203,7 @@ pub mod printf { } if let Some(precision) = precision { - s.push_str("."); + s.push('.'); precision.translate(&mut s).ok()?; } @@ -212,7 +212,7 @@ pub mod printf { } } - s.push_str("}"); + s.push('}'); Some(s) } } diff --git a/compiler/rustc_builtin_macros/src/global_asm.rs b/compiler/rustc_builtin_macros/src/global_asm.rs index 2465f33622e..3689e33be6f 100644 --- a/compiler/rustc_builtin_macros/src/global_asm.rs +++ b/compiler/rustc_builtin_macros/src/global_asm.rs @@ -14,7 +14,6 @@ use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; use rustc_errors::DiagnosticBuilder; use rustc_expand::base::{self, *}; -use rustc_span::source_map::respan; use rustc_span::symbol::Ident; use rustc_span::Span; use smallvec::smallvec; @@ -30,7 +29,11 @@ pub fn expand_global_asm<'cx>( attrs: Vec::new(), id: ast::DUMMY_NODE_ID, kind: ast::ItemKind::GlobalAsm(P(global_asm)), - vis: respan(sp.shrink_to_lo(), ast::VisibilityKind::Inherited), + vis: ast::Visibility { + span: sp.shrink_to_lo(), + kind: ast::VisibilityKind::Inherited, + tokens: None, + }, span: cx.with_def_site_ctxt(sp), tokens: None, })]), diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs index 0c6769906f3..c6ab3faf568 100644 --- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs +++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs @@ -98,7 +98,7 @@ pub fn inject( impl<'a> CollectProcMacros<'a> { fn check_not_pub_in_root(&self, vis: &ast::Visibility, sp: Span) { - if self.is_proc_macro_crate && self.in_root && vis.node.is_pub() { + if self.is_proc_macro_crate && self.in_root && vis.kind.is_pub() { self.handler.span_err( sp, "`proc-macro` crate types currently cannot export any items other \ @@ -184,7 +184,7 @@ impl<'a> CollectProcMacros<'a> { Vec::new() }; - if self.in_root && item.vis.node.is_pub() { + if self.in_root && item.vis.kind.is_pub() { self.macros.push(ProcMacro::Derive(ProcMacroDerive { id: item.id, span: item.span, @@ -204,7 +204,7 @@ impl<'a> CollectProcMacros<'a> { } fn collect_attr_proc_macro(&mut self, item: &'a ast::Item) { - if self.in_root && item.vis.node.is_pub() { + if self.in_root && item.vis.kind.is_pub() { self.macros.push(ProcMacro::Def(ProcMacroDef { id: item.id, span: item.span, @@ -223,7 +223,7 @@ impl<'a> CollectProcMacros<'a> { } fn collect_bang_proc_macro(&mut self, item: &'a ast::Item) { - if self.in_root && item.vis.node.is_pub() { + if self.in_root && item.vis.kind.is_pub() { self.macros.push(ProcMacro::Def(ProcMacroDef { id: item.id, span: item.span, diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index 8e56e80bba2..1de0b32f519 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -7,7 +7,6 @@ use rustc_ast::attr; use rustc_ast_pretty::pprust; use rustc_expand::base::*; use rustc_session::Session; -use rustc_span::source_map::respan; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::Span; @@ -35,7 +34,11 @@ pub fn expand_test_case( let sp = ecx.with_def_site_ctxt(attr_sp); let mut item = anno_item.expect_item(); item = item.map(|mut item| { - item.vis = respan(item.vis.span, ast::VisibilityKind::Public); + item.vis = ast::Visibility { + span: item.vis.span, + kind: ast::VisibilityKind::Public, + tokens: None, + }; item.ident.span = item.ident.span.with_ctxt(sp.ctxt()); item.attrs.push(ecx.attribute(ecx.meta_word(sp, sym::rustc_test_marker))); item @@ -292,7 +295,7 @@ pub fn expand_test_or_bench( ), ); test_const = test_const.map(|mut tc| { - tc.vis.node = ast::VisibilityKind::Public; + tc.vis.kind = ast::VisibilityKind::Public; tc }); diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 0ea60665d67..0a60ca8faaa 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -10,7 +10,6 @@ use rustc_expand::expand::{AstFragment, ExpansionConfig}; use rustc_feature::Features; use rustc_session::Session; use rustc_span::hygiene::{AstPass, SyntaxContext, Transparency}; -use rustc_span::source_map::respan; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; use rustc_target::spec::PanicStrategy; @@ -333,7 +332,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> { attrs: vec![main_attr], id: ast::DUMMY_NODE_ID, kind: main, - vis: respan(sp, ast::VisibilityKind::Public), + vis: ast::Visibility { span: sp, kind: ast::VisibilityKind::Public, tokens: None }, span: sp, tokens: None, }); diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index 38f552558c8..04792b334d5 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -25,7 +25,7 @@ rustc_fs_util = { path = "../rustc_fs_util" } rustc_hir = { path = "../rustc_hir" } rustc_incremental = { path = "../rustc_incremental" } rustc_index = { path = "../rustc_index" } -rustc_llvm = { path = "../../src/librustc_llvm" } +rustc_llvm = { path = "../rustc_llvm" } rustc_session = { path = "../rustc_session" } rustc_serialize = { path = "../rustc_serialize" } rustc_target = { path = "../rustc_target" } diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 7c710a1cb3d..4b2d5907a02 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -346,14 +346,14 @@ fn fat_lto( Ok(LtoModuleCodegen::Fat { module: Some(module), _serialized_bitcode: serialized_bitcode }) } -struct Linker<'a>(&'a mut llvm::Linker<'a>); +crate struct Linker<'a>(&'a mut llvm::Linker<'a>); impl Linker<'a> { - fn new(llmod: &'a llvm::Module) -> Self { + crate fn new(llmod: &'a llvm::Module) -> Self { unsafe { Linker(llvm::LLVMRustLinkerNew(llmod)) } } - fn add(&mut self, bytecode: &[u8]) -> Result<(), ()> { + crate fn add(&mut self, bytecode: &[u8]) -> Result<(), ()> { unsafe { if llvm::LLVMRustLinkerAdd( self.0, diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 6f386c1287c..937821e9d4f 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -617,6 +617,31 @@ unsafe fn add_sanitizer_passes(config: &ModuleConfig, passes: &mut Vec<&'static } } +pub(crate) fn link( + cgcx: &CodegenContext<LlvmCodegenBackend>, + diag_handler: &Handler, + mut modules: Vec<ModuleCodegen<ModuleLlvm>>, +) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> { + use super::lto::{Linker, ModuleBuffer}; + // Sort the modules by name to ensure to ensure deterministic behavior. + modules.sort_by(|a, b| a.name.cmp(&b.name)); + let (first, elements) = + modules.split_first().expect("Bug! modules must contain at least one module."); + + let mut linker = Linker::new(first.module_llvm.llmod()); + for module in elements { + let _timer = + cgcx.prof.generic_activity_with_arg("LLVM_link_module", format!("{:?}", module.name)); + let buffer = ModuleBuffer::new(module.module_llvm.llmod()); + linker.add(&buffer.data()).map_err(|()| { + let msg = format!("failed to serialize module {:?}", module.name); + llvm_err(&diag_handler, &msg) + })?; + } + drop(linker); + Ok(modules.remove(0)) +} + pub(crate) unsafe fn codegen( cgcx: &CodegenContext<LlvmCodegenBackend>, diag_handler: &Handler, diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 67d4b2642c0..2e2abe9fb30 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -130,6 +130,13 @@ impl WriteBackendMethods for LlvmCodegenBackend { llvm::LLVMRustPrintPassTimings(); } } + fn run_link( + cgcx: &CodegenContext<Self>, + diag_handler: &Handler, + modules: Vec<ModuleCodegen<Self::Module>>, + ) -> Result<ModuleCodegen<Self::Module>, FatalError> { + back::write::link(cgcx, diag_handler, modules) + } fn run_fat_lto( cgcx: &CodegenContext<Self>, modules: Vec<FatLTOInput<Self>>, diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 32822eba930..4942c997682 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -96,7 +96,7 @@ pub enum DLLStorageClass { DllExport = 2, // Function to be accessible from DLL. } -/// Matches LLVMRustAttribute in rustllvm.h +/// Matches LLVMRustAttribute in LLVMWrapper.h /// Semantically a subset of the C++ enum llvm::Attribute::AttrKind, /// though it is not ABI compatible (since it's a C++ enum) #[repr(C)] @@ -1705,7 +1705,7 @@ extern "C" { PM: &PassManager<'_>, ); - // Stuff that's in rustllvm/ because it's not upstream yet. + // Stuff that's in llvm-wrapper/ because it's not upstream yet. /// Opens an object file. pub fn LLVMCreateObjectFile( diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index c044020d930..faeb727202c 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1076,7 +1076,7 @@ fn exec_linker( } .to_string(), ); - args.push_str("\n"); + args.push('\n'); } let file = tmpdir.join("linker-arguments"); let bytes = if sess.target.target.options.is_like_msvc { diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 7d69bb983dd..0edf0fcd1a2 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -702,6 +702,7 @@ impl<B: WriteBackendMethods> WorkItem<B> { enum WorkItemResult<B: WriteBackendMethods> { Compiled(CompiledModule), + NeedsLink(ModuleCodegen<B::Module>), NeedsFatLTO(FatLTOInput<B>), NeedsThinLTO(String, B::ThinBuffer), } @@ -801,11 +802,8 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>( None }; - Ok(match lto_type { - ComputedLtoType::No => { - let module = unsafe { B::codegen(cgcx, &diag_handler, module, module_config)? }; - WorkItemResult::Compiled(module) - } + match lto_type { + ComputedLtoType::No => finish_intra_module_work(cgcx, module, module_config), ComputedLtoType::Thin => { let (name, thin_buffer) = B::prepare_thin(module); if let Some(path) = bitcode { @@ -813,7 +811,7 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>( panic!("Error writing pre-lto-bitcode file `{}`: {}", path.display(), e); }); } - WorkItemResult::NeedsThinLTO(name, thin_buffer) + Ok(WorkItemResult::NeedsThinLTO(name, thin_buffer)) } ComputedLtoType::Fat => match bitcode { Some(path) => { @@ -821,11 +819,11 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>( fs::write(&path, buffer.data()).unwrap_or_else(|e| { panic!("Error writing pre-lto-bitcode file `{}`: {}", path.display(), e); }); - WorkItemResult::NeedsFatLTO(FatLTOInput::Serialized { name, buffer }) + Ok(WorkItemResult::NeedsFatLTO(FatLTOInput::Serialized { name, buffer })) } - None => WorkItemResult::NeedsFatLTO(FatLTOInput::InMemory(module)), + None => Ok(WorkItemResult::NeedsFatLTO(FatLTOInput::InMemory(module))), }, - }) + } } fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>( @@ -871,12 +869,25 @@ fn execute_lto_work_item<B: ExtraBackendMethods>( mut module: lto::LtoModuleCodegen<B>, module_config: &ModuleConfig, ) -> Result<WorkItemResult<B>, FatalError> { + let module = unsafe { module.optimize(cgcx)? }; + finish_intra_module_work(cgcx, module, module_config) +} + +fn finish_intra_module_work<B: ExtraBackendMethods>( + cgcx: &CodegenContext<B>, + module: ModuleCodegen<B::Module>, + module_config: &ModuleConfig, +) -> Result<WorkItemResult<B>, FatalError> { let diag_handler = cgcx.create_diag_handler(); - unsafe { - let module = module.optimize(cgcx)?; - let module = B::codegen(cgcx, &diag_handler, module, module_config)?; + if !cgcx.opts.debugging_opts.combine_cgu + || module.kind == ModuleKind::Metadata + || module.kind == ModuleKind::Allocator + { + let module = unsafe { B::codegen(cgcx, &diag_handler, module, module_config)? }; Ok(WorkItemResult::Compiled(module)) + } else { + Ok(WorkItemResult::NeedsLink(module)) } } @@ -891,6 +902,10 @@ pub enum Message<B: WriteBackendMethods> { thin_buffer: B::ThinBuffer, worker_id: usize, }, + NeedsLink { + module: ModuleCodegen<B::Module>, + worker_id: usize, + }, Done { result: Result<CompiledModule, Option<WorkerFatalError>>, worker_id: usize, @@ -1178,6 +1193,7 @@ fn start_executing_work<B: ExtraBackendMethods>( let mut compiled_modules = vec![]; let mut compiled_metadata_module = None; let mut compiled_allocator_module = None; + let mut needs_link = Vec::new(); let mut needs_fat_lto = Vec::new(); let mut needs_thin_lto = Vec::new(); let mut lto_import_only_modules = Vec::new(); @@ -1434,6 +1450,10 @@ fn start_executing_work<B: ExtraBackendMethods>( } } } + Message::NeedsLink { module, worker_id } => { + free_worker(worker_id); + needs_link.push(module); + } Message::NeedsFatLTO { result, worker_id } => { assert!(!started_lto); free_worker(worker_id); @@ -1462,6 +1482,18 @@ fn start_executing_work<B: ExtraBackendMethods>( } } + let needs_link = mem::take(&mut needs_link); + if !needs_link.is_empty() { + assert!(compiled_modules.is_empty()); + let diag_handler = cgcx.create_diag_handler(); + let module = B::run_link(&cgcx, &diag_handler, needs_link).map_err(|_| ())?; + let module = unsafe { + B::codegen(&cgcx, &diag_handler, module, cgcx.config(ModuleKind::Regular)) + .map_err(|_| ())? + }; + compiled_modules.push(module); + } + // Drop to print timings drop(llvm_start_time); @@ -1521,6 +1553,9 @@ fn spawn_work<B: ExtraBackendMethods>(cgcx: CodegenContext<B>, work: WorkItem<B> Some(Ok(WorkItemResult::Compiled(m))) => { Message::Done::<B> { result: Ok(m), worker_id } } + Some(Ok(WorkItemResult::NeedsLink(m))) => { + Message::NeedsLink::<B> { module: m, worker_id } + } Some(Ok(WorkItemResult::NeedsFatLTO(m))) => { Message::NeedsFatLTO::<B> { result: m, worker_id } } diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 54e5d4d00f6..0c0f1bc681c 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -37,7 +37,7 @@ pub fn push_debuginfo_type_name<'tcx>( ty::Bool => output.push_str("bool"), ty::Char => output.push_str("char"), ty::Str => output.push_str("str"), - ty::Never => output.push_str("!"), + ty::Never => output.push('!'), ty::Int(int_ty) => output.push_str(int_ty.name_str()), ty::Uint(uint_ty) => output.push_str(uint_ty.name_str()), ty::Float(float_ty) => output.push_str(float_ty.name_str()), diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index 27d52e9b9c5..264e7c2aa92 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -13,6 +13,12 @@ pub trait WriteBackendMethods: 'static + Sized + Clone { type ThinData: Send + Sync; type ThinBuffer: ThinBufferMethods; + /// Merge all modules into main_module and returning it + fn run_link( + cgcx: &CodegenContext<Self>, + diag_handler: &Handler, + modules: Vec<ModuleCodegen<Self::Module>>, + ) -> Result<ModuleCodegen<Self::Module>, FatalError>; /// Performs fat LTO by merging all modules into a single one and returning it /// for further optimization. fn run_fat_lto( diff --git a/compiler/rustc_data_structures/src/graph/iterate/mod.rs b/compiler/rustc_data_structures/src/graph/iterate/mod.rs index 64ff6130ddf..bc3d1ce53ba 100644 --- a/compiler/rustc_data_structures/src/graph/iterate/mod.rs +++ b/compiler/rustc_data_structures/src/graph/iterate/mod.rs @@ -87,11 +87,8 @@ where } /// Allows searches to terminate early with a value. -#[derive(Clone, Copy, Debug)] -pub enum ControlFlow<T> { - Break(T), - Continue, -} +// FIXME (#75744): remove the alias once the generics are in a better order and `C=()`. +pub type ControlFlow<T> = std::ops::ControlFlow<(), T>; /// The status of a node in the depth-first search. /// @@ -260,12 +257,12 @@ where _node: G::Node, _prior_status: Option<NodeStatus>, ) -> ControlFlow<Self::BreakVal> { - ControlFlow::Continue + ControlFlow::CONTINUE } /// Called after all nodes reachable from this one have been examined. fn node_settled(&mut self, _node: G::Node) -> ControlFlow<Self::BreakVal> { - ControlFlow::Continue + ControlFlow::CONTINUE } /// Behave as if no edges exist from `source` to `target`. @@ -289,8 +286,8 @@ where prior_status: Option<NodeStatus>, ) -> ControlFlow<Self::BreakVal> { match prior_status { - Some(NodeStatus::Visited) => ControlFlow::Break(()), - _ => ControlFlow::Continue, + Some(NodeStatus::Visited) => ControlFlow::BREAK, + _ => ControlFlow::CONTINUE, } } } diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index de4e7a13424..88c160e93b6 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -8,6 +8,7 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] #![allow(incomplete_features)] +#![feature(control_flow_enum)] #![feature(in_band_lifetimes)] #![feature(unboxed_closures)] #![feature(generators)] diff --git a/compiler/rustc_data_structures/src/temp_dir.rs b/compiler/rustc_data_structures/src/temp_dir.rs index 0d9b3e3ca25..a780d2386a6 100644 --- a/compiler/rustc_data_structures/src/temp_dir.rs +++ b/compiler/rustc_data_structures/src/temp_dir.rs @@ -12,7 +12,7 @@ pub struct MaybeTempDir { impl Drop for MaybeTempDir { fn drop(&mut self) { - // Safety: We are in the destructor, and no further access will + // SAFETY: We are in the destructor, and no further access will // occur. let dir = unsafe { ManuallyDrop::take(&mut self.dir) }; if self.keep { diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs index 789a1fc35a6..b0be1bf7e72 100644 --- a/compiler/rustc_error_codes/src/error_codes.rs +++ b/compiler/rustc_error_codes/src/error_codes.rs @@ -455,6 +455,7 @@ E0769: include_str!("./error_codes/E0769.md"), E0770: include_str!("./error_codes/E0770.md"), E0771: include_str!("./error_codes/E0771.md"), E0773: include_str!("./error_codes/E0773.md"), +E0774: include_str!("./error_codes/E0774.md"), ; // E0006, // merged with E0005 // E0008, // cannot bind by-move into a pattern guard diff --git a/compiler/rustc_error_codes/src/error_codes/E0224.md b/compiler/rustc_error_codes/src/error_codes/E0224.md index fd89c1d5256..628488575b2 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0224.md +++ b/compiler/rustc_error_codes/src/error_codes/E0224.md @@ -1,4 +1,4 @@ -A trait object was declaired with no traits. +A trait object was declared with no traits. Erroneous code example: @@ -8,7 +8,7 @@ type Foo = dyn 'static +; Rust does not currently support this. -To solve ensure the the trait object has at least one trait: +To solve, ensure that the trait object has at least one trait: ``` type Foo = dyn 'static + Copy; diff --git a/compiler/rustc_error_codes/src/error_codes/E0433.md b/compiler/rustc_error_codes/src/error_codes/E0433.md index f9e333e8ccd..5a64c13c9af 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0433.md +++ b/compiler/rustc_error_codes/src/error_codes/E0433.md @@ -1,17 +1,27 @@ -An undeclared type or module was used. +An undeclared crate, module, or type was used. Erroneous code example: ```compile_fail,E0433 let map = HashMap::new(); -// error: failed to resolve: use of undeclared type or module `HashMap` +// error: failed to resolve: use of undeclared type `HashMap` ``` Please verify you didn't misspell the type/module's name or that you didn't forget to import it: - ``` use std::collections::HashMap; // HashMap has been imported. let map: HashMap<u32, u32> = HashMap::new(); // So it can be used! ``` + +If you've expected to use a crate name: + +```compile_fail +use ferris_wheel::BigO; +// error: failed to resolve: use of undeclared crate or module `ferris_wheel` +``` + +Make sure the crate has been added as a dependency in `Cargo.toml`. + +To use a module from your current crate, add the `crate::` prefix to the path. diff --git a/compiler/rustc_error_codes/src/error_codes/E0607.md b/compiler/rustc_error_codes/src/error_codes/E0607.md index ea6e10105b0..0545246929f 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0607.md +++ b/compiler/rustc_error_codes/src/error_codes/E0607.md @@ -12,15 +12,15 @@ First: what are thin and fat pointers? Thin pointers are "simple" pointers: they are purely a reference to a memory address. -Fat pointers are pointers referencing Dynamically Sized Types (also called DST). -DST don't have a statically known size, therefore they can only exist behind -some kind of pointers that contain additional information. Slices and trait -objects are DSTs. In the case of slices, the additional information the fat -pointer holds is their size. +Fat pointers are pointers referencing Dynamically Sized Types (also called +DSTs). DSTs don't have a statically known size, therefore they can only exist +behind some kind of pointer that contains additional information. For example, +slices and trait objects are DSTs. In the case of slices, the additional +information the fat pointer holds is their size. To fix this error, don't try to cast directly between thin and fat pointers. -For more information about casts, take a look at the Type cast section in -[The Reference Book][1]. +For more information about type casts, take a look at the section of the +[The Rust Reference][1] on type cast expressions. [1]: https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions diff --git a/compiler/rustc_error_codes/src/error_codes/E0774.md b/compiler/rustc_error_codes/src/error_codes/E0774.md new file mode 100644 index 00000000000..79793ba9d7d --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0774.md @@ -0,0 +1,24 @@ +`derive` was applied on something which is not a struct, a union or an enum. + +Erroneous code example: + +```compile_fail,E0774 +trait Foo { + #[derive(Clone)] // error! + type Bar; +} +``` + +As said above, the `derive` attribute is only allowed on structs, unions or +enums: + +``` +#[derive(Clone)] // ok! +struct Bar { + field: u32, +} +``` + +You can find more information about `derive` in the [Rust Book]. + +[Rust Book]: https://doc.rust-lang.org/book/appendix-03-derivable-traits.html diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 4c01cb8159a..926e3dbfc52 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -400,6 +400,7 @@ macro_rules! make_stmts_default { id: ast::DUMMY_NODE_ID, span: e.span, kind: ast::StmtKind::Expr(e), + tokens: None }] }) }; @@ -607,6 +608,7 @@ impl DummyResult { id: ast::DUMMY_NODE_ID, kind: if is_error { ast::TyKind::Err } else { ast::TyKind::Tup(Vec::new()) }, span: sp, + tokens: None, }) } } @@ -641,6 +643,7 @@ impl MacResult for DummyResult { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Expr(DummyResult::raw_expr(self.span, self.is_error)), span: self.span, + tokens: None }]) } diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 9490b62aa17..a5a7ee6c9a3 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -3,7 +3,7 @@ use crate::base::ExtCtxt; use rustc_ast::attr; use rustc_ast::ptr::P; use rustc_ast::{self as ast, AttrVec, BlockCheckMode, Expr, PatKind, UnOp}; -use rustc_span::source_map::{respan, Spanned}; +use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; @@ -46,7 +46,7 @@ impl<'a> ExtCtxt<'a> { id: ast::DUMMY_NODE_ID, args, }); - ast::Path { span, segments } + ast::Path { span, segments, tokens: None } } pub fn ty_mt(&self, ty: P<ast::Ty>, mutbl: ast::Mutability) -> ast::MutTy { @@ -54,7 +54,7 @@ impl<'a> ExtCtxt<'a> { } pub fn ty(&self, span: Span, kind: ast::TyKind) -> P<ast::Ty> { - P(ast::Ty { id: ast::DUMMY_NODE_ID, span, kind }) + P(ast::Ty { id: ast::DUMMY_NODE_ID, span, kind, tokens: None }) } pub fn ty_path(&self, path: ast::Path) -> P<ast::Ty> { @@ -158,7 +158,12 @@ impl<'a> ExtCtxt<'a> { } pub fn stmt_expr(&self, expr: P<ast::Expr>) -> ast::Stmt { - ast::Stmt { id: ast::DUMMY_NODE_ID, span: expr.span, kind: ast::StmtKind::Expr(expr) } + ast::Stmt { + id: ast::DUMMY_NODE_ID, + span: expr.span, + kind: ast::StmtKind::Expr(expr), + tokens: None, + } } pub fn stmt_let(&self, sp: Span, mutbl: bool, ident: Ident, ex: P<ast::Expr>) -> ast::Stmt { @@ -176,7 +181,12 @@ impl<'a> ExtCtxt<'a> { span: sp, attrs: AttrVec::new(), }); - ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span: sp } + ast::Stmt { + id: ast::DUMMY_NODE_ID, + kind: ast::StmtKind::Local(local), + span: sp, + tokens: None, + } } // Generates `let _: Type;`, which is usually used for type assertions. @@ -189,11 +199,16 @@ impl<'a> ExtCtxt<'a> { span, attrs: AttrVec::new(), }); - ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span } + ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span, tokens: None } } pub fn stmt_item(&self, sp: Span, item: P<ast::Item>) -> ast::Stmt { - ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Item(item), span: sp } + ast::Stmt { + id: ast::DUMMY_NODE_ID, + kind: ast::StmtKind::Item(item), + span: sp, + tokens: None, + } } pub fn block_expr(&self, expr: P<ast::Expr>) -> P<ast::Block> { @@ -203,11 +218,18 @@ impl<'a> ExtCtxt<'a> { id: ast::DUMMY_NODE_ID, span: expr.span, kind: ast::StmtKind::Expr(expr), + tokens: None, }], ) } pub fn block(&self, span: Span, stmts: Vec<ast::Stmt>) -> P<ast::Block> { - P(ast::Block { stmts, id: ast::DUMMY_NODE_ID, rules: BlockCheckMode::Default, span }) + P(ast::Block { + stmts, + id: ast::DUMMY_NODE_ID, + rules: BlockCheckMode::Default, + span, + tokens: None, + }) } pub fn expr(&self, span: Span, kind: ast::ExprKind) -> P<ast::Expr> { @@ -578,7 +600,11 @@ impl<'a> ExtCtxt<'a> { attrs, id: ast::DUMMY_NODE_ID, kind, - vis: respan(span.shrink_to_lo(), ast::VisibilityKind::Inherited), + vis: ast::Visibility { + span: span.shrink_to_lo(), + kind: ast::VisibilityKind::Inherited, + tokens: None, + }, span, tokens: None, }) @@ -592,7 +618,11 @@ impl<'a> ExtCtxt<'a> { span: ty.span, ty, ident: None, - vis: respan(vis_span, ast::VisibilityKind::Inherited), + vis: ast::Visibility { + span: vis_span, + kind: ast::VisibilityKind::Inherited, + tokens: None, + }, attrs: Vec::new(), id: ast::DUMMY_NODE_ID, is_placeholder: false, @@ -611,7 +641,11 @@ impl<'a> ExtCtxt<'a> { disr_expr: None, id: ast::DUMMY_NODE_ID, ident, - vis: respan(vis_span, ast::VisibilityKind::Inherited), + vis: ast::Visibility { + span: vis_span, + kind: ast::VisibilityKind::Inherited, + tokens: None, + }, span, is_placeholder: false, } diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 97608a38903..dd087ab9150 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -399,7 +399,7 @@ impl<'a> StripUnconfigured<'a> { } pub fn configure_foreign_mod(&mut self, foreign_mod: &mut ast::ForeignMod) { - let ast::ForeignMod { abi: _, items } = foreign_mod; + let ast::ForeignMod { unsafety: _, abi: _, items } = foreign_mod; items.flat_map_in_place(|item| self.configure(item)); } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 105f81c6e0f..e5cfb866938 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -13,7 +13,7 @@ use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{self, AssocCtxt, Visitor}; use rustc_ast::{self as ast, AttrItem, Block, LitKind, NodeId, PatKind, Path}; -use rustc_ast::{ItemKind, MacArgs, MacCallStmt, MacStmtStyle, StmtKind}; +use rustc_ast::{ItemKind, MacArgs, MacCallStmt, MacStmtStyle, StmtKind, Unsafe}; use rustc_ast_pretty::pprust; use rustc_attr::{self as attr, is_builtin_attr, HasAttrs}; use rustc_data_structures::map_in_place::MapInPlace; @@ -26,7 +26,6 @@ use rustc_session::lint::builtin::UNUSED_DOC_COMMENTS; use rustc_session::lint::BuiltinLintDiagnostics; use rustc_session::parse::{feature_err, ParseSess}; use rustc_session::Limit; -use rustc_span::source_map::respan; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{ExpnId, FileName, Span, DUMMY_SP}; @@ -358,7 +357,11 @@ impl<'a, 'b> MacroExpander<'a, 'b> { kind: ast::ItemKind::Mod(krate.module), ident: Ident::invalid(), id: ast::DUMMY_NODE_ID, - vis: respan(krate.span.shrink_to_lo(), ast::VisibilityKind::Public), + vis: ast::Visibility { + span: krate.span.shrink_to_lo(), + kind: ast::VisibilityKind::Public, + tokens: None, + }, tokens: None, })]); @@ -370,11 +373,21 @@ impl<'a, 'b> MacroExpander<'a, 'b> { None => { // Resolution failed so we return an empty expansion krate.attrs = vec![]; - krate.module = ast::Mod { inner: orig_mod_span, items: vec![], inline: true }; + krate.module = ast::Mod { + inner: orig_mod_span, + unsafety: Unsafe::No, + items: vec![], + inline: true, + }; } Some(ast::Item { span, kind, .. }) => { krate.attrs = vec![]; - krate.module = ast::Mod { inner: orig_mod_span, items: vec![], inline: true }; + krate.module = ast::Mod { + inner: orig_mod_span, + unsafety: Unsafe::No, + items: vec![], + inline: true, + }; self.cx.span_err( span, &format!( @@ -529,9 +542,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> { fn error_derive_forbidden_on_non_adt(&self, derives: &[Path], item: &Annotatable) { let attr = self.cx.sess.find_by_name(item.attrs(), sym::derive); let span = attr.map_or(item.span(), |attr| attr.span); - let mut err = self - .cx - .struct_span_err(span, "`derive` may only be applied to structs, enums and unions"); + let mut err = rustc_errors::struct_span_err!( + self.cx.sess, + span, + E0774, + "`derive` may only be applied to structs, enums and unions", + ); if let Some(ast::Attribute { style: ast::AttrStyle::Inner, .. }) = attr { let trait_list = derives.iter().map(|t| pprust::path_to_string(t)).collect::<Vec<_>>(); let suggestion = format!("#[derive({})]", trait_list.join(", ")); @@ -1380,10 +1396,10 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { } // The placeholder expander gives ids to statements, so we avoid folding the id here. - let ast::Stmt { id, kind, span } = stmt; + let ast::Stmt { id, kind, span, tokens } = stmt; noop_flat_map_stmt_kind(kind, self) .into_iter() - .map(|kind| ast::Stmt { id, kind, span }) + .map(|kind| ast::Stmt { id, kind, span, tokens: tokens.clone() }) .collect() } @@ -1438,8 +1454,15 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { push_directory(&self.cx.sess, ident, &item.attrs, dir) } else { // We have an outline `mod foo;` so we need to parse the file. - let (new_mod, dir) = - parse_external_mod(&self.cx.sess, ident, span, dir, &mut attrs, pushed); + let (new_mod, dir) = parse_external_mod( + &self.cx.sess, + ident, + span, + old_mod.unsafety, + dir, + &mut attrs, + pushed, + ); let krate = ast::Crate { span: new_mod.inner, @@ -1757,6 +1780,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { kind: ast::AttrKind::Normal(AttrItem { path: meta.path, args: meta.kind.mac_args(meta.span), + tokens: None, }), span: at.span, id: at.id, diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index b908a12c1fc..0e5c5fe4d44 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -4,7 +4,7 @@ use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq, NamedMatch}; use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::token::{self, NtTT, Token}; -use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndJoint}; +use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndSpacing}; use rustc_ast::MacCall; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; @@ -111,7 +111,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<TreeAndJoint> = Vec::new(); + let mut result: Vec<TreeAndSpacing> = Vec::new(); let mut result_stack = Vec::new(); let mut marker = Marker(cx.current_expansion.id, transparency); diff --git a/compiler/rustc_expand/src/module.rs b/compiler/rustc_expand/src/module.rs index 1e123a2e145..171cb3fa8e6 100644 --- a/compiler/rustc_expand/src/module.rs +++ b/compiler/rustc_expand/src/module.rs @@ -1,4 +1,4 @@ -use rustc_ast::{token, Attribute, Mod}; +use rustc_ast::{token, Attribute, Mod, Unsafe}; use rustc_errors::{struct_span_err, PResult}; use rustc_parse::new_parser_from_file; use rustc_session::parse::ParseSess; @@ -42,6 +42,7 @@ crate fn parse_external_mod( sess: &Session, id: Ident, span: Span, // The span to blame on errors. + unsafety: Unsafe, Directory { mut ownership, path }: Directory, attrs: &mut Vec<Attribute>, pop_mod_stack: &mut bool, @@ -60,13 +61,16 @@ crate fn parse_external_mod( drop(included_mod_stack); // Actually parse the external file as a module. - let mut module = - new_parser_from_file(&sess.parse_sess, &mp.path, Some(span)).parse_mod(&token::Eof)?; + let mut parser = new_parser_from_file(&sess.parse_sess, &mp.path, Some(span)); + let mut module = parser.parse_mod(&token::Eof, unsafety)?; module.0.inline = false; module }; // (1) ...instead, we return a dummy module. - let (module, mut new_attrs) = result.map_err(|mut err| err.emit()).unwrap_or_default(); + let (module, mut new_attrs) = result.map_err(|mut err| err.emit()).unwrap_or_else(|_| { + let module = Mod { inner: Span::default(), unsafety, items: Vec::new(), inline: false }; + (module, Vec::new()) + }); attrs.append(&mut new_attrs); // Extract the directory path for submodules of `module`. @@ -219,8 +223,7 @@ fn error_cannot_declare_mod_here<'a, T>( /// Derive a submodule path from the first found `#[path = "path_string"]`. /// The provided `dir_path` is joined with the `path_string`. -// Public for rustfmt usage. -pub fn submod_path_from_attr( +pub(super) fn submod_path_from_attr( sess: &Session, attrs: &[Attribute], dir_path: &Path, diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index dbd2e70af6a..4c9271a58df 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -4,7 +4,7 @@ use crate::expand::{AstFragment, AstFragmentKind}; use rustc_ast as ast; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; -use rustc_span::source_map::{dummy_spanned, DUMMY_SP}; +use rustc_span::source_map::DUMMY_SP; use rustc_span::symbol::Ident; use smallvec::{smallvec, SmallVec}; @@ -18,7 +18,7 @@ pub fn placeholder( ) -> AstFragment { fn mac_placeholder() -> ast::MacCall { ast::MacCall { - path: ast::Path { span: DUMMY_SP, segments: Vec::new() }, + path: ast::Path { span: DUMMY_SP, segments: Vec::new(), tokens: None }, args: P(ast::MacArgs::Empty), prior_type_ascription: None, } @@ -26,7 +26,11 @@ pub fn placeholder( let ident = Ident::invalid(); let attrs = Vec::new(); - let vis = vis.unwrap_or_else(|| dummy_spanned(ast::VisibilityKind::Inherited)); + let vis = vis.unwrap_or(ast::Visibility { + span: DUMMY_SP, + kind: ast::VisibilityKind::Inherited, + tokens: None, + }); let span = DUMMY_SP; let expr_placeholder = || { P(ast::Expr { @@ -37,7 +41,8 @@ pub fn placeholder( tokens: None, }) }; - let ty = || P(ast::Ty { id, kind: ast::TyKind::MacCall(mac_placeholder()), span }); + let ty = + || P(ast::Ty { id, kind: ast::TyKind::MacCall(mac_placeholder()), span, tokens: None }); let pat = || P(ast::Pat { id, kind: ast::PatKind::MacCall(mac_placeholder()), span, tokens: None }); @@ -88,16 +93,19 @@ pub fn placeholder( kind: ast::PatKind::MacCall(mac_placeholder()), tokens: None, })), - AstFragmentKind::Ty => { - AstFragment::Ty(P(ast::Ty { id, span, kind: ast::TyKind::MacCall(mac_placeholder()) })) - } + AstFragmentKind::Ty => AstFragment::Ty(P(ast::Ty { + id, + span, + kind: ast::TyKind::MacCall(mac_placeholder()), + tokens: None, + })), AstFragmentKind::Stmts => AstFragment::Stmts(smallvec![{ let mac = P(ast::MacCallStmt { mac: mac_placeholder(), style: ast::MacStmtStyle::Braces, attrs: ast::AttrVec::new(), }); - ast::Stmt { id, span, kind: ast::StmtKind::MacCall(mac) } + ast::Stmt { id, span, kind: ast::StmtKind::MacCall(mac), tokens: None } }]), AstFragmentKind::Arms => AstFragment::Arms(smallvec![ast::Arm { attrs: Default::default(), diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 39c82f97e0a..ec41fd7a3ee 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -2,7 +2,7 @@ use crate::base::ExtCtxt; use rustc_ast as ast; use rustc_ast::token; -use rustc_ast::tokenstream::{self, DelimSpan, IsJoint::*, TokenStream, TreeAndJoint}; +use rustc_ast::tokenstream::{self, DelimSpan, Spacing::*, TokenStream, TreeAndSpacing}; use rustc_ast_pretty::pprust; use rustc_data_structures::sync::Lrc; use rustc_errors::Diagnostic; @@ -47,15 +47,15 @@ impl ToInternal<token::DelimToken> for Delimiter { } } -impl FromInternal<(TreeAndJoint, &'_ ParseSess, &'_ mut Vec<Self>)> +impl FromInternal<(TreeAndSpacing, &'_ ParseSess, &'_ mut Vec<Self>)> for TokenTree<Group, Punct, Ident, Literal> { fn from_internal( - ((tree, is_joint), sess, stack): (TreeAndJoint, &ParseSess, &mut Vec<Self>), + ((tree, spacing), sess, stack): (TreeAndSpacing, &ParseSess, &mut Vec<Self>), ) -> Self { use rustc_ast::token::*; - let joint = is_joint == Joint; + let joint = spacing == Joint; let Token { kind, span } = match tree { tokenstream::TokenTree::Delimited(span, delim, tts) => { let delimiter = Delimiter::from_internal(delim); @@ -261,7 +261,7 @@ impl ToInternal<TokenStream> for TokenTree<Group, Punct, Ident, Literal> { }; let tree = tokenstream::TokenTree::token(kind, span); - TokenStream::new(vec![(tree, if joint { Joint } else { NonJoint })]) + TokenStream::new(vec![(tree, if joint { Joint } else { Alone })]) } } @@ -444,7 +444,7 @@ impl server::TokenStreamIter for Rustc<'_> { ) -> Option<TokenTree<Self::Group, Self::Punct, Self::Ident, Self::Literal>> { loop { let tree = iter.stack.pop().or_else(|| { - let next = iter.cursor.next_with_joint()?; + let next = iter.cursor.next_with_spacing()?; Some(TokenTree::from_internal((next, self.sess, &mut iter.stack))) })?; // A hack used to pass AST fragments to attribute and derive macros diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index e858980738d..1aeb0bd5ad9 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -585,6 +585,9 @@ declare_features! ( /// Allows `if let` guard in match arms. (active, if_let_guard, "1.47.0", Some(51114), None), + /// Allows non trivial generic constants which have to be manually propageted upwards. + (active, const_evaluatable_checked, "1.48.0", Some(76560), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- @@ -600,8 +603,14 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[ sym::const_generics, sym::let_chains, sym::raw_dylib, + sym::const_evaluatable_checked, sym::const_trait_impl, sym::const_trait_bound_opt_out, sym::lazy_normalization_consts, sym::specialization, ]; + +/// Some features are not allowed to be used together at the same time, if +/// the two are present, produce an error. +pub const INCOMPATIBLE_FEATURES: &[(Symbol, Symbol)] = + &[(sym::const_generics, sym::min_const_generics)]; diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index 4393368cd45..15564a59658 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -131,7 +131,7 @@ pub fn find_feature_issue(feature: Symbol, issue: GateIssue) -> Option<NonZeroU3 } pub use accepted::ACCEPTED_FEATURES; -pub use active::{Features, ACTIVE_FEATURES, INCOMPLETE_FEATURES}; +pub use active::{Features, ACTIVE_FEATURES, INCOMPATIBLE_FEATURES, INCOMPLETE_FEATURES}; pub use builtin_attrs::{ deprecated_attributes, find_gated_cfg, is_builtin_attr_name, AttributeGate, AttributeTemplate, AttributeType, BuiltinAttribute, GatedCfg, BUILTIN_ATTRIBUTES, BUILTIN_ATTRIBUTE_MAP, diff --git a/compiler/rustc_graphviz/src/lib.rs b/compiler/rustc_graphviz/src/lib.rs index 4339092b63e..29ec3572016 100644 --- a/compiler/rustc_graphviz/src/lib.rs +++ b/compiler/rustc_graphviz/src/lib.rs @@ -599,6 +599,7 @@ pub enum RenderOption { NoNodeStyles, Monospace, + DarkTheme, } /// Returns vec holding all the default render options. @@ -630,10 +631,23 @@ where writeln!(w, "digraph {} {{", g.graph_id().as_slice())?; // Global graph properties + let mut graph_attrs = Vec::new(); + let mut content_attrs = Vec::new(); if options.contains(&RenderOption::Monospace) { - writeln!(w, r#" graph[fontname="monospace"];"#)?; - writeln!(w, r#" node[fontname="monospace"];"#)?; - writeln!(w, r#" edge[fontname="monospace"];"#)?; + let font = r#"fontname="Courier, monospace""#; + graph_attrs.push(font); + content_attrs.push(font); + }; + if options.contains(&RenderOption::DarkTheme) { + graph_attrs.push(r#"bgcolor="black""#); + content_attrs.push(r#"color="white""#); + content_attrs.push(r#"fontcolor="white""#); + } + if !(graph_attrs.is_empty() && content_attrs.is_empty()) { + writeln!(w, r#" graph[{}];"#, graph_attrs.join(" "))?; + let content_attrs_str = content_attrs.join(" "); + writeln!(w, r#" node[{}];"#, content_attrs_str)?; + writeln!(w, r#" edge[{}];"#, content_attrs_str)?; } for n in g.nodes().iter() { diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 0d61dc037c6..b019e518d0c 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -6,6 +6,7 @@ use rustc_ast::NodeId; use rustc_macros::HashStable_Generic; use rustc_span::hygiene::MacroKind; +use std::array::IntoIter; use std::fmt::Debug; /// Encodes if a `DefKind::Ctor` is the constructor of an enum variant or a struct. @@ -291,6 +292,14 @@ impl<T> PerNS<T> { pub fn map<U, F: FnMut(T) -> U>(self, mut f: F) -> PerNS<U> { PerNS { value_ns: f(self.value_ns), type_ns: f(self.type_ns), macro_ns: f(self.macro_ns) } } + + pub fn into_iter(self) -> IntoIter<T, 3> { + IntoIter::new([self.value_ns, self.type_ns, self.macro_ns]) + } + + pub fn iter(&self) -> IntoIter<&T, 3> { + IntoIter::new([&self.value_ns, &self.type_ns, &self.macro_ns]) + } } impl<T> ::std::ops::Index<Namespace> for PerNS<T> { diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index c69a9b063ae..9d931b3a9e1 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -2,6 +2,7 @@ //! //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html +#![feature(array_value_iter)] #![feature(crate_visibility_modifier)] #![feature(const_fn)] // For the unsizing cast on `&[]` #![feature(const_panic)] diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index c43d1a6830d..8e00e54650d 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -28,13 +28,20 @@ pub const WORD_BITS: usize = WORD_BYTES * 8; /// will panic if the bitsets have differing domain sizes. /// /// [`GrowableBitSet`]: struct.GrowableBitSet.html -#[derive(Clone, Eq, PartialEq, Decodable, Encodable)] -pub struct BitSet<T: Idx> { +#[derive(Eq, PartialEq, Decodable, Encodable)] +pub struct BitSet<T> { domain_size: usize, words: Vec<Word>, marker: PhantomData<T>, } +impl<T> BitSet<T> { + /// Gets the domain size. + pub fn domain_size(&self) -> usize { + self.domain_size + } +} + impl<T: Idx> BitSet<T> { /// Creates a new, empty bitset with a given `domain_size`. #[inline] @@ -52,11 +59,6 @@ impl<T: Idx> BitSet<T> { result } - /// Gets the domain size. - pub fn domain_size(&self) -> usize { - self.domain_size - } - /// Clear all elements. #[inline] pub fn clear(&mut self) { @@ -75,12 +77,6 @@ impl<T: Idx> BitSet<T> { } } - /// Efficiently overwrite `self` with `other`. - pub fn overwrite(&mut self, other: &BitSet<T>) { - assert!(self.domain_size == other.domain_size); - self.words.clone_from_slice(&other.words); - } - /// Count the number of set bits in the set. pub fn count(&self) -> usize { self.words.iter().map(|e| e.count_ones() as usize).sum() @@ -243,6 +239,21 @@ impl<T: Idx> SubtractFromBitSet<T> for BitSet<T> { } } +impl<T> Clone for BitSet<T> { + fn clone(&self) -> Self { + BitSet { domain_size: self.domain_size, words: self.words.clone(), marker: PhantomData } + } + + fn clone_from(&mut self, from: &Self) { + if self.domain_size != from.domain_size { + self.words.resize(from.domain_size, 0); + self.domain_size = from.domain_size; + } + + self.words.copy_from_slice(&from.words); + } +} + impl<T: Idx> fmt::Debug for BitSet<T> { fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result { w.debug_list().entries(self.iter()).finish() @@ -363,7 +374,7 @@ const SPARSE_MAX: usize = 8; /// /// This type is used by `HybridBitSet`; do not use directly. #[derive(Clone, Debug)] -pub struct SparseBitSet<T: Idx> { +pub struct SparseBitSet<T> { domain_size: usize, elems: ArrayVec<[T; SPARSE_MAX]>, } @@ -464,18 +475,27 @@ impl<T: Idx> SubtractFromBitSet<T> for SparseBitSet<T> { /// All operations that involve an element will panic if the element is equal /// to or greater than the domain size. All operations that involve two bitsets /// will panic if the bitsets have differing domain sizes. -#[derive(Clone, Debug)] -pub enum HybridBitSet<T: Idx> { +#[derive(Clone)] +pub enum HybridBitSet<T> { Sparse(SparseBitSet<T>), Dense(BitSet<T>), } +impl<T: Idx> fmt::Debug for HybridBitSet<T> { + fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Sparse(b) => b.fmt(w), + Self::Dense(b) => b.fmt(w), + } + } +} + impl<T: Idx> HybridBitSet<T> { pub fn new_empty(domain_size: usize) -> Self { HybridBitSet::Sparse(SparseBitSet::new_empty(domain_size)) } - fn domain_size(&self) -> usize { + pub fn domain_size(&self) -> usize { match self { HybridBitSet::Sparse(sparse) => sparse.domain_size, HybridBitSet::Dense(dense) => dense.domain_size, diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index b53605b0796..1225776db45 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -2093,7 +2093,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { _ => String::new(), }; if !s.is_empty() { - s.push_str(" "); + s.push(' '); } s }; diff --git a/compiler/rustc_infer/src/infer/lub.rs b/compiler/rustc_infer/src/infer/lub.rs index 3e2ea3d0f8f..9f43fac0916 100644 --- a/compiler/rustc_infer/src/infer/lub.rs +++ b/compiler/rustc_infer/src/infer/lub.rs @@ -50,7 +50,7 @@ impl TypeRelation<'tcx> for Lub<'combine, 'infcx, 'tcx> { ty::Invariant => self.fields.equate(self.a_is_expected).relate(a, b), ty::Covariant => self.relate(a, b), // FIXME(#41044) -- not correct, need test - ty::Bivariant => Ok(a.clone()), + ty::Bivariant => Ok(a), ty::Contravariant => self.fields.glb(self.a_is_expected).relate(a, b), } } diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index b1b39fd1ad2..f15eb413833 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -693,6 +693,7 @@ impl<'a> MutVisitor for ReplaceBodyWithLoop<'a, '_> { rules, id: resolver.next_node_id(), span: rustc_span::DUMMY_SP, + tokens: None, } } @@ -709,6 +710,7 @@ impl<'a> MutVisitor for ReplaceBodyWithLoop<'a, '_> { id: resolver.next_node_id(), kind: ast::StmtKind::Expr(expr), span: rustc_span::DUMMY_SP, + tokens: None, } } @@ -725,6 +727,7 @@ impl<'a> MutVisitor for ReplaceBodyWithLoop<'a, '_> { id: self.resolver.next_node_id(), span: rustc_span::DUMMY_SP, kind: ast::StmtKind::Expr(loop_expr), + tokens: None, }; if self.within_static_or_const { diff --git a/compiler/rustc_llvm/Cargo.toml b/compiler/rustc_llvm/Cargo.toml new file mode 100644 index 00000000000..ee83689f0a4 --- /dev/null +++ b/compiler/rustc_llvm/Cargo.toml @@ -0,0 +1,16 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_llvm" +version = "0.0.0" +edition = "2018" + +[features] +static-libstdcpp = [] +emscripten = [] + +[dependencies] +libc = "0.2.73" + +[build-dependencies] +build_helper = { path = "../../src/build_helper" } +cc = "1.0.58" diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs new file mode 100644 index 00000000000..7f1e5cf336a --- /dev/null +++ b/compiler/rustc_llvm/build.rs @@ -0,0 +1,322 @@ +use std::env; +use std::path::{Path, PathBuf}; +use std::process::Command; + +use build_helper::{output, tracked_env_var_os}; + +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`. + if tracked_env_var_os("LLVM_LINK_SHARED").is_some() { + ("dylib", "--link-shared") + } else { + ("static", "--link-static") + } +} + +fn main() { + 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; + } + + build_helper::restore_library_path(); + + let target = env::var("TARGET").expect("TARGET was not set"); + let llvm_config = + tracked_env_var_os("LLVM_CONFIG").map(|x| Some(PathBuf::from(x))).unwrap_or_else(|| { + if let Some(dir) = tracked_env_var_os("CARGO_TARGET_DIR").map(PathBuf::from) { + let to_test = dir + .parent() + .unwrap() + .parent() + .unwrap() + .join(&target) + .join("llvm/bin/llvm-config"); + if Command::new(&to_test).output().is_ok() { + return Some(to_test); + } + } + None + }); + + if let Some(llvm_config) = &llvm_config { + println!("cargo:rerun-if-changed={}", llvm_config.display()); + } + let llvm_config = llvm_config.unwrap_or_else(|| PathBuf::from("llvm-config")); + + // Test whether we're cross-compiling LLVM. This is a pretty rare case + // currently where we're producing an LLVM for a different platform than + // what this build script is currently running on. + // + // In that case, there's no guarantee that we can actually run the target, + // so the build system works around this by giving us the LLVM_CONFIG for + // the host platform. This only really works if the host LLVM and target + // LLVM are compiled the same way, but for us that's typically the case. + // + // We *want* detect this cross compiling situation by asking llvm-config + // what its host-target is. If that's not the TARGET, then we're cross + // compiling. Unfortunately `llvm-config` seems either be buggy, or we're + // misconfiguring it, because the `i686-pc-windows-gnu` build of LLVM will + // report itself with a `--host-target` of `x86_64-pc-windows-gnu`. This + // tricks us into thinking we're doing a cross build when we aren't, so + // havoc ensues. + // + // In any case, if we're cross compiling, this generally just means that we + // can't trust all the output of llvm-config because it might be targeted + // for the host rather than the target. As a result a bunch of blocks below + // are gated on `if !is_crossed` + let target = env::var("TARGET").expect("TARGET was not set"); + let host = env::var("HOST").expect("HOST was not set"); + let is_crossed = target != host; + + let mut optional_components = vec![ + "x86", + "arm", + "aarch64", + "amdgpu", + "avr", + "mips", + "powerpc", + "systemz", + "jsbackend", + "webassembly", + "msp430", + "sparc", + "nvptx", + "hexagon", + ]; + + let mut version_cmd = Command::new(&llvm_config); + version_cmd.arg("--version"); + let version_output = output(&mut version_cmd); + let mut parts = version_output.split('.').take(2).filter_map(|s| s.parse::<u32>().ok()); + let (major, _minor) = if let (Some(major), Some(minor)) = (parts.next(), parts.next()) { + (major, minor) + } else { + (6, 0) + }; + + if major > 6 { + optional_components.push("riscv"); + } + + 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)); + + for component in required_components { + if !components.contains(component) { + panic!("require llvm component {} but wasn't found", component); + } + } + + for component in components.iter() { + println!("cargo:rustc-cfg=llvm_component=\"{}\"", component); + } + + if major >= 9 { + println!("cargo:rustc-cfg=llvm_has_msp430_asm_parser"); + } + + // Link in our own LLVM shims, compiled with the same flags as LLVM + let mut cmd = Command::new(&llvm_config); + cmd.arg("--cxxflags"); + let cxxflags = output(&mut cmd); + let mut cfg = cc::Build::new(); + cfg.warnings(false); + for flag in cxxflags.split_whitespace() { + // Ignore flags like `-m64` when we're doing a cross build + if is_crossed && flag.starts_with("-m") { + continue; + } + + if flag.starts_with("-flto") { + continue; + } + + // -Wdate-time is not supported by the netbsd cross compiler + if is_crossed && target.contains("netbsd") && flag.contains("date-time") { + continue; + } + + // Include path contains host directory, replace it with target + if is_crossed && flag.starts_with("-I") { + cfg.flag(&flag.replace(&host, &target)); + continue; + } + + cfg.flag(flag); + } + + for component in &components { + let mut flag = String::from("LLVM_COMPONENT_"); + flag.push_str(&component.to_uppercase()); + cfg.define(&flag, None); + } + + if tracked_env_var_os("LLVM_RUSTLLVM").is_some() { + cfg.define("LLVM_RUSTLLVM", None); + } + + if tracked_env_var_os("LLVM_NDEBUG").is_some() { + cfg.define("NDEBUG", None); + cfg.debug(false); + } + + build_helper::rerun_if_changed_anything_in_dir(Path::new("llvm-wrapper")); + cfg.file("llvm-wrapper/PassWrapper.cpp") + .file("llvm-wrapper/RustWrapper.cpp") + .file("llvm-wrapper/ArchiveWrapper.cpp") + .file("llvm-wrapper/CoverageMappingWrapper.cpp") + .file("llvm-wrapper/Linker.cpp") + .cpp(true) + .cpp_link_stdlib(None) // we handle this below + .compile("llvm-wrapper"); + + let (llvm_kind, llvm_link_arg) = detect_llvm_link(); + + // Link in all LLVM libraries, if we're using the "wrong" llvm-config then + // we don't pick up system libs because unfortunately they're for the host + // of llvm-config, not the target that we're attempting to link. + let mut cmd = Command::new(&llvm_config); + cmd.arg(llvm_link_arg).arg("--libs"); + + if !is_crossed { + cmd.arg("--system-libs"); + } else if target.contains("windows-gnu") { + println!("cargo:rustc-link-lib=shell32"); + println!("cargo:rustc-link-lib=uuid"); + } else if target.contains("netbsd") || target.contains("haiku") { + println!("cargo:rustc-link-lib=z"); + } + cmd.args(&components); + + for lib in output(&mut cmd).split_whitespace() { + let name = if lib.starts_with("-l") { + &lib[2..] + } else if lib.starts_with('-') { + &lib[1..] + } else if Path::new(lib).exists() { + // On MSVC llvm-config will print the full name to libraries, but + // we're only interested in the name part + let name = Path::new(lib).file_name().unwrap().to_str().unwrap(); + name.trim_end_matches(".lib") + } else if lib.ends_with(".lib") { + // Some MSVC libraries just come up with `.lib` tacked on, so chop + // that off + lib.trim_end_matches(".lib") + } else { + continue; + }; + + // Don't need or want this library, but LLVM's CMake build system + // doesn't provide a way to disable it, so filter it here even though we + // may or may not have built it. We don't reference anything from this + // library and it otherwise may just pull in extra dependencies on + // libedit which we don't want + if name == "LLVMLineEditor" { + continue; + } + + let kind = if name.starts_with("LLVM") { llvm_kind } else { "dylib" }; + println!("cargo:rustc-link-lib={}={}", kind, name); + } + + // LLVM ldflags + // + // If we're a cross-compile of LLVM then unfortunately we can't trust these + // ldflags (largely where all the LLVM libs are located). Currently just + // hack around this by replacing the host triple with the target and pray + // that those -L directories are the same! + let mut cmd = Command::new(&llvm_config); + cmd.arg(llvm_link_arg).arg("--ldflags"); + for lib in output(&mut cmd).split_whitespace() { + if is_crossed { + if lib.starts_with("-LIBPATH:") { + println!("cargo:rustc-link-search=native={}", lib[9..].replace(&host, &target)); + } else if lib.starts_with("-L") { + println!("cargo:rustc-link-search=native={}", lib[2..].replace(&host, &target)); + } + } else if lib.starts_with("-LIBPATH:") { + println!("cargo:rustc-link-search=native={}", &lib[9..]); + } else if lib.starts_with("-l") { + println!("cargo:rustc-link-lib={}", &lib[2..]); + } else if lib.starts_with("-L") { + println!("cargo:rustc-link-search=native={}", &lib[2..]); + } + } + + // Some LLVM linker flags (-L and -l) may be needed even when linking + // rustc_llvm, for example when using static libc++, we may need to + // manually specify the library search path and -ldl -lpthread as link + // dependencies. + let llvm_linker_flags = tracked_env_var_os("LLVM_LINKER_FLAGS"); + if let Some(s) = llvm_linker_flags { + for lib in s.into_string().unwrap().split_whitespace() { + if lib.starts_with("-l") { + println!("cargo:rustc-link-lib={}", &lib[2..]); + } else if lib.starts_with("-L") { + println!("cargo:rustc-link-search=native={}", &lib[2..]); + } + } + } + + let llvm_static_stdcpp = tracked_env_var_os("LLVM_STATIC_STDCPP"); + let llvm_use_libcxx = tracked_env_var_os("LLVM_USE_LIBCXX"); + + let stdcppname = if target.contains("openbsd") { + if target.contains("sparc64") { "estdc++" } else { "c++" } + } else if target.contains("freebsd") { + "c++" + } else if target.contains("darwin") { + "c++" + } else if target.contains("netbsd") && llvm_static_stdcpp.is_some() { + // NetBSD uses a separate library when relocation is required + "stdc++_pic" + } else if llvm_use_libcxx.is_some() { + "c++" + } else { + "stdc++" + }; + + // RISC-V requires libatomic for sub-word atomic operations + if target.starts_with("riscv") { + println!("cargo:rustc-link-lib=atomic"); + } + + // C++ runtime library + if !target.contains("msvc") { + if let Some(s) = llvm_static_stdcpp { + assert!(!cxxflags.contains("stdlib=libc++")); + let path = PathBuf::from(s); + println!("cargo:rustc-link-search=native={}", path.parent().unwrap().display()); + if target.contains("windows") { + println!("cargo:rustc-link-lib=static-nobundle={}", stdcppname); + } else { + println!("cargo:rustc-link-lib=static={}", stdcppname); + } + } else if cxxflags.contains("stdlib=libc++") { + println!("cargo:rustc-link-lib=c++"); + } else { + println!("cargo:rustc-link-lib={}", stdcppname); + } + } + + // Libstdc++ depends on pthread which Rust doesn't link on MinGW + // since nothing else requires it. + if target.contains("windows-gnu") { + println!("cargo:rustc-link-lib=static-nobundle=pthread"); + } +} diff --git a/compiler/rustc_llvm/llvm-wrapper/.editorconfig b/compiler/rustc_llvm/llvm-wrapper/.editorconfig new file mode 100644 index 00000000000..865cd45f708 --- /dev/null +++ b/compiler/rustc_llvm/llvm-wrapper/.editorconfig @@ -0,0 +1,6 @@ +[*.{h,cpp}] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +indent_style = space +indent_size = 2 diff --git a/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp new file mode 100644 index 00000000000..2797fe8df4a --- /dev/null +++ b/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp @@ -0,0 +1,226 @@ +#include "LLVMWrapper.h" + +#include "llvm/Object/Archive.h" +#include "llvm/Object/ArchiveWriter.h" +#include "llvm/Support/Path.h" + +using namespace llvm; +using namespace llvm::object; + +struct RustArchiveMember { + const char *Filename; + const char *Name; + Archive::Child Child; + + RustArchiveMember() + : Filename(nullptr), Name(nullptr), + Child(nullptr, nullptr, nullptr) + { + } + ~RustArchiveMember() {} +}; + +struct RustArchiveIterator { + bool First; + Archive::child_iterator Cur; + Archive::child_iterator End; + std::unique_ptr<Error> Err; + + RustArchiveIterator(Archive::child_iterator Cur, Archive::child_iterator End, + std::unique_ptr<Error> Err) + : First(true), + Cur(Cur), + End(End), + Err(std::move(Err)) {} +}; + +enum class LLVMRustArchiveKind { + GNU, + BSD, + DARWIN, + COFF, +}; + +static Archive::Kind fromRust(LLVMRustArchiveKind Kind) { + switch (Kind) { + case LLVMRustArchiveKind::GNU: + return Archive::K_GNU; + case LLVMRustArchiveKind::BSD: + return Archive::K_BSD; + case LLVMRustArchiveKind::DARWIN: + return Archive::K_DARWIN; + case LLVMRustArchiveKind::COFF: + return Archive::K_COFF; + default: + report_fatal_error("Bad ArchiveKind."); + } +} + +typedef OwningBinary<Archive> *LLVMRustArchiveRef; +typedef RustArchiveMember *LLVMRustArchiveMemberRef; +typedef Archive::Child *LLVMRustArchiveChildRef; +typedef Archive::Child const *LLVMRustArchiveChildConstRef; +typedef RustArchiveIterator *LLVMRustArchiveIteratorRef; + +extern "C" LLVMRustArchiveRef LLVMRustOpenArchive(char *Path) { + ErrorOr<std::unique_ptr<MemoryBuffer>> BufOr = + MemoryBuffer::getFile(Path, -1, false); + if (!BufOr) { + LLVMRustSetLastError(BufOr.getError().message().c_str()); + return nullptr; + } + + Expected<std::unique_ptr<Archive>> ArchiveOr = + Archive::create(BufOr.get()->getMemBufferRef()); + + if (!ArchiveOr) { + LLVMRustSetLastError(toString(ArchiveOr.takeError()).c_str()); + return nullptr; + } + + OwningBinary<Archive> *Ret = new OwningBinary<Archive>( + std::move(ArchiveOr.get()), std::move(BufOr.get())); + + return Ret; +} + +extern "C" void LLVMRustDestroyArchive(LLVMRustArchiveRef RustArchive) { + delete RustArchive; +} + +extern "C" LLVMRustArchiveIteratorRef +LLVMRustArchiveIteratorNew(LLVMRustArchiveRef RustArchive) { + Archive *Archive = RustArchive->getBinary(); +#if LLVM_VERSION_GE(10, 0) + std::unique_ptr<Error> Err = std::make_unique<Error>(Error::success()); +#else + std::unique_ptr<Error> Err = llvm::make_unique<Error>(Error::success()); +#endif + auto Cur = Archive->child_begin(*Err); + if (*Err) { + LLVMRustSetLastError(toString(std::move(*Err)).c_str()); + return nullptr; + } + auto End = Archive->child_end(); + return new RustArchiveIterator(Cur, End, std::move(Err)); +} + +extern "C" LLVMRustArchiveChildConstRef +LLVMRustArchiveIteratorNext(LLVMRustArchiveIteratorRef RAI) { + if (RAI->Cur == RAI->End) + return nullptr; + + // Advancing the iterator validates the next child, and this can + // uncover an error. LLVM requires that we check all Errors, + // so we only advance the iterator if we actually need to fetch + // the next child. + // This means we must not advance the iterator in the *first* call, + // but instead advance it *before* fetching the child in all later calls. + if (!RAI->First) { + ++RAI->Cur; + if (*RAI->Err) { + LLVMRustSetLastError(toString(std::move(*RAI->Err)).c_str()); + return nullptr; + } + } else { + RAI->First = false; + } + + if (RAI->Cur == RAI->End) + return nullptr; + + const Archive::Child &Child = *RAI->Cur.operator->(); + Archive::Child *Ret = new Archive::Child(Child); + + return Ret; +} + +extern "C" void LLVMRustArchiveChildFree(LLVMRustArchiveChildRef Child) { + delete Child; +} + +extern "C" void LLVMRustArchiveIteratorFree(LLVMRustArchiveIteratorRef RAI) { + delete RAI; +} + +extern "C" const char * +LLVMRustArchiveChildName(LLVMRustArchiveChildConstRef Child, size_t *Size) { + Expected<StringRef> NameOrErr = Child->getName(); + if (!NameOrErr) { + // rustc_codegen_llvm currently doesn't use this error string, but it might be + // useful in the future, and in the mean time this tells LLVM that the + // error was not ignored and that it shouldn't abort the process. + LLVMRustSetLastError(toString(NameOrErr.takeError()).c_str()); + return nullptr; + } + StringRef Name = NameOrErr.get(); + *Size = Name.size(); + return Name.data(); +} + +extern "C" const char *LLVMRustArchiveChildData(LLVMRustArchiveChildRef Child, + size_t *Size) { + StringRef Buf; + Expected<StringRef> BufOrErr = Child->getBuffer(); + if (!BufOrErr) { + LLVMRustSetLastError(toString(BufOrErr.takeError()).c_str()); + return nullptr; + } + Buf = BufOrErr.get(); + *Size = Buf.size(); + return Buf.data(); +} + +extern "C" LLVMRustArchiveMemberRef +LLVMRustArchiveMemberNew(char *Filename, char *Name, + LLVMRustArchiveChildRef Child) { + RustArchiveMember *Member = new RustArchiveMember; + Member->Filename = Filename; + Member->Name = Name; + if (Child) + Member->Child = *Child; + return Member; +} + +extern "C" void LLVMRustArchiveMemberFree(LLVMRustArchiveMemberRef Member) { + delete Member; +} + +extern "C" LLVMRustResult +LLVMRustWriteArchive(char *Dst, size_t NumMembers, + const LLVMRustArchiveMemberRef *NewMembers, + bool WriteSymbtab, LLVMRustArchiveKind RustKind) { + + std::vector<NewArchiveMember> Members; + auto Kind = fromRust(RustKind); + + for (size_t I = 0; I < NumMembers; I++) { + auto Member = NewMembers[I]; + assert(Member->Name); + if (Member->Filename) { + Expected<NewArchiveMember> MOrErr = + NewArchiveMember::getFile(Member->Filename, true); + if (!MOrErr) { + LLVMRustSetLastError(toString(MOrErr.takeError()).c_str()); + return LLVMRustResult::Failure; + } + MOrErr->MemberName = sys::path::filename(MOrErr->MemberName); + Members.push_back(std::move(*MOrErr)); + } else { + Expected<NewArchiveMember> MOrErr = + NewArchiveMember::getOldMember(Member->Child, true); + if (!MOrErr) { + LLVMRustSetLastError(toString(MOrErr.takeError()).c_str()); + return LLVMRustResult::Failure; + } + Members.push_back(std::move(*MOrErr)); + } + } + + auto Result = writeArchive(Dst, Members, WriteSymbtab, Kind, true, false); + if (!Result) + return LLVMRustResult::Success; + LLVMRustSetLastError(toString(std::move(Result)).c_str()); + + return LLVMRustResult::Failure; +} diff --git a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp new file mode 100644 index 00000000000..2b1143a4ecf --- /dev/null +++ b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp @@ -0,0 +1,70 @@ +#include "LLVMWrapper.h" +#include "llvm/ProfileData/Coverage/CoverageMapping.h" +#include "llvm/ProfileData/Coverage/CoverageMappingWriter.h" +#include "llvm/ProfileData/InstrProf.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/LEB128.h" + +#include <iostream> + +using namespace llvm; + +extern "C" void LLVMRustCoverageWriteFilenamesSectionToBuffer( + const char* const Filenames[], + size_t FilenamesLen, + RustStringRef BufferOut) { + // LLVM 11's CoverageFilenamesSectionWriter uses its new `Version4` format, + // so we're manually writing the `Version3` format ourselves. + RawRustStringOstream OS(BufferOut); + encodeULEB128(FilenamesLen, OS); + for (size_t i = 0; i < FilenamesLen; i++) { + StringRef Filename(Filenames[i]); + encodeULEB128(Filename.size(), OS); + OS << Filename; + } +} + +extern "C" void LLVMRustCoverageWriteMappingToBuffer( + const unsigned *VirtualFileMappingIDs, + unsigned NumVirtualFileMappingIDs, + const coverage::CounterExpression *Expressions, + unsigned NumExpressions, + coverage::CounterMappingRegion *MappingRegions, + unsigned NumMappingRegions, + RustStringRef BufferOut) { + auto CoverageMappingWriter = coverage::CoverageMappingWriter( + makeArrayRef(VirtualFileMappingIDs, NumVirtualFileMappingIDs), + makeArrayRef(Expressions, NumExpressions), + makeMutableArrayRef(MappingRegions, NumMappingRegions)); + RawRustStringOstream OS(BufferOut); + CoverageMappingWriter.write(OS); +} + +extern "C" LLVMValueRef LLVMRustCoverageCreatePGOFuncNameVar(LLVMValueRef F, const char *FuncName) { + StringRef FuncNameRef(FuncName); + return wrap(createPGOFuncNameVar(*cast<Function>(unwrap(F)), FuncNameRef)); +} + +extern "C" uint64_t LLVMRustCoverageComputeHash(const char *Name) { + StringRef NameRef(Name); + return IndexedInstrProf::ComputeHash(NameRef); +} + +extern "C" void LLVMRustCoverageWriteSectionNameToString(LLVMModuleRef M, + RustStringRef Str) { + Triple TargetTriple(unwrap(M)->getTargetTriple()); + auto name = getInstrProfSectionName(IPSK_covmap, + TargetTriple.getObjectFormat()); + RawRustStringOstream OS(Str); + OS << name; +} + +extern "C" void LLVMRustCoverageWriteMappingVarNameToString(RustStringRef Str) { + auto name = getCoverageMappingVarName(); + RawRustStringOstream OS(Str); + OS << name; +} + +extern "C" uint32_t LLVMRustCoverageMappingVersion() { + return coverage::CovMapVersion::Version3; +} diff --git a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h new file mode 100644 index 00000000000..57b8664d3b6 --- /dev/null +++ b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h @@ -0,0 +1,115 @@ +#include "llvm-c/BitReader.h" +#include "llvm-c/Core.h" +#include "llvm-c/Object.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Analysis/Lint.h" +#include "llvm/Analysis/Passes.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InlineAsm.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/DynamicLibrary.h" +#include "llvm/Support/FormattedStream.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/Memory.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetOptions.h" +#include "llvm/Transforms/IPO.h" +#include "llvm/Transforms/Instrumentation.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/Vectorize.h" + +#define LLVM_VERSION_GE(major, minor) \ + (LLVM_VERSION_MAJOR > (major) || \ + LLVM_VERSION_MAJOR == (major) && LLVM_VERSION_MINOR >= (minor)) + +#define LLVM_VERSION_EQ(major, minor) \ + (LLVM_VERSION_MAJOR == (major) && LLVM_VERSION_MINOR == (minor)) + +#define LLVM_VERSION_LE(major, minor) \ + (LLVM_VERSION_MAJOR < (major) || \ + LLVM_VERSION_MAJOR == (major) && LLVM_VERSION_MINOR <= (minor)) + +#define LLVM_VERSION_LT(major, minor) (!LLVM_VERSION_GE((major), (minor))) + +#include "llvm/IR/LegacyPassManager.h" + +#include "llvm/Bitcode/BitcodeReader.h" +#include "llvm/Bitcode/BitcodeWriter.h" + +#include "llvm/IR/DIBuilder.h" +#include "llvm/IR/DebugInfo.h" +#include "llvm/IR/IRPrintingPasses.h" +#include "llvm/Linker/Linker.h" + +extern "C" void LLVMRustSetLastError(const char *); + +enum class LLVMRustResult { Success, Failure }; + +enum LLVMRustAttribute { + AlwaysInline = 0, + ByVal = 1, + Cold = 2, + InlineHint = 3, + MinSize = 4, + Naked = 5, + NoAlias = 6, + NoCapture = 7, + NoInline = 8, + NonNull = 9, + NoRedZone = 10, + NoReturn = 11, + NoUnwind = 12, + OptimizeForSize = 13, + ReadOnly = 14, + SExt = 15, + StructRet = 16, + UWTable = 17, + ZExt = 18, + InReg = 19, + SanitizeThread = 20, + SanitizeAddress = 21, + SanitizeMemory = 22, + NonLazyBind = 23, + OptimizeNone = 24, + ReturnsTwice = 25, + ReadNone = 26, + InaccessibleMemOnly = 27, +}; + +typedef struct OpaqueRustString *RustStringRef; +typedef struct LLVMOpaqueTwine *LLVMTwineRef; +typedef struct LLVMOpaqueSMDiagnostic *LLVMSMDiagnosticRef; + +extern "C" void LLVMRustStringWriteImpl(RustStringRef Str, const char *Ptr, + size_t Size); + +class RawRustStringOstream : public llvm::raw_ostream { + RustStringRef Str; + uint64_t Pos; + + void write_impl(const char *Ptr, size_t Size) override { + LLVMRustStringWriteImpl(Str, Ptr, Size); + Pos += Size; + } + + uint64_t current_pos() const override { return Pos; } + +public: + explicit RawRustStringOstream(RustStringRef Str) : Str(Str), Pos(0) {} + + ~RawRustStringOstream() { + // LLVM requires this. + flush(); + } +}; diff --git a/compiler/rustc_llvm/llvm-wrapper/Linker.cpp b/compiler/rustc_llvm/llvm-wrapper/Linker.cpp new file mode 100644 index 00000000000..8766e96f086 --- /dev/null +++ b/compiler/rustc_llvm/llvm-wrapper/Linker.cpp @@ -0,0 +1,48 @@ +#include "llvm/Linker/Linker.h" + +#include "LLVMWrapper.h" + +using namespace llvm; + +struct RustLinker { + Linker L; + LLVMContext &Ctx; + + RustLinker(Module &M) : + L(M), + Ctx(M.getContext()) + {} +}; + +extern "C" RustLinker* +LLVMRustLinkerNew(LLVMModuleRef DstRef) { + Module *Dst = unwrap(DstRef); + + return new RustLinker(*Dst); +} + +extern "C" void +LLVMRustLinkerFree(RustLinker *L) { + delete L; +} + +extern "C" bool +LLVMRustLinkerAdd(RustLinker *L, char *BC, size_t Len) { + std::unique_ptr<MemoryBuffer> Buf = + MemoryBuffer::getMemBufferCopy(StringRef(BC, Len)); + + Expected<std::unique_ptr<Module>> SrcOrError = + llvm::getLazyBitcodeModule(Buf->getMemBufferRef(), L->Ctx); + if (!SrcOrError) { + LLVMRustSetLastError(toString(SrcOrError.takeError()).c_str()); + return false; + } + + auto Src = std::move(*SrcOrError); + + if (L->L.linkInModule(std::move(Src))) { + LLVMRustSetLastError(""); + return false; + } + return true; +} diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp new file mode 100644 index 00000000000..7b1c3f9ba2c --- /dev/null +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -0,0 +1,1655 @@ +#include <stdio.h> + +#include <vector> +#include <set> + +#include "LLVMWrapper.h" + +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/Analysis/TargetTransformInfo.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/InitializePasses.h" +#include "llvm/IR/AutoUpgrade.h" +#include "llvm/IR/AssemblyAnnotationWriter.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Verifier.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Object/IRObjectFile.h" +#include "llvm/Passes/PassBuilder.h" +#if LLVM_VERSION_GE(9, 0) +#include "llvm/Passes/StandardInstrumentations.h" +#endif +#include "llvm/Support/CBindingWrapping.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Host.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/IPO/AlwaysInliner.h" +#include "llvm/Transforms/IPO/FunctionImport.h" +#include "llvm/Transforms/Utils/FunctionImportUtils.h" +#include "llvm/LTO/LTO.h" +#include "llvm-c/Transforms/PassManagerBuilder.h" + +#include "llvm/Transforms/Instrumentation.h" +#if LLVM_VERSION_GE(9, 0) +#include "llvm/Transforms/Instrumentation/AddressSanitizer.h" +#include "llvm/Support/TimeProfiler.h" +#endif +#include "llvm/Transforms/Instrumentation/ThreadSanitizer.h" +#include "llvm/Transforms/Instrumentation/MemorySanitizer.h" +#if LLVM_VERSION_GE(9, 0) +#include "llvm/Transforms/Utils/CanonicalizeAliases.h" +#endif +#include "llvm/Transforms/Utils/NameAnonGlobals.h" + +using namespace llvm; + +typedef struct LLVMOpaquePass *LLVMPassRef; +typedef struct LLVMOpaqueTargetMachine *LLVMTargetMachineRef; + +DEFINE_STDCXX_CONVERSION_FUNCTIONS(Pass, LLVMPassRef) +DEFINE_STDCXX_CONVERSION_FUNCTIONS(TargetMachine, LLVMTargetMachineRef) +#if LLVM_VERSION_LT(11, 0) +DEFINE_STDCXX_CONVERSION_FUNCTIONS(PassManagerBuilder, + LLVMPassManagerBuilderRef) +#endif + +extern "C" void LLVMInitializePasses() { + PassRegistry &Registry = *PassRegistry::getPassRegistry(); + initializeCore(Registry); + initializeCodeGen(Registry); + initializeScalarOpts(Registry); + initializeVectorization(Registry); + initializeIPO(Registry); + initializeAnalysis(Registry); + initializeTransformUtils(Registry); + initializeInstCombine(Registry); + initializeInstrumentation(Registry); + initializeTarget(Registry); +} + +extern "C" void LLVMTimeTraceProfilerInitialize() { +#if LLVM_VERSION_GE(10, 0) + timeTraceProfilerInitialize( + /* TimeTraceGranularity */ 0, + /* ProcName */ "rustc"); +#elif LLVM_VERSION_GE(9, 0) + timeTraceProfilerInitialize(); +#endif +} + +extern "C" void LLVMTimeTraceProfilerFinish(const char* FileName) { +#if LLVM_VERSION_GE(9, 0) + StringRef FN(FileName); + std::error_code EC; + raw_fd_ostream OS(FN, EC, sys::fs::CD_CreateAlways); + + timeTraceProfilerWrite(OS); + timeTraceProfilerCleanup(); +#endif +} + +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) { + StringRef SR(PassName); + PassRegistry *PR = PassRegistry::getPassRegistry(); + + const PassInfo *PI = PR->getPassInfo(SR); + if (PI) { + return wrap(PI->createPass()); + } + return nullptr; +} + +extern "C" LLVMPassRef LLVMRustCreateAddressSanitizerFunctionPass(bool Recover) { + const bool CompileKernel = false; + const bool UseAfterScope = true; + + return wrap(createAddressSanitizerFunctionPass(CompileKernel, Recover, UseAfterScope)); +} + +extern "C" LLVMPassRef LLVMRustCreateModuleAddressSanitizerPass(bool Recover) { + const bool CompileKernel = false; + +#if LLVM_VERSION_GE(9, 0) + return wrap(createModuleAddressSanitizerLegacyPassPass(CompileKernel, Recover)); +#else + return wrap(createAddressSanitizerModulePass(CompileKernel, Recover)); +#endif +} + +extern "C" LLVMPassRef LLVMRustCreateMemorySanitizerPass(int TrackOrigins, bool Recover) { +#if LLVM_VERSION_GE(9, 0) + const bool CompileKernel = false; + + return wrap(createMemorySanitizerLegacyPassPass( + MemorySanitizerOptions{TrackOrigins, Recover, CompileKernel})); +#else + return wrap(createMemorySanitizerLegacyPassPass(TrackOrigins, Recover)); +#endif +} + +extern "C" LLVMPassRef LLVMRustCreateThreadSanitizerPass() { + return wrap(createThreadSanitizerLegacyPassPass()); +} + +extern "C" LLVMRustPassKind LLVMRustPassKind(LLVMPassRef RustPass) { + assert(RustPass); + Pass *Pass = unwrap(RustPass); + return toRust(Pass->getPassKind()); +} + +extern "C" void LLVMRustAddPass(LLVMPassManagerRef PMR, LLVMPassRef RustPass) { + assert(RustPass); + Pass *Pass = unwrap(RustPass); + PassManagerBase *PMB = unwrap(PMR); + PMB->add(Pass); +} + +extern "C" +void LLVMRustPassManagerBuilderPopulateThinLTOPassManager( + LLVMPassManagerBuilderRef PMBR, + LLVMPassManagerRef PMR +) { + unwrap(PMBR)->populateThinLTOPassManager(*unwrap(PMR)); +} + +extern "C" +void LLVMRustAddLastExtensionPasses( + LLVMPassManagerBuilderRef PMBR, LLVMPassRef *Passes, size_t NumPasses) { + auto AddExtensionPasses = [Passes, NumPasses]( + const PassManagerBuilder &Builder, PassManagerBase &PM) { + for (size_t I = 0; I < NumPasses; I++) { + PM.add(unwrap(Passes[I])); + } + }; + // Add the passes to both of the pre-finalization extension points, + // so they are run for optimized and non-optimized builds. + unwrap(PMBR)->addExtension(PassManagerBuilder::EP_OptimizerLast, + AddExtensionPasses); + unwrap(PMBR)->addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0, + AddExtensionPasses); +} + +#ifdef LLVM_COMPONENT_X86 +#define SUBTARGET_X86 SUBTARGET(X86) +#else +#define SUBTARGET_X86 +#endif + +#ifdef LLVM_COMPONENT_ARM +#define SUBTARGET_ARM SUBTARGET(ARM) +#else +#define SUBTARGET_ARM +#endif + +#ifdef LLVM_COMPONENT_AARCH64 +#define SUBTARGET_AARCH64 SUBTARGET(AArch64) +#else +#define SUBTARGET_AARCH64 +#endif + +#ifdef LLVM_COMPONENT_AVR +#define SUBTARGET_AVR SUBTARGET(AVR) +#else +#define SUBTARGET_AVR +#endif + +#ifdef LLVM_COMPONENT_MIPS +#define SUBTARGET_MIPS SUBTARGET(Mips) +#else +#define SUBTARGET_MIPS +#endif + +#ifdef LLVM_COMPONENT_POWERPC +#define SUBTARGET_PPC SUBTARGET(PPC) +#else +#define SUBTARGET_PPC +#endif + +#ifdef LLVM_COMPONENT_SYSTEMZ +#define SUBTARGET_SYSTEMZ SUBTARGET(SystemZ) +#else +#define SUBTARGET_SYSTEMZ +#endif + +#ifdef LLVM_COMPONENT_MSP430 +#define SUBTARGET_MSP430 SUBTARGET(MSP430) +#else +#define SUBTARGET_MSP430 +#endif + +#ifdef LLVM_COMPONENT_RISCV +#define SUBTARGET_RISCV SUBTARGET(RISCV) +#else +#define SUBTARGET_RISCV +#endif + +#ifdef LLVM_COMPONENT_SPARC +#define SUBTARGET_SPARC SUBTARGET(Sparc) +#else +#define SUBTARGET_SPARC +#endif + +#ifdef LLVM_COMPONENT_HEXAGON +#define SUBTARGET_HEXAGON SUBTARGET(Hexagon) +#else +#define SUBTARGET_HEXAGON +#endif + +#define GEN_SUBTARGETS \ + SUBTARGET_X86 \ + SUBTARGET_ARM \ + SUBTARGET_AARCH64 \ + SUBTARGET_AVR \ + SUBTARGET_MIPS \ + SUBTARGET_PPC \ + SUBTARGET_SYSTEMZ \ + SUBTARGET_MSP430 \ + SUBTARGET_SPARC \ + SUBTARGET_HEXAGON \ + SUBTARGET_RISCV \ + +#define SUBTARGET(x) \ + namespace llvm { \ + extern const SubtargetFeatureKV x##FeatureKV[]; \ + extern const SubtargetFeatureKV x##SubTypeKV[]; \ + } + +GEN_SUBTARGETS +#undef SUBTARGET + +extern "C" bool LLVMRustHasFeature(LLVMTargetMachineRef TM, + const char *Feature) { + TargetMachine *Target = unwrap(TM); + const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo(); + return MCInfo->checkFeatures(std::string("+") + Feature); +} + +enum class LLVMRustCodeModel { + Tiny, + Small, + Kernel, + Medium, + Large, + None, +}; + +static Optional<CodeModel::Model> fromRust(LLVMRustCodeModel Model) { + switch (Model) { + case LLVMRustCodeModel::Tiny: + return CodeModel::Tiny; + case LLVMRustCodeModel::Small: + return CodeModel::Small; + case LLVMRustCodeModel::Kernel: + return CodeModel::Kernel; + case LLVMRustCodeModel::Medium: + return CodeModel::Medium; + case LLVMRustCodeModel::Large: + return CodeModel::Large; + case LLVMRustCodeModel::None: + return None; + default: + report_fatal_error("Bad CodeModel."); + } +} + +enum class LLVMRustCodeGenOptLevel { + None, + Less, + Default, + Aggressive, +}; + +static CodeGenOpt::Level fromRust(LLVMRustCodeGenOptLevel Level) { + switch (Level) { + case LLVMRustCodeGenOptLevel::None: + return CodeGenOpt::None; + case LLVMRustCodeGenOptLevel::Less: + return CodeGenOpt::Less; + case LLVMRustCodeGenOptLevel::Default: + return CodeGenOpt::Default; + case LLVMRustCodeGenOptLevel::Aggressive: + return CodeGenOpt::Aggressive; + default: + report_fatal_error("Bad CodeGenOptLevel."); + } +} + +enum class LLVMRustPassBuilderOptLevel { + O0, + O1, + O2, + O3, + Os, + Oz, +}; + +static PassBuilder::OptimizationLevel fromRust(LLVMRustPassBuilderOptLevel Level) { + switch (Level) { + case LLVMRustPassBuilderOptLevel::O0: + return PassBuilder::OptimizationLevel::O0; + case LLVMRustPassBuilderOptLevel::O1: + return PassBuilder::OptimizationLevel::O1; + case LLVMRustPassBuilderOptLevel::O2: + return PassBuilder::OptimizationLevel::O2; + case LLVMRustPassBuilderOptLevel::O3: + return PassBuilder::OptimizationLevel::O3; + case LLVMRustPassBuilderOptLevel::Os: + return PassBuilder::OptimizationLevel::Os; + case LLVMRustPassBuilderOptLevel::Oz: + return PassBuilder::OptimizationLevel::Oz; + default: + report_fatal_error("Bad PassBuilderOptLevel."); + } +} + +enum class LLVMRustRelocModel { + Static, + PIC, + DynamicNoPic, + ROPI, + RWPI, + ROPIRWPI, +}; + +static Reloc::Model fromRust(LLVMRustRelocModel RustReloc) { + switch (RustReloc) { + case LLVMRustRelocModel::Static: + return Reloc::Static; + case LLVMRustRelocModel::PIC: + return Reloc::PIC_; + case LLVMRustRelocModel::DynamicNoPic: + return Reloc::DynamicNoPIC; + case LLVMRustRelocModel::ROPI: + return Reloc::ROPI; + case LLVMRustRelocModel::RWPI: + return Reloc::RWPI; + case LLVMRustRelocModel::ROPIRWPI: + return Reloc::ROPI_RWPI; + } + report_fatal_error("Bad RelocModel."); +} + +#ifdef LLVM_RUSTLLVM +/// getLongestEntryLength - Return the length of the longest entry in the table. +template<typename KV> +static size_t getLongestEntryLength(ArrayRef<KV> Table) { + size_t MaxLen = 0; + for (auto &I : Table) + MaxLen = std::max(MaxLen, std::strlen(I.Key)); + return MaxLen; +} + +extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM) { + const TargetMachine *Target = unwrap(TM); + const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo(); + const Triple::ArchType HostArch = Triple(sys::getProcessTriple()).getArch(); + const Triple::ArchType TargetArch = Target->getTargetTriple().getArch(); + const ArrayRef<SubtargetSubTypeKV> CPUTable = MCInfo->getCPUTable(); + unsigned MaxCPULen = getLongestEntryLength(CPUTable); + + printf("Available CPUs for this target:\n"); + if (HostArch == TargetArch) { + const StringRef HostCPU = sys::getHostCPUName(); + printf(" %-*s - Select the CPU of the current host (currently %.*s).\n", + MaxCPULen, "native", (int)HostCPU.size(), HostCPU.data()); + } + for (auto &CPU : CPUTable) + printf(" %-*s\n", MaxCPULen, CPU.Key); + printf("\n"); +} + +extern "C" void LLVMRustPrintTargetFeatures(LLVMTargetMachineRef TM) { + const TargetMachine *Target = unwrap(TM); + const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo(); + const ArrayRef<SubtargetFeatureKV> FeatTable = MCInfo->getFeatureTable(); + unsigned MaxFeatLen = getLongestEntryLength(FeatTable); + + printf("Available features for this target:\n"); + for (auto &Feature : FeatTable) + printf(" %-*s - %s.\n", MaxFeatLen, Feature.Key, Feature.Desc); + printf("\nRust-specific features:\n"); + printf(" %-*s - %s.\n", + MaxFeatLen, + "crt-static", + "Enables libraries with C Run-time Libraries(CRT) to be statically linked" + ); + printf("\n"); + + printf("Use +feature to enable a feature, or -feature to disable it.\n" + "For example, rustc -C -target-cpu=mycpu -C " + "target-feature=+feature1,-feature2\n\n"); +} + +#else + +extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef) { + printf("Target CPU help is not supported by this LLVM version.\n\n"); +} + +extern "C" void LLVMRustPrintTargetFeatures(LLVMTargetMachineRef) { + printf("Target features help is not supported by this LLVM version.\n\n"); +} +#endif + +extern "C" const char* LLVMRustGetHostCPUName(size_t *len) { + StringRef Name = sys::getHostCPUName(); + *len = Name.size(); + return Name.data(); +} + +extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( + const char *TripleStr, const char *CPU, const char *Feature, + const char *ABIStr, LLVMRustCodeModel RustCM, LLVMRustRelocModel RustReloc, + LLVMRustCodeGenOptLevel RustOptLevel, bool UseSoftFloat, + bool FunctionSections, + bool DataSections, + bool TrapUnreachable, + bool Singlethread, + bool AsmComments, + bool EmitStackSizeSection, + bool RelaxELFRelocations, + bool UseInitArray) { + + auto OptLevel = fromRust(RustOptLevel); + auto RM = fromRust(RustReloc); + auto CM = fromRust(RustCM); + + std::string Error; + Triple Trip(Triple::normalize(TripleStr)); + const llvm::Target *TheTarget = + TargetRegistry::lookupTarget(Trip.getTriple(), Error); + if (TheTarget == nullptr) { + LLVMRustSetLastError(Error.c_str()); + return nullptr; + } + + TargetOptions Options; + + Options.FloatABIType = FloatABI::Default; + if (UseSoftFloat) { + Options.FloatABIType = FloatABI::Soft; + } + Options.DataSections = DataSections; + Options.FunctionSections = FunctionSections; + Options.MCOptions.AsmVerbose = AsmComments; + Options.MCOptions.PreserveAsmComments = AsmComments; + Options.MCOptions.ABIName = ABIStr; + Options.RelaxELFRelocations = RelaxELFRelocations; + Options.UseInitArray = UseInitArray; + + if (TrapUnreachable) { + // Tell LLVM to codegen `unreachable` into an explicit trap instruction. + // This limits the extent of possible undefined behavior in some cases, as + // it prevents control flow from "falling through" into whatever code + // happens to be laid out next in memory. + Options.TrapUnreachable = true; + } + + if (Singlethread) { + Options.ThreadModel = ThreadModel::Single; + } + + Options.EmitStackSizeSection = EmitStackSizeSection; + + TargetMachine *TM = TheTarget->createTargetMachine( + Trip.getTriple(), CPU, Feature, Options, RM, CM, OptLevel); + return wrap(TM); +} + +extern "C" void LLVMRustDisposeTargetMachine(LLVMTargetMachineRef TM) { + delete unwrap(TM); +} + +extern "C" void LLVMRustConfigurePassManagerBuilder( + LLVMPassManagerBuilderRef PMBR, LLVMRustCodeGenOptLevel OptLevel, + bool MergeFunctions, bool SLPVectorize, bool LoopVectorize, bool PrepareForThinLTO, + const char* PGOGenPath, const char* PGOUsePath) { + unwrap(PMBR)->MergeFunctions = MergeFunctions; + unwrap(PMBR)->SLPVectorize = SLPVectorize; + unwrap(PMBR)->OptLevel = fromRust(OptLevel); + unwrap(PMBR)->LoopVectorize = LoopVectorize; + unwrap(PMBR)->PrepareForThinLTO = PrepareForThinLTO; + + if (PGOGenPath) { + assert(!PGOUsePath); + unwrap(PMBR)->EnablePGOInstrGen = true; + unwrap(PMBR)->PGOInstrGen = PGOGenPath; + } + if (PGOUsePath) { + assert(!PGOGenPath); + unwrap(PMBR)->PGOInstrUse = PGOUsePath; + } +} + +// Unfortunately, the LLVM C API doesn't provide a way to set the `LibraryInfo` +// field of a PassManagerBuilder, we expose our own method of doing so. +extern "C" void LLVMRustAddBuilderLibraryInfo(LLVMPassManagerBuilderRef PMBR, + LLVMModuleRef M, + bool DisableSimplifyLibCalls) { + Triple TargetTriple(unwrap(M)->getTargetTriple()); + TargetLibraryInfoImpl *TLI = new TargetLibraryInfoImpl(TargetTriple); + if (DisableSimplifyLibCalls) + TLI->disableAllFunctions(); + unwrap(PMBR)->LibraryInfo = TLI; +} + +// Unfortunately, the LLVM C API doesn't provide a way to create the +// TargetLibraryInfo pass, so we use this method to do so. +extern "C" void LLVMRustAddLibraryInfo(LLVMPassManagerRef PMR, LLVMModuleRef M, + bool DisableSimplifyLibCalls) { + Triple TargetTriple(unwrap(M)->getTargetTriple()); + TargetLibraryInfoImpl TLII(TargetTriple); + if (DisableSimplifyLibCalls) + TLII.disableAllFunctions(); + unwrap(PMR)->add(new TargetLibraryInfoWrapperPass(TLII)); +} + +// Unfortunately, the LLVM C API doesn't provide an easy way of iterating over +// all the functions in a module, so we do that manually here. You'll find +// similar code in clang's BackendUtil.cpp file. +extern "C" void LLVMRustRunFunctionPassManager(LLVMPassManagerRef PMR, + LLVMModuleRef M) { + llvm::legacy::FunctionPassManager *P = + unwrap<llvm::legacy::FunctionPassManager>(PMR); + P->doInitialization(); + + // Upgrade all calls to old intrinsics first. + for (Module::iterator I = unwrap(M)->begin(), E = unwrap(M)->end(); I != E;) + UpgradeCallsToIntrinsic(&*I++); // must be post-increment, as we remove + + for (Module::iterator I = unwrap(M)->begin(), E = unwrap(M)->end(); I != E; + ++I) + if (!I->isDeclaration()) + P->run(*I); + + P->doFinalization(); +} + +extern "C" void LLVMRustSetLLVMOptions(int Argc, char **Argv) { + // Initializing the command-line options more than once is not allowed. So, + // check if they've already been initialized. (This could happen if we're + // being called from rustpkg, for example). If the arguments change, then + // that's just kinda unfortunate. + static bool Initialized = false; + if (Initialized) + return; + Initialized = true; + cl::ParseCommandLineOptions(Argc, Argv); +} + +enum class LLVMRustFileType { + AssemblyFile, + ObjectFile, +}; + +#if LLVM_VERSION_GE(10, 0) +static CodeGenFileType fromRust(LLVMRustFileType Type) { + switch (Type) { + case LLVMRustFileType::AssemblyFile: + return CGFT_AssemblyFile; + case LLVMRustFileType::ObjectFile: + return CGFT_ObjectFile; + default: + report_fatal_error("Bad FileType."); + } +} +#else +static TargetMachine::CodeGenFileType fromRust(LLVMRustFileType Type) { + switch (Type) { + case LLVMRustFileType::AssemblyFile: + return TargetMachine::CGFT_AssemblyFile; + case LLVMRustFileType::ObjectFile: + return TargetMachine::CGFT_ObjectFile; + default: + report_fatal_error("Bad FileType."); + } +} +#endif + +extern "C" LLVMRustResult +LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMPassManagerRef PMR, + LLVMModuleRef M, const char *Path, + LLVMRustFileType RustFileType) { + llvm::legacy::PassManager *PM = unwrap<llvm::legacy::PassManager>(PMR); + auto FileType = fromRust(RustFileType); + + std::string ErrorInfo; + std::error_code EC; + raw_fd_ostream OS(Path, EC, sys::fs::F_None); + if (EC) + ErrorInfo = EC.message(); + if (ErrorInfo != "") { + LLVMRustSetLastError(ErrorInfo.c_str()); + return LLVMRustResult::Failure; + } + + buffer_ostream BOS(OS); + unwrap(Target)->addPassesToEmitFile(*PM, BOS, nullptr, FileType, false); + PM->run(*unwrap(M)); + + // Apparently `addPassesToEmitFile` adds a pointer to our on-the-stack output + // stream (OS), so the only real safe place to delete this is here? Don't we + // wish this was written in Rust? + LLVMDisposePassManager(PMR); + return LLVMRustResult::Success; +} + +extern "C" typedef void (*LLVMRustSelfProfileBeforePassCallback)(void*, // LlvmSelfProfiler + const char*, // pass name + const char*); // IR name +extern "C" typedef void (*LLVMRustSelfProfileAfterPassCallback)(void*); // LlvmSelfProfiler + +#if LLVM_VERSION_GE(9, 0) + +std::string LLVMRustwrappedIrGetName(const llvm::Any &WrappedIr) { + if (any_isa<const Module *>(WrappedIr)) + return any_cast<const Module *>(WrappedIr)->getName().str(); + if (any_isa<const Function *>(WrappedIr)) + return any_cast<const Function *>(WrappedIr)->getName().str(); + if (any_isa<const Loop *>(WrappedIr)) + return any_cast<const Loop *>(WrappedIr)->getName().str(); + if (any_isa<const LazyCallGraph::SCC *>(WrappedIr)) + return any_cast<const LazyCallGraph::SCC *>(WrappedIr)->getName(); + return "<UNKNOWN>"; +} + + +void LLVMSelfProfileInitializeCallbacks( + PassInstrumentationCallbacks& PIC, void* LlvmSelfProfiler, + LLVMRustSelfProfileBeforePassCallback BeforePassCallback, + LLVMRustSelfProfileAfterPassCallback AfterPassCallback) { + PIC.registerBeforePassCallback([LlvmSelfProfiler, BeforePassCallback]( + StringRef Pass, llvm::Any Ir) { + std::string PassName = Pass.str(); + std::string IrName = LLVMRustwrappedIrGetName(Ir); + BeforePassCallback(LlvmSelfProfiler, PassName.c_str(), IrName.c_str()); + return true; + }); + + PIC.registerAfterPassCallback( + [LlvmSelfProfiler, AfterPassCallback](StringRef Pass, llvm::Any Ir) { + AfterPassCallback(LlvmSelfProfiler); + }); + + PIC.registerAfterPassInvalidatedCallback( + [LlvmSelfProfiler, AfterPassCallback](StringRef Pass) { + AfterPassCallback(LlvmSelfProfiler); + }); + + PIC.registerBeforeAnalysisCallback([LlvmSelfProfiler, BeforePassCallback]( + StringRef Pass, llvm::Any Ir) { + std::string PassName = Pass.str(); + std::string IrName = LLVMRustwrappedIrGetName(Ir); + BeforePassCallback(LlvmSelfProfiler, PassName.c_str(), IrName.c_str()); + }); + + PIC.registerAfterAnalysisCallback( + [LlvmSelfProfiler, AfterPassCallback](StringRef Pass, llvm::Any Ir) { + AfterPassCallback(LlvmSelfProfiler); + }); +} +#endif + +enum class LLVMRustOptStage { + PreLinkNoLTO, + PreLinkThinLTO, + PreLinkFatLTO, + ThinLTO, + FatLTO, +}; + +struct LLVMRustSanitizerOptions { + bool SanitizeAddress; + bool SanitizeAddressRecover; + bool SanitizeMemory; + bool SanitizeMemoryRecover; + int SanitizeMemoryTrackOrigins; + bool SanitizeThread; +}; + +extern "C" void +LLVMRustOptimizeWithNewPassManager( + LLVMModuleRef ModuleRef, + LLVMTargetMachineRef TMRef, + LLVMRustPassBuilderOptLevel OptLevelRust, + LLVMRustOptStage OptStage, + bool NoPrepopulatePasses, bool VerifyIR, bool UseThinLTOBuffers, + bool MergeFunctions, bool UnrollLoops, bool SLPVectorize, bool LoopVectorize, + bool DisableSimplifyLibCalls, bool EmitLifetimeMarkers, + LLVMRustSanitizerOptions *SanitizerOptions, + const char *PGOGenPath, const char *PGOUsePath, + void* LlvmSelfProfiler, + LLVMRustSelfProfileBeforePassCallback BeforePassCallback, + LLVMRustSelfProfileAfterPassCallback AfterPassCallback) { +#if LLVM_VERSION_GE(9, 0) + Module *TheModule = unwrap(ModuleRef); + TargetMachine *TM = unwrap(TMRef); + PassBuilder::OptimizationLevel OptLevel = fromRust(OptLevelRust); + + // FIXME: MergeFunctions is not supported by NewPM yet. + (void) MergeFunctions; + + PipelineTuningOptions PTO; + PTO.LoopUnrolling = UnrollLoops; + PTO.LoopInterleaving = UnrollLoops; + PTO.LoopVectorization = LoopVectorize; + PTO.SLPVectorization = SLPVectorize; + + PassInstrumentationCallbacks PIC; + StandardInstrumentations SI; + SI.registerCallbacks(PIC); + + if (LlvmSelfProfiler){ + LLVMSelfProfileInitializeCallbacks(PIC,LlvmSelfProfiler,BeforePassCallback,AfterPassCallback); + } + + Optional<PGOOptions> PGOOpt; + if (PGOGenPath) { + assert(!PGOUsePath); + PGOOpt = PGOOptions(PGOGenPath, "", "", PGOOptions::IRInstr); + } else if (PGOUsePath) { + assert(!PGOGenPath); + PGOOpt = PGOOptions(PGOUsePath, "", "", PGOOptions::IRUse); + } + + PassBuilder PB(TM, PTO, PGOOpt, &PIC); + + // FIXME: We may want to expose this as an option. + bool DebugPassManager = false; + LoopAnalysisManager LAM(DebugPassManager); + FunctionAnalysisManager FAM(DebugPassManager); + CGSCCAnalysisManager CGAM(DebugPassManager); + ModuleAnalysisManager MAM(DebugPassManager); + + FAM.registerPass([&] { return PB.buildDefaultAAPipeline(); }); + + Triple TargetTriple(TheModule->getTargetTriple()); + std::unique_ptr<TargetLibraryInfoImpl> TLII(new TargetLibraryInfoImpl(TargetTriple)); + if (DisableSimplifyLibCalls) + TLII->disableAllFunctions(); + FAM.registerPass([&] { return TargetLibraryAnalysis(*TLII); }); + + PB.registerModuleAnalyses(MAM); + PB.registerCGSCCAnalyses(CGAM); + PB.registerFunctionAnalyses(FAM); + PB.registerLoopAnalyses(LAM); + PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); + + // We manually collect pipeline callbacks so we can apply them at O0, where the + // PassBuilder does not create a pipeline. + std::vector<std::function<void(ModulePassManager &)>> PipelineStartEPCallbacks; +#if LLVM_VERSION_GE(11, 0) + std::vector<std::function<void(ModulePassManager &, PassBuilder::OptimizationLevel)>> + OptimizerLastEPCallbacks; +#else + std::vector<std::function<void(FunctionPassManager &, PassBuilder::OptimizationLevel)>> + OptimizerLastEPCallbacks; +#endif + + if (VerifyIR) { + PipelineStartEPCallbacks.push_back([VerifyIR](ModulePassManager &MPM) { + MPM.addPass(VerifierPass()); + }); + } + + if (SanitizerOptions) { + if (SanitizerOptions->SanitizeMemory) { + MemorySanitizerOptions Options( + SanitizerOptions->SanitizeMemoryTrackOrigins, + SanitizerOptions->SanitizeMemoryRecover, + /*CompileKernel=*/false); +#if LLVM_VERSION_GE(11, 0) + OptimizerLastEPCallbacks.push_back( + [Options](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) { + MPM.addPass(MemorySanitizerPass(Options)); + MPM.addPass(createModuleToFunctionPassAdaptor(MemorySanitizerPass(Options))); + } + ); +#else +#if LLVM_VERSION_GE(10, 0) + PipelineStartEPCallbacks.push_back([Options](ModulePassManager &MPM) { + MPM.addPass(MemorySanitizerPass(Options)); + }); +#endif + OptimizerLastEPCallbacks.push_back( + [Options](FunctionPassManager &FPM, PassBuilder::OptimizationLevel Level) { + FPM.addPass(MemorySanitizerPass(Options)); + } + ); +#endif + } + + if (SanitizerOptions->SanitizeThread) { +#if LLVM_VERSION_GE(11, 0) + OptimizerLastEPCallbacks.push_back( + [](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) { + MPM.addPass(ThreadSanitizerPass()); + MPM.addPass(createModuleToFunctionPassAdaptor(ThreadSanitizerPass())); + } + ); +#else +#if LLVM_VERSION_GE(10, 0) + PipelineStartEPCallbacks.push_back([](ModulePassManager &MPM) { + MPM.addPass(ThreadSanitizerPass()); + }); +#endif + OptimizerLastEPCallbacks.push_back( + [](FunctionPassManager &FPM, PassBuilder::OptimizationLevel Level) { + FPM.addPass(ThreadSanitizerPass()); + } + ); +#endif + } + + if (SanitizerOptions->SanitizeAddress) { +#if LLVM_VERSION_GE(11, 0) + OptimizerLastEPCallbacks.push_back( + [SanitizerOptions](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) { + MPM.addPass(RequireAnalysisPass<ASanGlobalsMetadataAnalysis, Module>()); + MPM.addPass(ModuleAddressSanitizerPass( + /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover)); + MPM.addPass(createModuleToFunctionPassAdaptor(AddressSanitizerPass( + /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover, + /*UseAfterScope=*/true))); + } + ); +#else + PipelineStartEPCallbacks.push_back([&](ModulePassManager &MPM) { + MPM.addPass(RequireAnalysisPass<ASanGlobalsMetadataAnalysis, Module>()); + }); + OptimizerLastEPCallbacks.push_back( + [SanitizerOptions](FunctionPassManager &FPM, PassBuilder::OptimizationLevel Level) { + FPM.addPass(AddressSanitizerPass( + /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover, + /*UseAfterScope=*/true)); + } + ); + PipelineStartEPCallbacks.push_back( + [SanitizerOptions](ModulePassManager &MPM) { + MPM.addPass(ModuleAddressSanitizerPass( + /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover)); + } + ); +#endif + } + } + + ModulePassManager MPM(DebugPassManager); + if (!NoPrepopulatePasses) { + if (OptLevel == PassBuilder::OptimizationLevel::O0) { + for (const auto &C : PipelineStartEPCallbacks) + C(MPM); + +#if LLVM_VERSION_GE(11, 0) + for (const auto &C : OptimizerLastEPCallbacks) + C(MPM, OptLevel); +#else + if (!OptimizerLastEPCallbacks.empty()) { + FunctionPassManager FPM(DebugPassManager); + for (const auto &C : OptimizerLastEPCallbacks) + C(FPM, OptLevel); + MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); + } +#endif + + MPM.addPass(AlwaysInlinerPass(EmitLifetimeMarkers)); + +#if LLVM_VERSION_GE(10, 0) + if (PGOOpt) { + PB.addPGOInstrPassesForO0( + MPM, DebugPassManager, PGOOpt->Action == PGOOptions::IRInstr, + /*IsCS=*/false, PGOOpt->ProfileFile, PGOOpt->ProfileRemappingFile); + } +#endif + } else { + for (const auto &C : PipelineStartEPCallbacks) + PB.registerPipelineStartEPCallback(C); + if (OptStage != LLVMRustOptStage::PreLinkThinLTO) { + for (const auto &C : OptimizerLastEPCallbacks) + PB.registerOptimizerLastEPCallback(C); + } + + switch (OptStage) { + case LLVMRustOptStage::PreLinkNoLTO: + MPM = PB.buildPerModuleDefaultPipeline(OptLevel, DebugPassManager); + break; + case LLVMRustOptStage::PreLinkThinLTO: + MPM = PB.buildThinLTOPreLinkDefaultPipeline(OptLevel, DebugPassManager); +#if LLVM_VERSION_GE(11, 0) + for (const auto &C : OptimizerLastEPCallbacks) + C(MPM, OptLevel); +#else + if (!OptimizerLastEPCallbacks.empty()) { + FunctionPassManager FPM(DebugPassManager); + for (const auto &C : OptimizerLastEPCallbacks) + C(FPM, OptLevel); + MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); + } +#endif + break; + case LLVMRustOptStage::PreLinkFatLTO: + MPM = PB.buildLTOPreLinkDefaultPipeline(OptLevel, DebugPassManager); + break; + case LLVMRustOptStage::ThinLTO: + // FIXME: Does it make sense to pass the ModuleSummaryIndex? + // It only seems to be needed for C++ specific optimizations. + MPM = PB.buildThinLTODefaultPipeline(OptLevel, DebugPassManager, nullptr); + break; + case LLVMRustOptStage::FatLTO: + MPM = PB.buildLTODefaultPipeline(OptLevel, DebugPassManager, nullptr); + break; + } + } + } + + if (UseThinLTOBuffers) { + MPM.addPass(CanonicalizeAliasesPass()); + MPM.addPass(NameAnonGlobalPass()); + } + + // Upgrade all calls to old intrinsics first. + for (Module::iterator I = TheModule->begin(), E = TheModule->end(); I != E;) + UpgradeCallsToIntrinsic(&*I++); // must be post-increment, as we remove + + MPM.run(*TheModule, MAM); +#else + // The new pass manager has been available for a long time, + // but we don't bother supporting it on old LLVM versions. + report_fatal_error("New pass manager only supported since LLVM 9"); +#endif +} + +// Callback to demangle function name +// Parameters: +// * name to be demangled +// * name len +// * output buffer +// * output buffer len +// Returns len of demangled string, or 0 if demangle failed. +typedef size_t (*DemangleFn)(const char*, size_t, char*, size_t); + + +namespace { + +class RustAssemblyAnnotationWriter : public AssemblyAnnotationWriter { + DemangleFn Demangle; + std::vector<char> Buf; + +public: + RustAssemblyAnnotationWriter(DemangleFn Demangle) : Demangle(Demangle) {} + + // Return empty string if demangle failed + // or if name does not need to be demangled + StringRef CallDemangle(StringRef name) { + if (!Demangle) { + return StringRef(); + } + + if (Buf.size() < name.size() * 2) { + // Semangled name usually shorter than mangled, + // but allocate twice as much memory just in case + Buf.resize(name.size() * 2); + } + + auto R = Demangle(name.data(), name.size(), Buf.data(), Buf.size()); + if (!R) { + // Demangle failed. + return StringRef(); + } + + auto Demangled = StringRef(Buf.data(), R); + if (Demangled == name) { + // Do not print anything if demangled name is equal to mangled. + return StringRef(); + } + + return Demangled; + } + + void emitFunctionAnnot(const Function *F, + formatted_raw_ostream &OS) override { + StringRef Demangled = CallDemangle(F->getName()); + if (Demangled.empty()) { + return; + } + + OS << "; " << Demangled << "\n"; + } + + void emitInstructionAnnot(const Instruction *I, + formatted_raw_ostream &OS) override { + const char *Name; + const Value *Value; + if (const CallInst *CI = dyn_cast<CallInst>(I)) { + Name = "call"; + Value = CI->getCalledOperand(); + } else if (const InvokeInst* II = dyn_cast<InvokeInst>(I)) { + Name = "invoke"; + Value = II->getCalledOperand(); + } else { + // Could demangle more operations, e. g. + // `store %place, @function`. + return; + } + + if (!Value->hasName()) { + return; + } + + StringRef Demangled = CallDemangle(Value->getName()); + if (Demangled.empty()) { + return; + } + + OS << "; " << Name << " " << Demangled << "\n"; + } +}; + +} // namespace + +extern "C" LLVMRustResult +LLVMRustPrintModule(LLVMModuleRef M, const char *Path, DemangleFn Demangle) { + std::string ErrorInfo; + std::error_code EC; + raw_fd_ostream OS(Path, EC, sys::fs::F_None); + if (EC) + ErrorInfo = EC.message(); + if (ErrorInfo != "") { + LLVMRustSetLastError(ErrorInfo.c_str()); + return LLVMRustResult::Failure; + } + + RustAssemblyAnnotationWriter AAW(Demangle); + formatted_raw_ostream FOS(OS); + unwrap(M)->print(FOS, &AAW); + + return LLVMRustResult::Success; +} + +extern "C" void LLVMRustPrintPasses() { + LLVMInitializePasses(); + struct MyListener : PassRegistrationListener { + void passEnumerate(const PassInfo *Info) { + StringRef PassArg = Info->getPassArgument(); + StringRef PassName = Info->getPassName(); + if (!PassArg.empty()) { + // These unsigned->signed casts could theoretically overflow, but + // realistically never will (and even if, the result is implementation + // defined rather plain UB). + printf("%15.*s - %.*s\n", (int)PassArg.size(), PassArg.data(), + (int)PassName.size(), PassName.data()); + } + } + } Listener; + + PassRegistry *PR = PassRegistry::getPassRegistry(); + PR->enumerateWith(&Listener); +} + +extern "C" void LLVMRustAddAlwaysInlinePass(LLVMPassManagerBuilderRef PMBR, + bool AddLifetimes) { + unwrap(PMBR)->Inliner = llvm::createAlwaysInlinerLegacyPass(AddLifetimes); +} + +extern "C" void LLVMRustRunRestrictionPass(LLVMModuleRef M, char **Symbols, + size_t Len) { + llvm::legacy::PassManager passes; + + auto PreserveFunctions = [=](const GlobalValue &GV) { + for (size_t I = 0; I < Len; I++) { + if (GV.getName() == Symbols[I]) { + return true; + } + } + return false; + }; + + passes.add(llvm::createInternalizePass(PreserveFunctions)); + + passes.run(*unwrap(M)); +} + +extern "C" void LLVMRustMarkAllFunctionsNounwind(LLVMModuleRef M) { + for (Module::iterator GV = unwrap(M)->begin(), E = unwrap(M)->end(); GV != E; + ++GV) { + GV->setDoesNotThrow(); + Function *F = dyn_cast<Function>(GV); + if (F == nullptr) + continue; + + for (Function::iterator B = F->begin(), BE = F->end(); B != BE; ++B) { + for (BasicBlock::iterator I = B->begin(), IE = B->end(); I != IE; ++I) { + if (isa<InvokeInst>(I)) { + InvokeInst *CI = cast<InvokeInst>(I); + CI->setDoesNotThrow(); + } + } + } + } +} + +extern "C" void +LLVMRustSetDataLayoutFromTargetMachine(LLVMModuleRef Module, + LLVMTargetMachineRef TMR) { + TargetMachine *Target = unwrap(TMR); + unwrap(Module)->setDataLayout(Target->createDataLayout()); +} + +extern "C" void LLVMRustSetModulePICLevel(LLVMModuleRef M) { + unwrap(M)->setPICLevel(PICLevel::Level::BigPIC); +} + +extern "C" void LLVMRustSetModulePIELevel(LLVMModuleRef M) { + unwrap(M)->setPIELevel(PIELevel::Level::Large); +} + +// Here you'll find an implementation of ThinLTO as used by the Rust compiler +// right now. This ThinLTO support is only enabled on "recent ish" versions of +// LLVM, and otherwise it's just blanket rejected from other compilers. +// +// Most of this implementation is straight copied from LLVM. At the time of +// this writing it wasn't *quite* suitable to reuse more code from upstream +// for our purposes, but we should strive to upstream this support once it's +// ready to go! I figure we may want a bit of testing locally first before +// sending this upstream to LLVM. I hear though they're quite eager to receive +// feedback like this! +// +// If you're reading this code and wondering "what in the world" or you're +// working "good lord by LLVM upgrade is *still* failing due to these bindings" +// then fear not! (ok maybe fear a little). All code here is mostly based +// on `lib/LTO/ThinLTOCodeGenerator.cpp` in LLVM. +// +// You'll find that the general layout here roughly corresponds to the `run` +// method in that file as well as `ProcessThinLTOModule`. Functions are +// specifically commented below as well, but if you're updating this code +// or otherwise trying to understand it, the LLVM source will be useful in +// interpreting the mysteries within. +// +// Otherwise I'll apologize in advance, it probably requires a relatively +// significant investment on your part to "truly understand" what's going on +// here. Not saying I do myself, but it took me awhile staring at LLVM's source +// and various online resources about ThinLTO to make heads or tails of all +// this. + +// This is a shared data structure which *must* be threadsafe to share +// read-only amongst threads. This also corresponds basically to the arguments +// of the `ProcessThinLTOModule` function in the LLVM source. +struct LLVMRustThinLTOData { + // The combined index that is the global analysis over all modules we're + // performing ThinLTO for. This is mostly managed by LLVM. + ModuleSummaryIndex Index; + + // All modules we may look at, stored as in-memory serialized versions. This + // is later used when inlining to ensure we can extract any module to inline + // from. + StringMap<MemoryBufferRef> ModuleMap; + + // A set that we manage of everything we *don't* want internalized. Note that + // this includes all transitive references right now as well, but it may not + // always! + DenseSet<GlobalValue::GUID> GUIDPreservedSymbols; + + // Not 100% sure what these are, but they impact what's internalized and + // what's inlined across modules, I believe. + StringMap<FunctionImporter::ImportMapTy> ImportLists; + StringMap<FunctionImporter::ExportSetTy> ExportLists; + StringMap<GVSummaryMapTy> ModuleToDefinedGVSummaries; + + LLVMRustThinLTOData() : Index(/* HaveGVs = */ false) {} +}; + +// Just an argument to the `LLVMRustCreateThinLTOData` function below. +struct LLVMRustThinLTOModule { + const char *identifier; + const char *data; + size_t len; +}; + +// This is copied from `lib/LTO/ThinLTOCodeGenerator.cpp`, not sure what it +// does. +static const GlobalValueSummary * +getFirstDefinitionForLinker(const GlobalValueSummaryList &GVSummaryList) { + auto StrongDefForLinker = llvm::find_if( + GVSummaryList, [](const std::unique_ptr<GlobalValueSummary> &Summary) { + auto Linkage = Summary->linkage(); + return !GlobalValue::isAvailableExternallyLinkage(Linkage) && + !GlobalValue::isWeakForLinker(Linkage); + }); + if (StrongDefForLinker != GVSummaryList.end()) + return StrongDefForLinker->get(); + + auto FirstDefForLinker = llvm::find_if( + GVSummaryList, [](const std::unique_ptr<GlobalValueSummary> &Summary) { + auto Linkage = Summary->linkage(); + return !GlobalValue::isAvailableExternallyLinkage(Linkage); + }); + if (FirstDefForLinker == GVSummaryList.end()) + return nullptr; + return FirstDefForLinker->get(); +} + +// The main entry point for creating the global ThinLTO analysis. The structure +// here is basically the same as before threads are spawned in the `run` +// function of `lib/LTO/ThinLTOCodeGenerator.cpp`. +extern "C" LLVMRustThinLTOData* +LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules, + int num_modules, + const char **preserved_symbols, + int num_symbols) { +#if LLVM_VERSION_GE(10, 0) + auto Ret = std::make_unique<LLVMRustThinLTOData>(); +#else + auto Ret = llvm::make_unique<LLVMRustThinLTOData>(); +#endif + + // Load each module's summary and merge it into one combined index + for (int i = 0; i < num_modules; i++) { + auto module = &modules[i]; + StringRef buffer(module->data, module->len); + MemoryBufferRef mem_buffer(buffer, module->identifier); + + Ret->ModuleMap[module->identifier] = mem_buffer; + + if (Error Err = readModuleSummaryIndex(mem_buffer, Ret->Index, i)) { + LLVMRustSetLastError(toString(std::move(Err)).c_str()); + return nullptr; + } + } + + // Collect for each module the list of function it defines (GUID -> Summary) + Ret->Index.collectDefinedGVSummariesPerModule(Ret->ModuleToDefinedGVSummaries); + + // Convert the preserved symbols set from string to GUID, this is then needed + // for internalization. + for (int i = 0; i < num_symbols; i++) { + auto GUID = GlobalValue::getGUID(preserved_symbols[i]); + Ret->GUIDPreservedSymbols.insert(GUID); + } + + // Collect the import/export lists for all modules from the call-graph in the + // combined index + // + // This is copied from `lib/LTO/ThinLTOCodeGenerator.cpp` + auto deadIsPrevailing = [&](GlobalValue::GUID G) { + return PrevailingType::Unknown; + }; + // We don't have a complete picture in our use of ThinLTO, just our immediate + // crate, so we need `ImportEnabled = false` to limit internalization. + // Otherwise, we sometimes lose `static` values -- see #60184. + computeDeadSymbolsWithConstProp(Ret->Index, Ret->GUIDPreservedSymbols, + deadIsPrevailing, /* ImportEnabled = */ false); + ComputeCrossModuleImport( + Ret->Index, + Ret->ModuleToDefinedGVSummaries, + Ret->ImportLists, + Ret->ExportLists + ); + + // Resolve LinkOnce/Weak symbols, this has to be computed early be cause it + // impacts the caching. + // + // This is copied from `lib/LTO/ThinLTOCodeGenerator.cpp` with some of this + // being lifted from `lib/LTO/LTO.cpp` as well + StringMap<std::map<GlobalValue::GUID, GlobalValue::LinkageTypes>> ResolvedODR; + DenseMap<GlobalValue::GUID, const GlobalValueSummary *> PrevailingCopy; + for (auto &I : Ret->Index) { + if (I.second.SummaryList.size() > 1) + PrevailingCopy[I.first] = getFirstDefinitionForLinker(I.second.SummaryList); + } + auto isPrevailing = [&](GlobalValue::GUID GUID, const GlobalValueSummary *S) { + const auto &Prevailing = PrevailingCopy.find(GUID); + if (Prevailing == PrevailingCopy.end()) + return true; + return Prevailing->second == S; + }; + auto recordNewLinkage = [&](StringRef ModuleIdentifier, + GlobalValue::GUID GUID, + GlobalValue::LinkageTypes NewLinkage) { + ResolvedODR[ModuleIdentifier][GUID] = NewLinkage; + }; +#if LLVM_VERSION_GE(9, 0) + thinLTOResolvePrevailingInIndex(Ret->Index, isPrevailing, recordNewLinkage, + Ret->GUIDPreservedSymbols); +#else + thinLTOResolvePrevailingInIndex(Ret->Index, isPrevailing, recordNewLinkage); +#endif + + // Here we calculate an `ExportedGUIDs` set for use in the `isExported` + // callback below. This callback below will dictate the linkage for all + // summaries in the index, and we basically just only want to ensure that dead + // symbols are internalized. Otherwise everything that's already external + // linkage will stay as external, and internal will stay as internal. + std::set<GlobalValue::GUID> ExportedGUIDs; + for (auto &List : Ret->Index) { + for (auto &GVS: List.second.SummaryList) { + if (GlobalValue::isLocalLinkage(GVS->linkage())) + continue; + auto GUID = GVS->getOriginalName(); + if (GVS->flags().Live) + ExportedGUIDs.insert(GUID); + } + } +#if LLVM_VERSION_GE(10, 0) + auto isExported = [&](StringRef ModuleIdentifier, ValueInfo VI) { + const auto &ExportList = Ret->ExportLists.find(ModuleIdentifier); + return (ExportList != Ret->ExportLists.end() && + ExportList->second.count(VI)) || + ExportedGUIDs.count(VI.getGUID()); + }; + thinLTOInternalizeAndPromoteInIndex(Ret->Index, isExported, isPrevailing); +#else + auto isExported = [&](StringRef ModuleIdentifier, GlobalValue::GUID GUID) { + const auto &ExportList = Ret->ExportLists.find(ModuleIdentifier); + return (ExportList != Ret->ExportLists.end() && + ExportList->second.count(GUID)) || + ExportedGUIDs.count(GUID); + }; + thinLTOInternalizeAndPromoteInIndex(Ret->Index, isExported); +#endif + + return Ret.release(); +} + +extern "C" void +LLVMRustFreeThinLTOData(LLVMRustThinLTOData *Data) { + delete Data; +} + +// Below are the various passes that happen *per module* when doing ThinLTO. +// +// In other words, these are the functions that are all run concurrently +// with one another, one per module. The passes here correspond to the analysis +// passes in `lib/LTO/ThinLTOCodeGenerator.cpp`, currently found in the +// `ProcessThinLTOModule` function. Here they're split up into separate steps +// so rustc can save off the intermediate bytecode between each step. + +#if LLVM_VERSION_GE(11, 0) +static bool +clearDSOLocalOnDeclarations(Module &Mod, TargetMachine &TM) { + // When linking an ELF shared object, dso_local should be dropped. We + // conservatively do this for -fpic. + bool ClearDSOLocalOnDeclarations = + TM.getTargetTriple().isOSBinFormatELF() && + TM.getRelocationModel() != Reloc::Static && + Mod.getPIELevel() == PIELevel::Default; + return ClearDSOLocalOnDeclarations; +} +#endif + +extern "C" bool +LLVMRustPrepareThinLTORename(const LLVMRustThinLTOData *Data, LLVMModuleRef M, + LLVMTargetMachineRef TM) { + Module &Mod = *unwrap(M); + TargetMachine &Target = *unwrap(TM); + +#if LLVM_VERSION_GE(11, 0) + bool ClearDSOLocal = clearDSOLocalOnDeclarations(Mod, Target); + bool error = renameModuleForThinLTO(Mod, Data->Index, ClearDSOLocal); +#else + bool error = renameModuleForThinLTO(Mod, Data->Index); +#endif + + if (error) { + LLVMRustSetLastError("renameModuleForThinLTO failed"); + return false; + } + return true; +} + +extern "C" bool +LLVMRustPrepareThinLTOResolveWeak(const LLVMRustThinLTOData *Data, LLVMModuleRef M) { + Module &Mod = *unwrap(M); + const auto &DefinedGlobals = Data->ModuleToDefinedGVSummaries.lookup(Mod.getModuleIdentifier()); + thinLTOResolvePrevailingInModule(Mod, DefinedGlobals); + return true; +} + +extern "C" bool +LLVMRustPrepareThinLTOInternalize(const LLVMRustThinLTOData *Data, LLVMModuleRef M) { + Module &Mod = *unwrap(M); + const auto &DefinedGlobals = Data->ModuleToDefinedGVSummaries.lookup(Mod.getModuleIdentifier()); + thinLTOInternalizeModule(Mod, DefinedGlobals); + return true; +} + +extern "C" bool +LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data, LLVMModuleRef M, + LLVMTargetMachineRef TM) { + Module &Mod = *unwrap(M); + TargetMachine &Target = *unwrap(TM); + + const auto &ImportList = Data->ImportLists.lookup(Mod.getModuleIdentifier()); + auto Loader = [&](StringRef Identifier) { + const auto &Memory = Data->ModuleMap.lookup(Identifier); + auto &Context = Mod.getContext(); + auto MOrErr = getLazyBitcodeModule(Memory, Context, true, true); + + if (!MOrErr) + return MOrErr; + + // The rest of this closure is a workaround for + // https://bugs.llvm.org/show_bug.cgi?id=38184 where during ThinLTO imports + // we accidentally import wasm custom sections into different modules, + // duplicating them by in the final output artifact. + // + // The issue is worked around here by manually removing the + // `wasm.custom_sections` named metadata node from any imported module. This + // we know isn't used by any optimization pass so there's no need for it to + // be imported. + // + // Note that the metadata is currently lazily loaded, so we materialize it + // here before looking up if there's metadata inside. The `FunctionImporter` + // will immediately materialize metadata anyway after an import, so this + // shouldn't be a perf hit. + if (Error Err = (*MOrErr)->materializeMetadata()) { + Expected<std::unique_ptr<Module>> Ret(std::move(Err)); + return Ret; + } + + auto *WasmCustomSections = (*MOrErr)->getNamedMetadata("wasm.custom_sections"); + if (WasmCustomSections) + WasmCustomSections->eraseFromParent(); + + return MOrErr; + }; +#if LLVM_VERSION_GE(11, 0) + bool ClearDSOLocal = clearDSOLocalOnDeclarations(Mod, Target); + FunctionImporter Importer(Data->Index, Loader, ClearDSOLocal); +#else + FunctionImporter Importer(Data->Index, Loader); +#endif + Expected<bool> Result = Importer.importFunctions(Mod, ImportList); + if (!Result) { + LLVMRustSetLastError(toString(Result.takeError()).c_str()); + return false; + } + 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 +LLVMRustGetThinLTOModuleImports(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 +// with the LTO support above we need to serialize the modules plus their +// ThinLTO summary into memory. +// +// This structure is basically an owned version of a serialize module, with +// a ThinLTO summary attached. +struct LLVMRustThinLTOBuffer { + std::string data; +}; + +extern "C" LLVMRustThinLTOBuffer* +LLVMRustThinLTOBufferCreate(LLVMModuleRef M) { +#if LLVM_VERSION_GE(10, 0) + auto Ret = std::make_unique<LLVMRustThinLTOBuffer>(); +#else + auto Ret = llvm::make_unique<LLVMRustThinLTOBuffer>(); +#endif + { + raw_string_ostream OS(Ret->data); + { + legacy::PassManager PM; + PM.add(createWriteThinLTOBitcodePass(OS)); + PM.run(*unwrap(M)); + } + } + return Ret.release(); +} + +extern "C" void +LLVMRustThinLTOBufferFree(LLVMRustThinLTOBuffer *Buffer) { + delete Buffer; +} + +extern "C" const void* +LLVMRustThinLTOBufferPtr(const LLVMRustThinLTOBuffer *Buffer) { + return Buffer->data.data(); +} + +extern "C" size_t +LLVMRustThinLTOBufferLen(const LLVMRustThinLTOBuffer *Buffer) { + return Buffer->data.length(); +} + +// This is what we used to parse upstream bitcode for actual ThinLTO +// processing. We'll call this once per module optimized through ThinLTO, and +// it'll be called concurrently on many threads. +extern "C" LLVMModuleRef +LLVMRustParseBitcodeForLTO(LLVMContextRef Context, + const char *data, + size_t len, + const char *identifier) { + StringRef Data(data, len); + MemoryBufferRef Buffer(Data, identifier); + unwrap(Context)->enableDebugTypeODRUniquing(); + Expected<std::unique_ptr<Module>> SrcOrError = + parseBitcodeFile(Buffer, *unwrap(Context)); + if (!SrcOrError) { + LLVMRustSetLastError(toString(SrcOrError.takeError()).c_str()); + return nullptr; + } + return wrap(std::move(*SrcOrError).release()); +} + +// Find the bitcode section in the object file data and return it as a slice. +// Fail if the bitcode section is present but empty. +// +// On success, the return value is the pointer to the start of the slice and +// `out_len` is filled with the (non-zero) length. On failure, the return value +// is `nullptr` and `out_len` is set to zero. +extern "C" const char* +LLVMRustGetBitcodeSliceFromObjectData(const char *data, + size_t len, + size_t *out_len) { + *out_len = 0; + + StringRef Data(data, len); + MemoryBufferRef Buffer(Data, ""); // The id is unused. + + Expected<MemoryBufferRef> BitcodeOrError = + object::IRObjectFile::findBitcodeInMemBuffer(Buffer); + if (!BitcodeOrError) { + LLVMRustSetLastError(toString(BitcodeOrError.takeError()).c_str()); + return nullptr; + } + + *out_len = BitcodeOrError->getBufferSize(); + return BitcodeOrError->getBufferStart(); +} + +// Rewrite all `DICompileUnit` pointers to the `DICompileUnit` specified. See +// the comment in `back/lto.rs` for why this exists. +extern "C" void +LLVMRustThinLTOGetDICompileUnit(LLVMModuleRef Mod, + DICompileUnit **A, + DICompileUnit **B) { + Module *M = unwrap(Mod); + DICompileUnit **Cur = A; + DICompileUnit **Next = B; + for (DICompileUnit *CU : M->debug_compile_units()) { + *Cur = CU; + Cur = Next; + Next = nullptr; + if (Cur == nullptr) + break; + } +} + +// Rewrite all `DICompileUnit` pointers to the `DICompileUnit` specified. See +// the comment in `back/lto.rs` for why this exists. +extern "C" void +LLVMRustThinLTOPatchDICompileUnit(LLVMModuleRef Mod, DICompileUnit *Unit) { + Module *M = unwrap(Mod); + + // If the original source module didn't have a `DICompileUnit` then try to + // merge all the existing compile units. If there aren't actually any though + // then there's not much for us to do so return. + if (Unit == nullptr) { + for (DICompileUnit *CU : M->debug_compile_units()) { + Unit = CU; + break; + } + if (Unit == nullptr) + return; + } + + // Use LLVM's built-in `DebugInfoFinder` to find a bunch of debuginfo and + // process it recursively. Note that we used to specifically iterate over + // instructions to ensure we feed everything into it, but `processModule` + // started doing this the same way in LLVM 7 (commit d769eb36ab2b8). + DebugInfoFinder Finder; + Finder.processModule(*M); + + // After we've found all our debuginfo, rewrite all subprograms to point to + // the same `DICompileUnit`. + for (auto &F : Finder.subprograms()) { + F->replaceUnit(Unit); + } + + // Erase any other references to other `DICompileUnit` instances, the verifier + // will later ensure that we don't actually have any other stale references to + // worry about. + auto *MD = M->getNamedMetadata("llvm.dbg.cu"); + MD->clearOperands(); + MD->addOperand(Unit); +} diff --git a/compiler/rustc_llvm/llvm-wrapper/README b/compiler/rustc_llvm/llvm-wrapper/README new file mode 100644 index 00000000000..e1c6dd07d2b --- /dev/null +++ b/compiler/rustc_llvm/llvm-wrapper/README @@ -0,0 +1,16 @@ +This directory currently contains some LLVM support code. This will generally +be sent upstream to LLVM in time; for now it lives here. + +NOTE: the LLVM C++ ABI is subject to between-version breakage and must *never* +be exposed to Rust. To allow for easy auditing of that, all Rust-exposed types +must be typedef-ed as "LLVMXyz", or "LLVMRustXyz" if they were defined here. + +Functions that return a failure status and leave the error in +the LLVM last error should return an LLVMRustResult rather than an +int or anything to avoid confusion. + +When translating enums, add a single `Other` variant as the first +one to allow for new variants to be added. It should abort when used +as an input. + +All other types must not be typedef-ed as such. diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp new file mode 100644 index 00000000000..e85a9b76380 --- /dev/null +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -0,0 +1,1721 @@ +#include "LLVMWrapper.h" +#include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/IR/DiagnosticInfo.h" +#include "llvm/IR/DiagnosticPrinter.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/Object/Archive.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Bitcode/BitcodeWriterPass.h" +#include "llvm/Support/Signals.h" +#include "llvm/ADT/Optional.h" + +#include <iostream> + +//===----------------------------------------------------------------------=== +// +// This file defines alternate interfaces to core functions that are more +// readily callable by Rust's FFI. +// +//===----------------------------------------------------------------------=== + +using namespace llvm; +using namespace llvm::sys; +using namespace llvm::object; + +// LLVMAtomicOrdering is already an enum - don't create another +// one. +static AtomicOrdering fromRust(LLVMAtomicOrdering Ordering) { + switch (Ordering) { + case LLVMAtomicOrderingNotAtomic: + return AtomicOrdering::NotAtomic; + case LLVMAtomicOrderingUnordered: + return AtomicOrdering::Unordered; + case LLVMAtomicOrderingMonotonic: + return AtomicOrdering::Monotonic; + case LLVMAtomicOrderingAcquire: + return AtomicOrdering::Acquire; + case LLVMAtomicOrderingRelease: + return AtomicOrdering::Release; + case LLVMAtomicOrderingAcquireRelease: + return AtomicOrdering::AcquireRelease; + case LLVMAtomicOrderingSequentiallyConsistent: + return AtomicOrdering::SequentiallyConsistent; + } + + report_fatal_error("Invalid LLVMAtomicOrdering value!"); +} + +static LLVM_THREAD_LOCAL char *LastError; + +// Custom error handler for fatal LLVM errors. +// +// Notably it exits the process with code 101, unlike LLVM's default of 1. +static void FatalErrorHandler(void *UserData, + const std::string& Reason, + bool GenCrashDiag) { + // Do the same thing that the default error handler does. + std::cerr << "LLVM ERROR: " << Reason << std::endl; + + // Since this error handler exits the process, we have to run any cleanup that + // LLVM would run after handling the error. This might change with an LLVM + // upgrade. + sys::RunInterruptHandlers(); + + exit(101); +} + +extern "C" void LLVMRustInstallFatalErrorHandler() { + install_fatal_error_handler(FatalErrorHandler); +} + +extern "C" LLVMMemoryBufferRef +LLVMRustCreateMemoryBufferWithContentsOfFile(const char *Path) { + ErrorOr<std::unique_ptr<MemoryBuffer>> BufOr = + MemoryBuffer::getFile(Path, -1, false); + if (!BufOr) { + LLVMRustSetLastError(BufOr.getError().message().c_str()); + return nullptr; + } + return wrap(BufOr.get().release()); +} + +extern "C" char *LLVMRustGetLastError(void) { + char *Ret = LastError; + LastError = nullptr; + 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); +} + +extern "C" LLVMContextRef LLVMRustContextCreate(bool shouldDiscardNames) { + auto ctx = new LLVMContext(); + ctx->setDiscardValueNames(shouldDiscardNames); + return wrap(ctx); +} + +extern "C" void LLVMRustSetNormalizedTarget(LLVMModuleRef M, + const char *Triple) { + unwrap(M)->setTargetTriple(Triple::normalize(Triple)); +} + +extern "C" void LLVMRustPrintPassTimings() { + raw_fd_ostream OS(2, false); // stderr. + TimerGroup::printAll(OS); +} + +extern "C" LLVMValueRef LLVMRustGetNamedValue(LLVMModuleRef M, const char *Name, + size_t NameLen) { + return wrap(unwrap(M)->getNamedValue(StringRef(Name, NameLen))); +} + +extern "C" LLVMValueRef LLVMRustGetOrInsertFunction(LLVMModuleRef M, + const char *Name, + size_t NameLen, + LLVMTypeRef FunctionTy) { + return wrap(unwrap(M) + ->getOrInsertFunction(StringRef(Name, NameLen), + unwrap<FunctionType>(FunctionTy)) +#if LLVM_VERSION_GE(9, 0) + .getCallee() +#endif + ); +} + +extern "C" LLVMValueRef +LLVMRustGetOrInsertGlobal(LLVMModuleRef M, const char *Name, size_t NameLen, LLVMTypeRef Ty) { + StringRef NameRef(Name, NameLen); + return wrap(unwrap(M)->getOrInsertGlobal(NameRef, unwrap(Ty))); +} + +extern "C" LLVMValueRef +LLVMRustInsertPrivateGlobal(LLVMModuleRef M, LLVMTypeRef Ty) { + return wrap(new GlobalVariable(*unwrap(M), + unwrap(Ty), + false, + GlobalValue::PrivateLinkage, + nullptr)); +} + +extern "C" LLVMTypeRef LLVMRustMetadataTypeInContext(LLVMContextRef C) { + return wrap(Type::getMetadataTy(*unwrap(C))); +} + +static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) { + switch (Kind) { + case AlwaysInline: + return Attribute::AlwaysInline; + case ByVal: + return Attribute::ByVal; + case Cold: + return Attribute::Cold; + case InlineHint: + return Attribute::InlineHint; + case MinSize: + return Attribute::MinSize; + case Naked: + return Attribute::Naked; + case NoAlias: + return Attribute::NoAlias; + case NoCapture: + return Attribute::NoCapture; + case NoInline: + return Attribute::NoInline; + case NonNull: + return Attribute::NonNull; + case NoRedZone: + return Attribute::NoRedZone; + case NoReturn: + return Attribute::NoReturn; + case NoUnwind: + return Attribute::NoUnwind; + case OptimizeForSize: + return Attribute::OptimizeForSize; + case ReadOnly: + return Attribute::ReadOnly; + case SExt: + return Attribute::SExt; + case StructRet: + return Attribute::StructRet; + case UWTable: + return Attribute::UWTable; + case ZExt: + return Attribute::ZExt; + case InReg: + return Attribute::InReg; + case SanitizeThread: + return Attribute::SanitizeThread; + case SanitizeAddress: + return Attribute::SanitizeAddress; + case SanitizeMemory: + return Attribute::SanitizeMemory; + case NonLazyBind: + return Attribute::NonLazyBind; + case OptimizeNone: + return Attribute::OptimizeNone; + case ReturnsTwice: + return Attribute::ReturnsTwice; + case ReadNone: + return Attribute::ReadNone; + case InaccessibleMemOnly: + return Attribute::InaccessibleMemOnly; + } + report_fatal_error("bad AttributeKind"); +} + +extern "C" void LLVMRustAddCallSiteAttribute(LLVMValueRef Instr, unsigned Index, + LLVMRustAttribute RustAttr) { + CallBase *Call = unwrap<CallBase>(Instr); + Attribute Attr = Attribute::get(Call->getContext(), fromRust(RustAttr)); + Call->addAttribute(Index, Attr); +} + +extern "C" void LLVMRustAddAlignmentCallSiteAttr(LLVMValueRef Instr, + unsigned Index, + uint32_t Bytes) { + CallBase *Call = unwrap<CallBase>(Instr); + AttrBuilder B; + B.addAlignmentAttr(Bytes); + Call->setAttributes(Call->getAttributes().addAttributes( + Call->getContext(), Index, B)); +} + +extern "C" void LLVMRustAddDereferenceableCallSiteAttr(LLVMValueRef Instr, + unsigned Index, + uint64_t Bytes) { + CallBase *Call = unwrap<CallBase>(Instr); + AttrBuilder B; + B.addDereferenceableAttr(Bytes); + Call->setAttributes(Call->getAttributes().addAttributes( + Call->getContext(), Index, B)); +} + +extern "C" void LLVMRustAddDereferenceableOrNullCallSiteAttr(LLVMValueRef Instr, + unsigned Index, + uint64_t Bytes) { + CallBase *Call = unwrap<CallBase>(Instr); + AttrBuilder B; + B.addDereferenceableOrNullAttr(Bytes); + Call->setAttributes(Call->getAttributes().addAttributes( + Call->getContext(), Index, B)); +} + +extern "C" void LLVMRustAddByValCallSiteAttr(LLVMValueRef Instr, unsigned Index, + LLVMTypeRef Ty) { + CallBase *Call = unwrap<CallBase>(Instr); +#if LLVM_VERSION_GE(9, 0) + Attribute Attr = Attribute::getWithByValType(Call->getContext(), unwrap(Ty)); +#else + Attribute Attr = Attribute::get(Call->getContext(), Attribute::ByVal); +#endif + Call->addAttribute(Index, Attr); +} + +extern "C" void LLVMRustAddFunctionAttribute(LLVMValueRef Fn, unsigned Index, + LLVMRustAttribute RustAttr) { + Function *A = unwrap<Function>(Fn); + Attribute Attr = Attribute::get(A->getContext(), fromRust(RustAttr)); + AttrBuilder B(Attr); + A->addAttributes(Index, B); +} + +extern "C" void LLVMRustAddAlignmentAttr(LLVMValueRef Fn, + unsigned Index, + uint32_t Bytes) { + Function *A = unwrap<Function>(Fn); + AttrBuilder B; + B.addAlignmentAttr(Bytes); + A->addAttributes(Index, B); +} + +extern "C" void LLVMRustAddDereferenceableAttr(LLVMValueRef Fn, unsigned Index, + uint64_t Bytes) { + Function *A = unwrap<Function>(Fn); + AttrBuilder B; + B.addDereferenceableAttr(Bytes); + A->addAttributes(Index, B); +} + +extern "C" void LLVMRustAddDereferenceableOrNullAttr(LLVMValueRef Fn, + unsigned Index, + uint64_t Bytes) { + Function *A = unwrap<Function>(Fn); + AttrBuilder B; + B.addDereferenceableOrNullAttr(Bytes); + A->addAttributes(Index, B); +} + +extern "C" void LLVMRustAddByValAttr(LLVMValueRef Fn, unsigned Index, + LLVMTypeRef Ty) { + Function *F = unwrap<Function>(Fn); +#if LLVM_VERSION_GE(9, 0) + Attribute Attr = Attribute::getWithByValType(F->getContext(), unwrap(Ty)); +#else + Attribute Attr = Attribute::get(F->getContext(), Attribute::ByVal); +#endif + F->addAttribute(Index, Attr); +} + +extern "C" void LLVMRustAddFunctionAttrStringValue(LLVMValueRef Fn, + unsigned Index, + const char *Name, + const char *Value) { + Function *F = unwrap<Function>(Fn); + AttrBuilder B; + B.addAttribute(Name, Value); + F->addAttributes(Index, B); +} + +extern "C" void LLVMRustRemoveFunctionAttributes(LLVMValueRef Fn, + unsigned Index, + LLVMRustAttribute RustAttr) { + Function *F = unwrap<Function>(Fn); + Attribute Attr = Attribute::get(F->getContext(), fromRust(RustAttr)); + AttrBuilder B(Attr); + auto PAL = F->getAttributes(); + auto PALNew = PAL.removeAttributes(F->getContext(), Index, B); + F->setAttributes(PALNew); +} + +// enable fpmath flag UnsafeAlgebra +extern "C" void LLVMRustSetHasUnsafeAlgebra(LLVMValueRef V) { + if (auto I = dyn_cast<Instruction>(unwrap<Value>(V))) { + I->setFast(true); + } +} + +extern "C" LLVMValueRef +LLVMRustBuildAtomicLoad(LLVMBuilderRef B, LLVMValueRef Source, const char *Name, + LLVMAtomicOrdering Order) { + Value *Ptr = unwrap(Source); + Type *Ty = Ptr->getType()->getPointerElementType(); + LoadInst *LI = unwrap(B)->CreateLoad(Ty, Ptr, Name); + LI->setAtomic(fromRust(Order)); + return wrap(LI); +} + +extern "C" LLVMValueRef LLVMRustBuildAtomicStore(LLVMBuilderRef B, + LLVMValueRef V, + LLVMValueRef Target, + LLVMAtomicOrdering Order) { + StoreInst *SI = unwrap(B)->CreateStore(unwrap(V), unwrap(Target)); + SI->setAtomic(fromRust(Order)); + return wrap(SI); +} + +// FIXME: Use the C-API LLVMBuildAtomicCmpXchg and LLVMSetWeak +// once we raise our minimum support to LLVM 10. +extern "C" LLVMValueRef +LLVMRustBuildAtomicCmpXchg(LLVMBuilderRef B, LLVMValueRef Target, + LLVMValueRef Old, LLVMValueRef Source, + LLVMAtomicOrdering Order, + LLVMAtomicOrdering FailureOrder, LLVMBool Weak) { + AtomicCmpXchgInst *ACXI = unwrap(B)->CreateAtomicCmpXchg( + unwrap(Target), unwrap(Old), unwrap(Source), fromRust(Order), + fromRust(FailureOrder)); + ACXI->setWeak(Weak); + return wrap(ACXI); +} + +enum class LLVMRustSynchronizationScope { + SingleThread, + CrossThread, +}; + +static SyncScope::ID fromRust(LLVMRustSynchronizationScope Scope) { + switch (Scope) { + case LLVMRustSynchronizationScope::SingleThread: + return SyncScope::SingleThread; + case LLVMRustSynchronizationScope::CrossThread: + return SyncScope::System; + default: + report_fatal_error("bad SynchronizationScope."); + } +} + +extern "C" LLVMValueRef +LLVMRustBuildAtomicFence(LLVMBuilderRef B, LLVMAtomicOrdering Order, + LLVMRustSynchronizationScope Scope) { + return wrap(unwrap(B)->CreateFence(fromRust(Order), fromRust(Scope))); +} + +enum class LLVMRustAsmDialect { + Att, + Intel, +}; + +static InlineAsm::AsmDialect fromRust(LLVMRustAsmDialect Dialect) { + switch (Dialect) { + case LLVMRustAsmDialect::Att: + return InlineAsm::AD_ATT; + case LLVMRustAsmDialect::Intel: + return InlineAsm::AD_Intel; + default: + report_fatal_error("bad AsmDialect."); + } +} + +extern "C" LLVMValueRef +LLVMRustInlineAsm(LLVMTypeRef Ty, char *AsmString, size_t AsmStringLen, + char *Constraints, size_t ConstraintsLen, + LLVMBool HasSideEffects, LLVMBool IsAlignStack, + LLVMRustAsmDialect Dialect) { + return wrap(InlineAsm::get(unwrap<FunctionType>(Ty), + StringRef(AsmString, AsmStringLen), + StringRef(Constraints, ConstraintsLen), + HasSideEffects, IsAlignStack, fromRust(Dialect))); +} + +extern "C" bool LLVMRustInlineAsmVerify(LLVMTypeRef Ty, char *Constraints, + size_t ConstraintsLen) { + return InlineAsm::Verify(unwrap<FunctionType>(Ty), + StringRef(Constraints, ConstraintsLen)); +} + +extern "C" void LLVMRustAppendModuleInlineAsm(LLVMModuleRef M, const char *Asm, + size_t AsmLen) { + unwrap(M)->appendModuleInlineAsm(StringRef(Asm, AsmLen)); +} + +typedef DIBuilder *LLVMRustDIBuilderRef; + +template <typename DIT> DIT *unwrapDIPtr(LLVMMetadataRef Ref) { + return (DIT *)(Ref ? unwrap<MDNode>(Ref) : nullptr); +} + +#define DIDescriptor DIScope +#define DIArray DINodeArray +#define unwrapDI unwrapDIPtr + +// These values **must** match debuginfo::DIFlags! They also *happen* +// to match LLVM, but that isn't required as we do giant sets of +// matching below. The value shouldn't be directly passed to LLVM. +enum class LLVMRustDIFlags : uint32_t { + FlagZero = 0, + FlagPrivate = 1, + FlagProtected = 2, + FlagPublic = 3, + FlagFwdDecl = (1 << 2), + FlagAppleBlock = (1 << 3), + FlagBlockByrefStruct = (1 << 4), + FlagVirtual = (1 << 5), + FlagArtificial = (1 << 6), + FlagExplicit = (1 << 7), + FlagPrototyped = (1 << 8), + FlagObjcClassComplete = (1 << 9), + FlagObjectPointer = (1 << 10), + FlagVector = (1 << 11), + FlagStaticMember = (1 << 12), + FlagLValueReference = (1 << 13), + FlagRValueReference = (1 << 14), + FlagExternalTypeRef = (1 << 15), + FlagIntroducedVirtual = (1 << 18), + FlagBitField = (1 << 19), + FlagNoReturn = (1 << 20), + // Do not add values that are not supported by the minimum LLVM + // version we support! see llvm/include/llvm/IR/DebugInfoFlags.def +}; + +inline LLVMRustDIFlags operator&(LLVMRustDIFlags A, LLVMRustDIFlags B) { + return static_cast<LLVMRustDIFlags>(static_cast<uint32_t>(A) & + static_cast<uint32_t>(B)); +} + +inline LLVMRustDIFlags operator|(LLVMRustDIFlags A, LLVMRustDIFlags B) { + return static_cast<LLVMRustDIFlags>(static_cast<uint32_t>(A) | + static_cast<uint32_t>(B)); +} + +inline LLVMRustDIFlags &operator|=(LLVMRustDIFlags &A, LLVMRustDIFlags B) { + return A = A | B; +} + +inline bool isSet(LLVMRustDIFlags F) { return F != LLVMRustDIFlags::FlagZero; } + +inline LLVMRustDIFlags visibility(LLVMRustDIFlags F) { + return static_cast<LLVMRustDIFlags>(static_cast<uint32_t>(F) & 0x3); +} + +static DINode::DIFlags fromRust(LLVMRustDIFlags Flags) { + DINode::DIFlags Result = DINode::DIFlags::FlagZero; + + switch (visibility(Flags)) { + case LLVMRustDIFlags::FlagPrivate: + Result |= DINode::DIFlags::FlagPrivate; + break; + case LLVMRustDIFlags::FlagProtected: + Result |= DINode::DIFlags::FlagProtected; + break; + case LLVMRustDIFlags::FlagPublic: + Result |= DINode::DIFlags::FlagPublic; + break; + default: + // The rest are handled below + break; + } + + if (isSet(Flags & LLVMRustDIFlags::FlagFwdDecl)) { + Result |= DINode::DIFlags::FlagFwdDecl; + } + if (isSet(Flags & LLVMRustDIFlags::FlagAppleBlock)) { + Result |= DINode::DIFlags::FlagAppleBlock; + } +#if LLVM_VERSION_LT(10, 0) + if (isSet(Flags & LLVMRustDIFlags::FlagBlockByrefStruct)) { + Result |= DINode::DIFlags::FlagBlockByrefStruct; + } +#endif + if (isSet(Flags & LLVMRustDIFlags::FlagVirtual)) { + Result |= DINode::DIFlags::FlagVirtual; + } + if (isSet(Flags & LLVMRustDIFlags::FlagArtificial)) { + Result |= DINode::DIFlags::FlagArtificial; + } + if (isSet(Flags & LLVMRustDIFlags::FlagExplicit)) { + Result |= DINode::DIFlags::FlagExplicit; + } + if (isSet(Flags & LLVMRustDIFlags::FlagPrototyped)) { + Result |= DINode::DIFlags::FlagPrototyped; + } + if (isSet(Flags & LLVMRustDIFlags::FlagObjcClassComplete)) { + Result |= DINode::DIFlags::FlagObjcClassComplete; + } + if (isSet(Flags & LLVMRustDIFlags::FlagObjectPointer)) { + Result |= DINode::DIFlags::FlagObjectPointer; + } + if (isSet(Flags & LLVMRustDIFlags::FlagVector)) { + Result |= DINode::DIFlags::FlagVector; + } + if (isSet(Flags & LLVMRustDIFlags::FlagStaticMember)) { + Result |= DINode::DIFlags::FlagStaticMember; + } + if (isSet(Flags & LLVMRustDIFlags::FlagLValueReference)) { + Result |= DINode::DIFlags::FlagLValueReference; + } + if (isSet(Flags & LLVMRustDIFlags::FlagRValueReference)) { + Result |= DINode::DIFlags::FlagRValueReference; + } + if (isSet(Flags & LLVMRustDIFlags::FlagIntroducedVirtual)) { + Result |= DINode::DIFlags::FlagIntroducedVirtual; + } + if (isSet(Flags & LLVMRustDIFlags::FlagBitField)) { + Result |= DINode::DIFlags::FlagBitField; + } + if (isSet(Flags & LLVMRustDIFlags::FlagNoReturn)) { + Result |= DINode::DIFlags::FlagNoReturn; + } + + return Result; +} + +// These values **must** match debuginfo::DISPFlags! They also *happen* +// to match LLVM, but that isn't required as we do giant sets of +// matching below. The value shouldn't be directly passed to LLVM. +enum class LLVMRustDISPFlags : uint32_t { + SPFlagZero = 0, + SPFlagVirtual = 1, + SPFlagPureVirtual = 2, + SPFlagLocalToUnit = (1 << 2), + SPFlagDefinition = (1 << 3), + SPFlagOptimized = (1 << 4), + SPFlagMainSubprogram = (1 << 5), + // Do not add values that are not supported by the minimum LLVM + // version we support! see llvm/include/llvm/IR/DebugInfoFlags.def + // (In LLVM < 8, createFunction supported these as separate bool arguments.) +}; + +inline LLVMRustDISPFlags operator&(LLVMRustDISPFlags A, LLVMRustDISPFlags B) { + return static_cast<LLVMRustDISPFlags>(static_cast<uint32_t>(A) & + static_cast<uint32_t>(B)); +} + +inline LLVMRustDISPFlags operator|(LLVMRustDISPFlags A, LLVMRustDISPFlags B) { + return static_cast<LLVMRustDISPFlags>(static_cast<uint32_t>(A) | + static_cast<uint32_t>(B)); +} + +inline LLVMRustDISPFlags &operator|=(LLVMRustDISPFlags &A, LLVMRustDISPFlags B) { + return A = A | B; +} + +inline bool isSet(LLVMRustDISPFlags F) { return F != LLVMRustDISPFlags::SPFlagZero; } + +inline LLVMRustDISPFlags virtuality(LLVMRustDISPFlags F) { + return static_cast<LLVMRustDISPFlags>(static_cast<uint32_t>(F) & 0x3); +} + +static DISubprogram::DISPFlags fromRust(LLVMRustDISPFlags SPFlags) { + DISubprogram::DISPFlags Result = DISubprogram::DISPFlags::SPFlagZero; + + switch (virtuality(SPFlags)) { + case LLVMRustDISPFlags::SPFlagVirtual: + Result |= DISubprogram::DISPFlags::SPFlagVirtual; + break; + case LLVMRustDISPFlags::SPFlagPureVirtual: + Result |= DISubprogram::DISPFlags::SPFlagPureVirtual; + break; + default: + // The rest are handled below + break; + } + + if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagLocalToUnit)) { + Result |= DISubprogram::DISPFlags::SPFlagLocalToUnit; + } + if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagDefinition)) { + Result |= DISubprogram::DISPFlags::SPFlagDefinition; + } + if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagOptimized)) { + Result |= DISubprogram::DISPFlags::SPFlagOptimized; + } +#if LLVM_VERSION_GE(9, 0) + if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagMainSubprogram)) { + Result |= DISubprogram::DISPFlags::SPFlagMainSubprogram; + } +#endif + + return Result; +} + +enum class LLVMRustDebugEmissionKind { + NoDebug, + FullDebug, + LineTablesOnly, +}; + +static DICompileUnit::DebugEmissionKind fromRust(LLVMRustDebugEmissionKind Kind) { + switch (Kind) { + case LLVMRustDebugEmissionKind::NoDebug: + return DICompileUnit::DebugEmissionKind::NoDebug; + case LLVMRustDebugEmissionKind::FullDebug: + return DICompileUnit::DebugEmissionKind::FullDebug; + case LLVMRustDebugEmissionKind::LineTablesOnly: + return DICompileUnit::DebugEmissionKind::LineTablesOnly; + default: + report_fatal_error("bad DebugEmissionKind."); + } +} + +enum class LLVMRustChecksumKind { + None, + MD5, + SHA1, +}; + +static Optional<DIFile::ChecksumKind> fromRust(LLVMRustChecksumKind Kind) { + switch (Kind) { + case LLVMRustChecksumKind::None: + return None; + case LLVMRustChecksumKind::MD5: + return DIFile::ChecksumKind::CSK_MD5; + case LLVMRustChecksumKind::SHA1: + return DIFile::ChecksumKind::CSK_SHA1; + default: + report_fatal_error("bad ChecksumKind."); + } +} + +extern "C" uint32_t LLVMRustDebugMetadataVersion() { + return DEBUG_METADATA_VERSION; +} + +extern "C" uint32_t LLVMRustVersionMinor() { return LLVM_VERSION_MINOR; } + +extern "C" uint32_t LLVMRustVersionMajor() { return LLVM_VERSION_MAJOR; } + +extern "C" void LLVMRustAddModuleFlag(LLVMModuleRef M, const char *Name, + uint32_t Value) { + unwrap(M)->addModuleFlag(Module::Warning, Name, Value); +} + +extern "C" LLVMValueRef LLVMRustMetadataAsValue(LLVMContextRef C, LLVMMetadataRef MD) { + return wrap(MetadataAsValue::get(*unwrap(C), unwrap(MD))); +} + +extern "C" LLVMRustDIBuilderRef LLVMRustDIBuilderCreate(LLVMModuleRef M) { + return new DIBuilder(*unwrap(M)); +} + +extern "C" void LLVMRustDIBuilderDispose(LLVMRustDIBuilderRef Builder) { + delete Builder; +} + +extern "C" void LLVMRustDIBuilderFinalize(LLVMRustDIBuilderRef Builder) { + Builder->finalize(); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateCompileUnit( + LLVMRustDIBuilderRef Builder, unsigned Lang, LLVMMetadataRef FileRef, + const char *Producer, size_t ProducerLen, bool isOptimized, + const char *Flags, unsigned RuntimeVer, + const char *SplitName, size_t SplitNameLen, + LLVMRustDebugEmissionKind Kind) { + auto *File = unwrapDI<DIFile>(FileRef); + + return wrap(Builder->createCompileUnit(Lang, File, StringRef(Producer, ProducerLen), + isOptimized, Flags, RuntimeVer, + StringRef(SplitName, SplitNameLen), + fromRust(Kind))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFile( + LLVMRustDIBuilderRef Builder, + const char *Filename, size_t FilenameLen, + const char *Directory, size_t DirectoryLen, LLVMRustChecksumKind CSKind, + const char *Checksum, size_t ChecksumLen) { + Optional<DIFile::ChecksumKind> llvmCSKind = fromRust(CSKind); + Optional<DIFile::ChecksumInfo<StringRef>> CSInfo{}; + if (llvmCSKind) + CSInfo.emplace(*llvmCSKind, StringRef{Checksum, ChecksumLen}); + return wrap(Builder->createFile(StringRef(Filename, FilenameLen), + StringRef(Directory, DirectoryLen), + CSInfo)); +} + +extern "C" LLVMMetadataRef +LLVMRustDIBuilderCreateSubroutineType(LLVMRustDIBuilderRef Builder, + LLVMMetadataRef ParameterTypes) { + return wrap(Builder->createSubroutineType( + DITypeRefArray(unwrap<MDTuple>(ParameterTypes)))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFunction( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, + const char *LinkageName, size_t LinkageNameLen, + LLVMMetadataRef File, unsigned LineNo, + LLVMMetadataRef Ty, unsigned ScopeLine, LLVMRustDIFlags Flags, + LLVMRustDISPFlags SPFlags, LLVMValueRef Fn, LLVMMetadataRef TParam, + LLVMMetadataRef Decl) { + DITemplateParameterArray TParams = + DITemplateParameterArray(unwrap<MDTuple>(TParam)); + DISubprogram::DISPFlags llvmSPFlags = fromRust(SPFlags); + DINode::DIFlags llvmFlags = fromRust(Flags); +#if LLVM_VERSION_LT(9, 0) + if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagMainSubprogram)) + llvmFlags |= DINode::DIFlags::FlagMainSubprogram; +#endif + DISubprogram *Sub = Builder->createFunction( + unwrapDI<DIScope>(Scope), + StringRef(Name, NameLen), + StringRef(LinkageName, LinkageNameLen), + unwrapDI<DIFile>(File), LineNo, + unwrapDI<DISubroutineType>(Ty), ScopeLine, llvmFlags, + llvmSPFlags, TParams, unwrapDIPtr<DISubprogram>(Decl)); + unwrap<Function>(Fn)->setSubprogram(Sub); + return wrap(Sub); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateBasicType( + LLVMRustDIBuilderRef Builder, const char *Name, size_t NameLen, + uint64_t SizeInBits, unsigned Encoding) { + return wrap(Builder->createBasicType(StringRef(Name, NameLen), SizeInBits, Encoding)); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateTypedef( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Type, const char *Name, size_t NameLen, + LLVMMetadataRef File, unsigned LineNo, LLVMMetadataRef Scope) { + return wrap(Builder->createTypedef( + unwrap<DIType>(Type), StringRef(Name, NameLen), unwrap<DIFile>(File), + LineNo, unwrap<DIScope>(Scope))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreatePointerType( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef PointeeTy, + uint64_t SizeInBits, uint32_t AlignInBits, unsigned AddressSpace, + const char *Name, size_t NameLen) { + return wrap(Builder->createPointerType(unwrapDI<DIType>(PointeeTy), + SizeInBits, AlignInBits, + AddressSpace, + StringRef(Name, NameLen))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStructType( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, + LLVMMetadataRef File, unsigned LineNumber, uint64_t SizeInBits, + uint32_t AlignInBits, LLVMRustDIFlags Flags, + LLVMMetadataRef DerivedFrom, LLVMMetadataRef Elements, + unsigned RunTimeLang, LLVMMetadataRef VTableHolder, + const char *UniqueId, size_t UniqueIdLen) { + return wrap(Builder->createStructType( + unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), + unwrapDI<DIFile>(File), LineNumber, + SizeInBits, AlignInBits, fromRust(Flags), unwrapDI<DIType>(DerivedFrom), + DINodeArray(unwrapDI<MDTuple>(Elements)), RunTimeLang, + unwrapDI<DIType>(VTableHolder), StringRef(UniqueId, UniqueIdLen))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantPart( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, + LLVMMetadataRef File, unsigned LineNumber, uint64_t SizeInBits, + uint32_t AlignInBits, LLVMRustDIFlags Flags, LLVMMetadataRef Discriminator, + LLVMMetadataRef Elements, const char *UniqueId, size_t UniqueIdLen) { + return wrap(Builder->createVariantPart( + unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), + unwrapDI<DIFile>(File), LineNumber, + SizeInBits, AlignInBits, fromRust(Flags), unwrapDI<DIDerivedType>(Discriminator), + DINodeArray(unwrapDI<MDTuple>(Elements)), StringRef(UniqueId, UniqueIdLen))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateMemberType( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, + LLVMMetadataRef File, unsigned LineNo, uint64_t SizeInBits, + uint32_t AlignInBits, uint64_t OffsetInBits, LLVMRustDIFlags Flags, + LLVMMetadataRef Ty) { + return wrap(Builder->createMemberType(unwrapDI<DIDescriptor>(Scope), + StringRef(Name, NameLen), + unwrapDI<DIFile>(File), LineNo, + SizeInBits, AlignInBits, OffsetInBits, + fromRust(Flags), unwrapDI<DIType>(Ty))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantMemberType( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNo, + uint64_t SizeInBits, uint32_t AlignInBits, uint64_t OffsetInBits, LLVMValueRef Discriminant, + LLVMRustDIFlags Flags, LLVMMetadataRef Ty) { + llvm::ConstantInt* D = nullptr; + if (Discriminant) { + D = unwrap<llvm::ConstantInt>(Discriminant); + } + return wrap(Builder->createVariantMemberType(unwrapDI<DIDescriptor>(Scope), + StringRef(Name, NameLen), + unwrapDI<DIFile>(File), LineNo, + SizeInBits, AlignInBits, OffsetInBits, D, + fromRust(Flags), unwrapDI<DIType>(Ty))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateLexicalBlock( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + LLVMMetadataRef File, unsigned Line, unsigned Col) { + return wrap(Builder->createLexicalBlock(unwrapDI<DIDescriptor>(Scope), + unwrapDI<DIFile>(File), Line, Col)); +} + +extern "C" LLVMMetadataRef +LLVMRustDIBuilderCreateLexicalBlockFile(LLVMRustDIBuilderRef Builder, + LLVMMetadataRef Scope, + LLVMMetadataRef File) { + return wrap(Builder->createLexicalBlockFile(unwrapDI<DIDescriptor>(Scope), + unwrapDI<DIFile>(File))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticVariable( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Context, + const char *Name, size_t NameLen, + const char *LinkageName, size_t LinkageNameLen, + LLVMMetadataRef File, unsigned LineNo, + LLVMMetadataRef Ty, bool IsLocalToUnit, LLVMValueRef V, + LLVMMetadataRef Decl = nullptr, uint32_t AlignInBits = 0) { + llvm::GlobalVariable *InitVal = cast<llvm::GlobalVariable>(unwrap(V)); + + llvm::DIExpression *InitExpr = nullptr; + if (llvm::ConstantInt *IntVal = llvm::dyn_cast<llvm::ConstantInt>(InitVal)) { + InitExpr = Builder->createConstantValueExpression( + IntVal->getValue().getSExtValue()); + } else if (llvm::ConstantFP *FPVal = + llvm::dyn_cast<llvm::ConstantFP>(InitVal)) { + InitExpr = Builder->createConstantValueExpression( + FPVal->getValueAPF().bitcastToAPInt().getZExtValue()); + } + + llvm::DIGlobalVariableExpression *VarExpr = Builder->createGlobalVariableExpression( + unwrapDI<DIDescriptor>(Context), StringRef(Name, NameLen), + StringRef(LinkageName, LinkageNameLen), + unwrapDI<DIFile>(File), LineNo, unwrapDI<DIType>(Ty), IsLocalToUnit, +#if LLVM_VERSION_GE(10, 0) + /* isDefined */ true, +#endif + InitExpr, unwrapDIPtr<MDNode>(Decl), + /* templateParams */ nullptr, + AlignInBits); + + InitVal->setMetadata("dbg", VarExpr); + + return wrap(VarExpr); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariable( + LLVMRustDIBuilderRef Builder, unsigned Tag, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, + LLVMMetadataRef File, unsigned LineNo, + LLVMMetadataRef Ty, bool AlwaysPreserve, LLVMRustDIFlags Flags, + unsigned ArgNo, uint32_t AlignInBits) { + if (Tag == 0x100) { // DW_TAG_auto_variable + return wrap(Builder->createAutoVariable( + unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), + unwrapDI<DIFile>(File), LineNo, + unwrapDI<DIType>(Ty), AlwaysPreserve, fromRust(Flags), AlignInBits)); + } else { + return wrap(Builder->createParameterVariable( + unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), ArgNo, + unwrapDI<DIFile>(File), LineNo, + unwrapDI<DIType>(Ty), AlwaysPreserve, fromRust(Flags))); + } +} + +extern "C" LLVMMetadataRef +LLVMRustDIBuilderCreateArrayType(LLVMRustDIBuilderRef Builder, uint64_t Size, + uint32_t AlignInBits, LLVMMetadataRef Ty, + LLVMMetadataRef Subscripts) { + return wrap( + Builder->createArrayType(Size, AlignInBits, unwrapDI<DIType>(Ty), + DINodeArray(unwrapDI<MDTuple>(Subscripts)))); +} + +extern "C" LLVMMetadataRef +LLVMRustDIBuilderGetOrCreateSubrange(LLVMRustDIBuilderRef Builder, int64_t Lo, + int64_t Count) { + return wrap(Builder->getOrCreateSubrange(Lo, Count)); +} + +extern "C" LLVMMetadataRef +LLVMRustDIBuilderGetOrCreateArray(LLVMRustDIBuilderRef Builder, + LLVMMetadataRef *Ptr, unsigned Count) { + Metadata **DataValue = unwrap(Ptr); + return wrap( + Builder->getOrCreateArray(ArrayRef<Metadata *>(DataValue, Count)).get()); +} + +extern "C" LLVMValueRef LLVMRustDIBuilderInsertDeclareAtEnd( + LLVMRustDIBuilderRef Builder, LLVMValueRef V, LLVMMetadataRef VarInfo, + int64_t *AddrOps, unsigned AddrOpsCount, LLVMValueRef DL, + LLVMBasicBlockRef InsertAtEnd) { + return wrap(Builder->insertDeclare( + unwrap(V), unwrap<DILocalVariable>(VarInfo), + Builder->createExpression(llvm::ArrayRef<int64_t>(AddrOps, AddrOpsCount)), + DebugLoc(cast<MDNode>(unwrap<MetadataAsValue>(DL)->getMetadata())), + unwrap(InsertAtEnd))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerator( + LLVMRustDIBuilderRef Builder, const char *Name, size_t NameLen, + int64_t Value, bool IsUnsigned) { + return wrap(Builder->createEnumerator(StringRef(Name, NameLen), Value, IsUnsigned)); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerationType( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, + LLVMMetadataRef File, unsigned LineNumber, uint64_t SizeInBits, + uint32_t AlignInBits, LLVMMetadataRef Elements, + LLVMMetadataRef ClassTy, bool IsScoped) { + return wrap(Builder->createEnumerationType( + unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), + unwrapDI<DIFile>(File), LineNumber, + SizeInBits, AlignInBits, DINodeArray(unwrapDI<MDTuple>(Elements)), + unwrapDI<DIType>(ClassTy), "", IsScoped)); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateUnionType( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, + LLVMMetadataRef File, unsigned LineNumber, uint64_t SizeInBits, + uint32_t AlignInBits, LLVMRustDIFlags Flags, LLVMMetadataRef Elements, + unsigned RunTimeLang, const char *UniqueId, size_t UniqueIdLen) { + return wrap(Builder->createUnionType( + unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), unwrapDI<DIFile>(File), + LineNumber, SizeInBits, AlignInBits, fromRust(Flags), + DINodeArray(unwrapDI<MDTuple>(Elements)), RunTimeLang, + StringRef(UniqueId, UniqueIdLen))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateTemplateTypeParameter( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, LLVMMetadataRef Ty) { +#if LLVM_VERSION_GE(11, 0) + bool IsDefault = false; // FIXME: should we ever set this true? + return wrap(Builder->createTemplateTypeParameter( + unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), unwrapDI<DIType>(Ty), IsDefault)); +#else + return wrap(Builder->createTemplateTypeParameter( + unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), unwrapDI<DIType>(Ty))); +#endif +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateNameSpace( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, bool ExportSymbols) { + return wrap(Builder->createNameSpace( + unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), ExportSymbols + )); +} + +extern "C" void +LLVMRustDICompositeTypeReplaceArrays(LLVMRustDIBuilderRef Builder, + LLVMMetadataRef CompositeTy, + LLVMMetadataRef Elements, + LLVMMetadataRef Params) { + DICompositeType *Tmp = unwrapDI<DICompositeType>(CompositeTy); + Builder->replaceArrays(Tmp, DINodeArray(unwrap<MDTuple>(Elements)), + DINodeArray(unwrap<MDTuple>(Params))); +} + +extern "C" LLVMValueRef +LLVMRustDIBuilderCreateDebugLocation(LLVMContextRef ContextRef, unsigned Line, + unsigned Column, LLVMMetadataRef Scope, + LLVMMetadataRef InlinedAt) { + LLVMContext &Context = *unwrap(ContextRef); + + DebugLoc debug_loc = DebugLoc::get(Line, Column, unwrapDIPtr<MDNode>(Scope), + unwrapDIPtr<MDNode>(InlinedAt)); + + return wrap(MetadataAsValue::get(Context, debug_loc.getAsMDNode())); +} + +extern "C" int64_t LLVMRustDIBuilderCreateOpDeref() { + return dwarf::DW_OP_deref; +} + +extern "C" int64_t LLVMRustDIBuilderCreateOpPlusUconst() { + return dwarf::DW_OP_plus_uconst; +} + +extern "C" void LLVMRustWriteTypeToString(LLVMTypeRef Ty, RustStringRef Str) { + RawRustStringOstream OS(Str); + unwrap<llvm::Type>(Ty)->print(OS); +} + +extern "C" void LLVMRustWriteValueToString(LLVMValueRef V, + RustStringRef Str) { + RawRustStringOstream OS(Str); + if (!V) { + OS << "(null)"; + } else { + OS << "("; + unwrap<llvm::Value>(V)->getType()->print(OS); + OS << ":"; + unwrap<llvm::Value>(V)->print(OS); + OS << ")"; + } +} + +// Note that the two following functions look quite similar to the +// LLVMGetSectionName function. Sadly, it appears that this function only +// returns a char* pointer, which isn't guaranteed to be null-terminated. The +// function provided by LLVM doesn't return the length, so we've created our own +// function which returns the length as well as the data pointer. +// +// For an example of this not returning a null terminated string, see +// lib/Object/COFFObjectFile.cpp in the getSectionName function. One of the +// branches explicitly creates a StringRef without a null terminator, and then +// that's returned. + +inline section_iterator *unwrap(LLVMSectionIteratorRef SI) { + return reinterpret_cast<section_iterator *>(SI); +} + +extern "C" size_t LLVMRustGetSectionName(LLVMSectionIteratorRef SI, + const char **Ptr) { +#if LLVM_VERSION_GE(10, 0) + auto NameOrErr = (*unwrap(SI))->getName(); + if (!NameOrErr) + report_fatal_error(NameOrErr.takeError()); + *Ptr = NameOrErr->data(); + return NameOrErr->size(); +#else + StringRef Ret; + if (std::error_code EC = (*unwrap(SI))->getName(Ret)) + report_fatal_error(EC.message()); + *Ptr = Ret.data(); + return Ret.size(); +#endif +} + +// LLVMArrayType function does not support 64-bit ElementCount +extern "C" LLVMTypeRef LLVMRustArrayType(LLVMTypeRef ElementTy, + uint64_t ElementCount) { + return wrap(ArrayType::get(unwrap(ElementTy), ElementCount)); +} + +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(Twine, LLVMTwineRef) + +extern "C" void LLVMRustWriteTwineToString(LLVMTwineRef T, RustStringRef Str) { + RawRustStringOstream OS(Str); + unwrap(T)->print(OS); +} + +extern "C" void LLVMRustUnpackOptimizationDiagnostic( + LLVMDiagnosticInfoRef DI, RustStringRef PassNameOut, + LLVMValueRef *FunctionOut, unsigned* Line, unsigned* Column, + RustStringRef FilenameOut, RustStringRef MessageOut) { + // Undefined to call this not on an optimization diagnostic! + llvm::DiagnosticInfoOptimizationBase *Opt = + static_cast<llvm::DiagnosticInfoOptimizationBase *>(unwrap(DI)); + + RawRustStringOstream PassNameOS(PassNameOut); + PassNameOS << Opt->getPassName(); + *FunctionOut = wrap(&Opt->getFunction()); + + RawRustStringOstream FilenameOS(FilenameOut); + DiagnosticLocation loc = Opt->getLocation(); + if (loc.isValid()) { + *Line = loc.getLine(); + *Column = loc.getColumn(); + FilenameOS << loc.getAbsolutePath(); + } + + RawRustStringOstream MessageOS(MessageOut); + MessageOS << Opt->getMsg(); +} + +enum class LLVMRustDiagnosticLevel { + Error, + Warning, + Note, + Remark, +}; + +extern "C" void +LLVMRustUnpackInlineAsmDiagnostic(LLVMDiagnosticInfoRef DI, + LLVMRustDiagnosticLevel *LevelOut, + unsigned *CookieOut, + LLVMTwineRef *MessageOut, + LLVMValueRef *InstructionOut) { + // Undefined to call this not on an inline assembly diagnostic! + llvm::DiagnosticInfoInlineAsm *IA = + static_cast<llvm::DiagnosticInfoInlineAsm *>(unwrap(DI)); + + *CookieOut = IA->getLocCookie(); + *MessageOut = wrap(&IA->getMsgStr()); + *InstructionOut = wrap(IA->getInstruction()); + + switch (IA->getSeverity()) { + case DS_Error: + *LevelOut = LLVMRustDiagnosticLevel::Error; + break; + case DS_Warning: + *LevelOut = LLVMRustDiagnosticLevel::Warning; + break; + case DS_Note: + *LevelOut = LLVMRustDiagnosticLevel::Note; + break; + case DS_Remark: + *LevelOut = LLVMRustDiagnosticLevel::Remark; + break; + default: + report_fatal_error("Invalid LLVMRustDiagnosticLevel value!"); + } +} + +extern "C" void LLVMRustWriteDiagnosticInfoToString(LLVMDiagnosticInfoRef DI, + RustStringRef Str) { + RawRustStringOstream OS(Str); + DiagnosticPrinterRawOStream DP(OS); + unwrap(DI)->print(DP); +} + +enum class LLVMRustDiagnosticKind { + Other, + InlineAsm, + StackSize, + DebugMetadataVersion, + SampleProfile, + OptimizationRemark, + OptimizationRemarkMissed, + OptimizationRemarkAnalysis, + OptimizationRemarkAnalysisFPCommute, + OptimizationRemarkAnalysisAliasing, + OptimizationRemarkOther, + OptimizationFailure, + PGOProfile, + Linker, +}; + +static LLVMRustDiagnosticKind toRust(DiagnosticKind Kind) { + switch (Kind) { + case DK_InlineAsm: + return LLVMRustDiagnosticKind::InlineAsm; + case DK_StackSize: + return LLVMRustDiagnosticKind::StackSize; + case DK_DebugMetadataVersion: + return LLVMRustDiagnosticKind::DebugMetadataVersion; + case DK_SampleProfile: + return LLVMRustDiagnosticKind::SampleProfile; + case DK_OptimizationRemark: + return LLVMRustDiagnosticKind::OptimizationRemark; + case DK_OptimizationRemarkMissed: + return LLVMRustDiagnosticKind::OptimizationRemarkMissed; + case DK_OptimizationRemarkAnalysis: + return LLVMRustDiagnosticKind::OptimizationRemarkAnalysis; + case DK_OptimizationRemarkAnalysisFPCommute: + return LLVMRustDiagnosticKind::OptimizationRemarkAnalysisFPCommute; + case DK_OptimizationRemarkAnalysisAliasing: + return LLVMRustDiagnosticKind::OptimizationRemarkAnalysisAliasing; + case DK_PGOProfile: + return LLVMRustDiagnosticKind::PGOProfile; + case DK_Linker: + return LLVMRustDiagnosticKind::Linker; + default: + return (Kind >= DK_FirstRemark && Kind <= DK_LastRemark) + ? LLVMRustDiagnosticKind::OptimizationRemarkOther + : LLVMRustDiagnosticKind::Other; + } +} + +extern "C" LLVMRustDiagnosticKind +LLVMRustGetDiagInfoKind(LLVMDiagnosticInfoRef DI) { + return toRust((DiagnosticKind)unwrap(DI)->getKind()); +} + +// This is kept distinct from LLVMGetTypeKind, because when +// a new type kind is added, the Rust-side enum must be +// updated or UB will result. +extern "C" LLVMTypeKind LLVMRustGetTypeKind(LLVMTypeRef Ty) { + switch (unwrap(Ty)->getTypeID()) { + case Type::VoidTyID: + return LLVMVoidTypeKind; + case Type::HalfTyID: + return LLVMHalfTypeKind; + case Type::FloatTyID: + return LLVMFloatTypeKind; + case Type::DoubleTyID: + return LLVMDoubleTypeKind; + case Type::X86_FP80TyID: + return LLVMX86_FP80TypeKind; + case Type::FP128TyID: + return LLVMFP128TypeKind; + case Type::PPC_FP128TyID: + return LLVMPPC_FP128TypeKind; + case Type::LabelTyID: + return LLVMLabelTypeKind; + case Type::MetadataTyID: + return LLVMMetadataTypeKind; + case Type::IntegerTyID: + return LLVMIntegerTypeKind; + case Type::FunctionTyID: + return LLVMFunctionTypeKind; + case Type::StructTyID: + return LLVMStructTypeKind; + case Type::ArrayTyID: + return LLVMArrayTypeKind; + case Type::PointerTyID: + return LLVMPointerTypeKind; +#if LLVM_VERSION_GE(11, 0) + case Type::FixedVectorTyID: + return LLVMVectorTypeKind; +#else + case Type::VectorTyID: + return LLVMVectorTypeKind; +#endif + case Type::X86_MMXTyID: + return LLVMX86_MMXTypeKind; + case Type::TokenTyID: + return LLVMTokenTypeKind; +#if LLVM_VERSION_GE(11, 0) + case Type::ScalableVectorTyID: + return LLVMScalableVectorTypeKind; + case Type::BFloatTyID: + return LLVMBFloatTypeKind; +#endif + } + report_fatal_error("Unhandled TypeID."); +} + +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(SMDiagnostic, LLVMSMDiagnosticRef) + +extern "C" void LLVMRustSetInlineAsmDiagnosticHandler( + LLVMContextRef C, LLVMContext::InlineAsmDiagHandlerTy H, void *CX) { + unwrap(C)->setInlineAsmDiagnosticHandler(H, CX); +} + +extern "C" bool LLVMRustUnpackSMDiagnostic(LLVMSMDiagnosticRef DRef, + RustStringRef MessageOut, + RustStringRef BufferOut, + LLVMRustDiagnosticLevel* LevelOut, + unsigned* LocOut, + unsigned* RangesOut, + size_t* NumRanges) { + SMDiagnostic& D = *unwrap(DRef); + RawRustStringOstream MessageOS(MessageOut); + MessageOS << D.getMessage(); + + switch (D.getKind()) { + case SourceMgr::DK_Error: + *LevelOut = LLVMRustDiagnosticLevel::Error; + break; + case SourceMgr::DK_Warning: + *LevelOut = LLVMRustDiagnosticLevel::Warning; + break; + case SourceMgr::DK_Note: + *LevelOut = LLVMRustDiagnosticLevel::Note; + break; + case SourceMgr::DK_Remark: + *LevelOut = LLVMRustDiagnosticLevel::Remark; + break; + default: + report_fatal_error("Invalid LLVMRustDiagnosticLevel value!"); + } + + if (D.getLoc() == SMLoc()) + return false; + + const SourceMgr &LSM = *D.getSourceMgr(); + const MemoryBuffer *LBuf = LSM.getMemoryBuffer(LSM.FindBufferContainingLoc(D.getLoc())); + LLVMRustStringWriteImpl(BufferOut, LBuf->getBufferStart(), LBuf->getBufferSize()); + + *LocOut = D.getLoc().getPointer() - LBuf->getBufferStart(); + + *NumRanges = std::min(*NumRanges, D.getRanges().size()); + size_t LineStart = *LocOut - (size_t)D.getColumnNo(); + for (size_t i = 0; i < *NumRanges; i++) { + RangesOut[i * 2] = LineStart + D.getRanges()[i].first; + RangesOut[i * 2 + 1] = LineStart + D.getRanges()[i].second; + } + + return true; +} + +extern "C" LLVMValueRef LLVMRustBuildCleanupPad(LLVMBuilderRef B, + LLVMValueRef ParentPad, + unsigned ArgCount, + LLVMValueRef *LLArgs, + const char *Name) { + Value **Args = unwrap(LLArgs); + if (ParentPad == nullptr) { + Type *Ty = Type::getTokenTy(unwrap(B)->getContext()); + ParentPad = wrap(Constant::getNullValue(Ty)); + } + return wrap(unwrap(B)->CreateCleanupPad( + unwrap(ParentPad), ArrayRef<Value *>(Args, ArgCount), Name)); +} + +extern "C" LLVMValueRef LLVMRustBuildCleanupRet(LLVMBuilderRef B, + LLVMValueRef CleanupPad, + LLVMBasicBlockRef UnwindBB) { + CleanupPadInst *Inst = cast<CleanupPadInst>(unwrap(CleanupPad)); + return wrap(unwrap(B)->CreateCleanupRet(Inst, unwrap(UnwindBB))); +} + +extern "C" LLVMValueRef +LLVMRustBuildCatchPad(LLVMBuilderRef B, LLVMValueRef ParentPad, + unsigned ArgCount, LLVMValueRef *LLArgs, const char *Name) { + Value **Args = unwrap(LLArgs); + return wrap(unwrap(B)->CreateCatchPad( + unwrap(ParentPad), ArrayRef<Value *>(Args, ArgCount), Name)); +} + +extern "C" LLVMValueRef LLVMRustBuildCatchRet(LLVMBuilderRef B, + LLVMValueRef Pad, + LLVMBasicBlockRef BB) { + return wrap(unwrap(B)->CreateCatchRet(cast<CatchPadInst>(unwrap(Pad)), + unwrap(BB))); +} + +extern "C" LLVMValueRef LLVMRustBuildCatchSwitch(LLVMBuilderRef B, + LLVMValueRef ParentPad, + LLVMBasicBlockRef BB, + unsigned NumHandlers, + const char *Name) { + if (ParentPad == nullptr) { + Type *Ty = Type::getTokenTy(unwrap(B)->getContext()); + ParentPad = wrap(Constant::getNullValue(Ty)); + } + return wrap(unwrap(B)->CreateCatchSwitch(unwrap(ParentPad), unwrap(BB), + NumHandlers, Name)); +} + +extern "C" void LLVMRustAddHandler(LLVMValueRef CatchSwitchRef, + LLVMBasicBlockRef Handler) { + Value *CatchSwitch = unwrap(CatchSwitchRef); + cast<CatchSwitchInst>(CatchSwitch)->addHandler(unwrap(Handler)); +} + +extern "C" OperandBundleDef *LLVMRustBuildOperandBundleDef(const char *Name, + LLVMValueRef *Inputs, + unsigned NumInputs) { + return new OperandBundleDef(Name, makeArrayRef(unwrap(Inputs), NumInputs)); +} + +extern "C" void LLVMRustFreeOperandBundleDef(OperandBundleDef *Bundle) { + delete Bundle; +} + +extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, + LLVMValueRef *Args, unsigned NumArgs, + OperandBundleDef *Bundle) { + Value *Callee = unwrap(Fn); + FunctionType *FTy = cast<FunctionType>(Callee->getType()->getPointerElementType()); + unsigned Len = Bundle ? 1 : 0; + ArrayRef<OperandBundleDef> Bundles = makeArrayRef(Bundle, Len); + return wrap(unwrap(B)->CreateCall( + FTy, Callee, makeArrayRef(unwrap(Args), NumArgs), Bundles)); +} + +extern "C" LLVMValueRef LLVMRustGetInstrProfIncrementIntrinsic(LLVMModuleRef M) { + return wrap(llvm::Intrinsic::getDeclaration(unwrap(M), + (llvm::Intrinsic::ID)llvm::Intrinsic::instrprof_increment)); +} + +extern "C" LLVMValueRef LLVMRustBuildMemCpy(LLVMBuilderRef B, + LLVMValueRef Dst, unsigned DstAlign, + LLVMValueRef Src, unsigned SrcAlign, + LLVMValueRef Size, bool IsVolatile) { +#if LLVM_VERSION_GE(10, 0) + return wrap(unwrap(B)->CreateMemCpy( + unwrap(Dst), MaybeAlign(DstAlign), + unwrap(Src), MaybeAlign(SrcAlign), + unwrap(Size), IsVolatile)); +#else + return wrap(unwrap(B)->CreateMemCpy( + unwrap(Dst), DstAlign, + unwrap(Src), SrcAlign, + unwrap(Size), IsVolatile)); +#endif +} + +extern "C" LLVMValueRef LLVMRustBuildMemMove(LLVMBuilderRef B, + LLVMValueRef Dst, unsigned DstAlign, + LLVMValueRef Src, unsigned SrcAlign, + LLVMValueRef Size, bool IsVolatile) { +#if LLVM_VERSION_GE(10, 0) + return wrap(unwrap(B)->CreateMemMove( + unwrap(Dst), MaybeAlign(DstAlign), + unwrap(Src), MaybeAlign(SrcAlign), + unwrap(Size), IsVolatile)); +#else + return wrap(unwrap(B)->CreateMemMove( + unwrap(Dst), DstAlign, + unwrap(Src), SrcAlign, + unwrap(Size), IsVolatile)); +#endif +} + +extern "C" LLVMValueRef LLVMRustBuildMemSet(LLVMBuilderRef B, + LLVMValueRef Dst, unsigned DstAlign, + LLVMValueRef Val, + LLVMValueRef Size, bool IsVolatile) { +#if LLVM_VERSION_GE(10, 0) + return wrap(unwrap(B)->CreateMemSet( + unwrap(Dst), unwrap(Val), unwrap(Size), MaybeAlign(DstAlign), IsVolatile)); +#else + return wrap(unwrap(B)->CreateMemSet( + unwrap(Dst), unwrap(Val), unwrap(Size), DstAlign, IsVolatile)); +#endif +} + +extern "C" LLVMValueRef +LLVMRustBuildInvoke(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args, + unsigned NumArgs, LLVMBasicBlockRef Then, + LLVMBasicBlockRef Catch, OperandBundleDef *Bundle, + const char *Name) { + Value *Callee = unwrap(Fn); + FunctionType *FTy = cast<FunctionType>(Callee->getType()->getPointerElementType()); + unsigned Len = Bundle ? 1 : 0; + ArrayRef<OperandBundleDef> Bundles = makeArrayRef(Bundle, Len); + return wrap(unwrap(B)->CreateInvoke(FTy, Callee, unwrap(Then), unwrap(Catch), + makeArrayRef(unwrap(Args), NumArgs), + Bundles, Name)); +} + +extern "C" void LLVMRustPositionBuilderAtStart(LLVMBuilderRef B, + LLVMBasicBlockRef BB) { + auto Point = unwrap(BB)->getFirstInsertionPt(); + unwrap(B)->SetInsertPoint(unwrap(BB), Point); +} + +extern "C" void LLVMRustSetComdat(LLVMModuleRef M, LLVMValueRef V, + const char *Name, size_t NameLen) { + Triple TargetTriple(unwrap(M)->getTargetTriple()); + GlobalObject *GV = unwrap<GlobalObject>(V); + if (!TargetTriple.isOSBinFormatMachO()) { + StringRef NameRef(Name, NameLen); + GV->setComdat(unwrap(M)->getOrInsertComdat(NameRef)); + } +} + +extern "C" void LLVMRustUnsetComdat(LLVMValueRef V) { + GlobalObject *GV = unwrap<GlobalObject>(V); + GV->setComdat(nullptr); +} + +enum class LLVMRustLinkage { + ExternalLinkage = 0, + AvailableExternallyLinkage = 1, + LinkOnceAnyLinkage = 2, + LinkOnceODRLinkage = 3, + WeakAnyLinkage = 4, + WeakODRLinkage = 5, + AppendingLinkage = 6, + InternalLinkage = 7, + PrivateLinkage = 8, + ExternalWeakLinkage = 9, + CommonLinkage = 10, +}; + +static LLVMRustLinkage toRust(LLVMLinkage Linkage) { + switch (Linkage) { + case LLVMExternalLinkage: + return LLVMRustLinkage::ExternalLinkage; + case LLVMAvailableExternallyLinkage: + return LLVMRustLinkage::AvailableExternallyLinkage; + case LLVMLinkOnceAnyLinkage: + return LLVMRustLinkage::LinkOnceAnyLinkage; + case LLVMLinkOnceODRLinkage: + return LLVMRustLinkage::LinkOnceODRLinkage; + case LLVMWeakAnyLinkage: + return LLVMRustLinkage::WeakAnyLinkage; + case LLVMWeakODRLinkage: + return LLVMRustLinkage::WeakODRLinkage; + case LLVMAppendingLinkage: + return LLVMRustLinkage::AppendingLinkage; + case LLVMInternalLinkage: + return LLVMRustLinkage::InternalLinkage; + case LLVMPrivateLinkage: + return LLVMRustLinkage::PrivateLinkage; + case LLVMExternalWeakLinkage: + return LLVMRustLinkage::ExternalWeakLinkage; + case LLVMCommonLinkage: + return LLVMRustLinkage::CommonLinkage; + default: + report_fatal_error("Invalid LLVMRustLinkage value!"); + } +} + +static LLVMLinkage fromRust(LLVMRustLinkage Linkage) { + switch (Linkage) { + case LLVMRustLinkage::ExternalLinkage: + return LLVMExternalLinkage; + case LLVMRustLinkage::AvailableExternallyLinkage: + return LLVMAvailableExternallyLinkage; + case LLVMRustLinkage::LinkOnceAnyLinkage: + return LLVMLinkOnceAnyLinkage; + case LLVMRustLinkage::LinkOnceODRLinkage: + return LLVMLinkOnceODRLinkage; + case LLVMRustLinkage::WeakAnyLinkage: + return LLVMWeakAnyLinkage; + case LLVMRustLinkage::WeakODRLinkage: + return LLVMWeakODRLinkage; + case LLVMRustLinkage::AppendingLinkage: + return LLVMAppendingLinkage; + case LLVMRustLinkage::InternalLinkage: + return LLVMInternalLinkage; + case LLVMRustLinkage::PrivateLinkage: + return LLVMPrivateLinkage; + case LLVMRustLinkage::ExternalWeakLinkage: + return LLVMExternalWeakLinkage; + case LLVMRustLinkage::CommonLinkage: + return LLVMCommonLinkage; + } + report_fatal_error("Invalid LLVMRustLinkage value!"); +} + +extern "C" LLVMRustLinkage LLVMRustGetLinkage(LLVMValueRef V) { + return toRust(LLVMGetLinkage(V)); +} + +extern "C" void LLVMRustSetLinkage(LLVMValueRef V, + LLVMRustLinkage RustLinkage) { + LLVMSetLinkage(V, fromRust(RustLinkage)); +} + +// Returns true if both high and low were successfully set. Fails in case constant wasn’t any of +// the common sizes (1, 8, 16, 32, 64, 128 bits) +extern "C" bool LLVMRustConstInt128Get(LLVMValueRef CV, bool sext, uint64_t *high, uint64_t *low) +{ + auto C = unwrap<llvm::ConstantInt>(CV); + if (C->getBitWidth() > 128) { return false; } + APInt AP; + if (sext) { + AP = C->getValue().sextOrSelf(128); + } else { + AP = C->getValue().zextOrSelf(128); + } + *low = AP.getLoBits(64).getZExtValue(); + *high = AP.getHiBits(64).getZExtValue(); + return true; +} + +enum class LLVMRustVisibility { + Default = 0, + Hidden = 1, + Protected = 2, +}; + +static LLVMRustVisibility toRust(LLVMVisibility Vis) { + switch (Vis) { + case LLVMDefaultVisibility: + return LLVMRustVisibility::Default; + case LLVMHiddenVisibility: + return LLVMRustVisibility::Hidden; + case LLVMProtectedVisibility: + return LLVMRustVisibility::Protected; + } + report_fatal_error("Invalid LLVMRustVisibility value!"); +} + +static LLVMVisibility fromRust(LLVMRustVisibility Vis) { + switch (Vis) { + case LLVMRustVisibility::Default: + return LLVMDefaultVisibility; + case LLVMRustVisibility::Hidden: + return LLVMHiddenVisibility; + case LLVMRustVisibility::Protected: + return LLVMProtectedVisibility; + } + report_fatal_error("Invalid LLVMRustVisibility value!"); +} + +extern "C" LLVMRustVisibility LLVMRustGetVisibility(LLVMValueRef V) { + return toRust(LLVMGetVisibility(V)); +} + +// Oh hey, a binding that makes sense for once? (because LLVM’s own do not) +extern "C" LLVMValueRef LLVMRustBuildIntCast(LLVMBuilderRef B, LLVMValueRef Val, + LLVMTypeRef DestTy, bool isSigned) { + return wrap(unwrap(B)->CreateIntCast(unwrap(Val), unwrap(DestTy), isSigned, "")); +} + +extern "C" void LLVMRustSetVisibility(LLVMValueRef V, + LLVMRustVisibility RustVisibility) { + LLVMSetVisibility(V, fromRust(RustVisibility)); +} + +struct LLVMRustModuleBuffer { + std::string data; +}; + +extern "C" LLVMRustModuleBuffer* +LLVMRustModuleBufferCreate(LLVMModuleRef M) { +#if LLVM_VERSION_GE(10, 0) + auto Ret = std::make_unique<LLVMRustModuleBuffer>(); +#else + auto Ret = llvm::make_unique<LLVMRustModuleBuffer>(); +#endif + { + raw_string_ostream OS(Ret->data); + { + legacy::PassManager PM; + PM.add(createBitcodeWriterPass(OS)); + PM.run(*unwrap(M)); + } + } + return Ret.release(); +} + +extern "C" void +LLVMRustModuleBufferFree(LLVMRustModuleBuffer *Buffer) { + delete Buffer; +} + +extern "C" const void* +LLVMRustModuleBufferPtr(const LLVMRustModuleBuffer *Buffer) { + return Buffer->data.data(); +} + +extern "C" size_t +LLVMRustModuleBufferLen(const LLVMRustModuleBuffer *Buffer) { + return Buffer->data.length(); +} + +extern "C" uint64_t +LLVMRustModuleCost(LLVMModuleRef M) { + auto f = unwrap(M)->functions(); + return std::distance(std::begin(f), std::end(f)); +} + +// Vector reductions: +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceFAdd(LLVMBuilderRef B, LLVMValueRef Acc, LLVMValueRef Src) { + return wrap(unwrap(B)->CreateFAddReduce(unwrap(Acc),unwrap(Src))); +} +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceFMul(LLVMBuilderRef B, LLVMValueRef Acc, LLVMValueRef Src) { + return wrap(unwrap(B)->CreateFMulReduce(unwrap(Acc),unwrap(Src))); +} +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceAdd(LLVMBuilderRef B, LLVMValueRef Src) { + return wrap(unwrap(B)->CreateAddReduce(unwrap(Src))); +} +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceMul(LLVMBuilderRef B, LLVMValueRef Src) { + return wrap(unwrap(B)->CreateMulReduce(unwrap(Src))); +} +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceAnd(LLVMBuilderRef B, LLVMValueRef Src) { + return wrap(unwrap(B)->CreateAndReduce(unwrap(Src))); +} +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceOr(LLVMBuilderRef B, LLVMValueRef Src) { + return wrap(unwrap(B)->CreateOrReduce(unwrap(Src))); +} +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceXor(LLVMBuilderRef B, LLVMValueRef Src) { + return wrap(unwrap(B)->CreateXorReduce(unwrap(Src))); +} +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceMin(LLVMBuilderRef B, LLVMValueRef Src, bool IsSigned) { + return wrap(unwrap(B)->CreateIntMinReduce(unwrap(Src), IsSigned)); +} +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceMax(LLVMBuilderRef B, LLVMValueRef Src, bool IsSigned) { + return wrap(unwrap(B)->CreateIntMaxReduce(unwrap(Src), IsSigned)); +} +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceFMin(LLVMBuilderRef B, LLVMValueRef Src, bool NoNaN) { + return wrap(unwrap(B)->CreateFPMinReduce(unwrap(Src), NoNaN)); +} +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceFMax(LLVMBuilderRef B, LLVMValueRef Src, bool NoNaN) { + return wrap(unwrap(B)->CreateFPMaxReduce(unwrap(Src), NoNaN)); +} + +extern "C" LLVMValueRef +LLVMRustBuildMinNum(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS) { + return wrap(unwrap(B)->CreateMinNum(unwrap(LHS),unwrap(RHS))); +} +extern "C" LLVMValueRef +LLVMRustBuildMaxNum(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS) { + return wrap(unwrap(B)->CreateMaxNum(unwrap(LHS),unwrap(RHS))); +} diff --git a/compiler/rustc_llvm/src/lib.rs b/compiler/rustc_llvm/src/lib.rs new file mode 100644 index 00000000000..9d23397ade0 --- /dev/null +++ b/compiler/rustc_llvm/src/lib.rs @@ -0,0 +1,173 @@ +#![feature(nll)] +#![feature(static_nobundle)] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] + +// NOTE: This crate only exists to allow linking on mingw targets. + +use libc::{c_char, size_t}; +use std::cell::RefCell; +use std::slice; + +#[repr(C)] +pub struct RustString { + pub bytes: RefCell<Vec<u8>>, +} + +impl RustString { + pub fn len(&self) -> usize { + self.bytes.borrow().len() + } +} + +/// Appending to a Rust string -- used by RawRustStringOstream. +#[no_mangle] +#[allow(improper_ctypes_definitions)] +pub unsafe extern "C" fn LLVMRustStringWriteImpl( + sr: &RustString, + ptr: *const c_char, + size: size_t, +) { + let slice = slice::from_raw_parts(ptr as *const u8, size as usize); + + sr.bytes.borrow_mut().extend_from_slice(slice); +} + +/// Initialize targets enabled by the build script via `cfg(llvm_component = "...")`. +/// N.B., this function can't be moved to `rustc_codegen_llvm` because of the `cfg`s. +pub fn initialize_available_targets() { + macro_rules! init_target( + ($cfg:meta, $($method:ident),*) => { { + #[cfg($cfg)] + fn init() { + extern { + $(fn $method();)* + } + unsafe { + $($method();)* + } + } + #[cfg(not($cfg))] + fn init() { } + init(); + } } + ); + init_target!( + llvm_component = "x86", + LLVMInitializeX86TargetInfo, + LLVMInitializeX86Target, + LLVMInitializeX86TargetMC, + LLVMInitializeX86AsmPrinter, + LLVMInitializeX86AsmParser + ); + init_target!( + llvm_component = "arm", + LLVMInitializeARMTargetInfo, + LLVMInitializeARMTarget, + LLVMInitializeARMTargetMC, + LLVMInitializeARMAsmPrinter, + LLVMInitializeARMAsmParser + ); + init_target!( + llvm_component = "aarch64", + LLVMInitializeAArch64TargetInfo, + LLVMInitializeAArch64Target, + LLVMInitializeAArch64TargetMC, + LLVMInitializeAArch64AsmPrinter, + LLVMInitializeAArch64AsmParser + ); + init_target!( + llvm_component = "amdgpu", + LLVMInitializeAMDGPUTargetInfo, + LLVMInitializeAMDGPUTarget, + LLVMInitializeAMDGPUTargetMC, + LLVMInitializeAMDGPUAsmPrinter, + LLVMInitializeAMDGPUAsmParser + ); + init_target!( + llvm_component = "avr", + LLVMInitializeAVRTargetInfo, + LLVMInitializeAVRTarget, + LLVMInitializeAVRTargetMC, + LLVMInitializeAVRAsmPrinter, + LLVMInitializeAVRAsmParser + ); + init_target!( + llvm_component = "mips", + LLVMInitializeMipsTargetInfo, + LLVMInitializeMipsTarget, + LLVMInitializeMipsTargetMC, + LLVMInitializeMipsAsmPrinter, + LLVMInitializeMipsAsmParser + ); + init_target!( + llvm_component = "powerpc", + LLVMInitializePowerPCTargetInfo, + LLVMInitializePowerPCTarget, + LLVMInitializePowerPCTargetMC, + LLVMInitializePowerPCAsmPrinter, + LLVMInitializePowerPCAsmParser + ); + init_target!( + llvm_component = "systemz", + LLVMInitializeSystemZTargetInfo, + LLVMInitializeSystemZTarget, + LLVMInitializeSystemZTargetMC, + LLVMInitializeSystemZAsmPrinter, + LLVMInitializeSystemZAsmParser + ); + init_target!( + llvm_component = "jsbackend", + LLVMInitializeJSBackendTargetInfo, + LLVMInitializeJSBackendTarget, + LLVMInitializeJSBackendTargetMC + ); + init_target!( + llvm_component = "msp430", + LLVMInitializeMSP430TargetInfo, + LLVMInitializeMSP430Target, + LLVMInitializeMSP430TargetMC, + LLVMInitializeMSP430AsmPrinter + ); + init_target!( + all(llvm_component = "msp430", llvm_has_msp430_asm_parser), + LLVMInitializeMSP430AsmParser + ); + init_target!( + llvm_component = "riscv", + LLVMInitializeRISCVTargetInfo, + LLVMInitializeRISCVTarget, + LLVMInitializeRISCVTargetMC, + LLVMInitializeRISCVAsmPrinter, + LLVMInitializeRISCVAsmParser + ); + init_target!( + llvm_component = "sparc", + LLVMInitializeSparcTargetInfo, + LLVMInitializeSparcTarget, + LLVMInitializeSparcTargetMC, + LLVMInitializeSparcAsmPrinter, + LLVMInitializeSparcAsmParser + ); + init_target!( + llvm_component = "nvptx", + LLVMInitializeNVPTXTargetInfo, + LLVMInitializeNVPTXTarget, + LLVMInitializeNVPTXTargetMC, + LLVMInitializeNVPTXAsmPrinter + ); + init_target!( + llvm_component = "hexagon", + LLVMInitializeHexagonTargetInfo, + LLVMInitializeHexagonTarget, + LLVMInitializeHexagonTargetMC, + LLVMInitializeHexagonAsmPrinter, + LLVMInitializeHexagonAsmParser + ); + init_target!( + llvm_component = "webassembly", + LLVMInitializeWebAssemblyTargetInfo, + LLVMInitializeWebAssemblyTarget, + LLVMInitializeWebAssemblyTargetMC, + LLVMInitializeWebAssemblyAsmPrinter + ); +} diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index 7fb3b0e7ea6..5c28839c9b7 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -1,3 +1,4 @@ +#![feature(proc_macro_diagnostic)] #![allow(rustc::default_hash_types)] #![recursion_limit = "128"] @@ -9,6 +10,7 @@ mod hash_stable; mod lift; mod query; mod serialize; +mod session_diagnostic; mod symbols; mod type_foldable; @@ -36,3 +38,14 @@ decl_derive!([MetadataDecodable] => serialize::meta_decodable_derive); decl_derive!([MetadataEncodable] => serialize::meta_encodable_derive); decl_derive!([TypeFoldable, attributes(type_foldable)] => type_foldable::type_foldable_derive); decl_derive!([Lift, attributes(lift)] => lift::lift_derive); +decl_derive!( + [SessionDiagnostic, attributes( + message, + lint, + error, + label, + suggestion, + suggestion_short, + suggestion_hidden, + suggestion_verbose)] => session_diagnostic::session_diagnostic_derive +); diff --git a/compiler/rustc_macros/src/session_diagnostic.rs b/compiler/rustc_macros/src/session_diagnostic.rs new file mode 100644 index 00000000000..610b9155cfc --- /dev/null +++ b/compiler/rustc_macros/src/session_diagnostic.rs @@ -0,0 +1,666 @@ +#![deny(unused_must_use)] +use proc_macro::Diagnostic; +use quote::{format_ident, quote}; +use syn::spanned::Spanned; + +use std::collections::{BTreeSet, HashMap}; + +/// Implements #[derive(SessionDiagnostic)], which allows for errors to be specified as a struct, independent +/// from the actual diagnostics emitting code. +/// ```ignore (pseudo-rust) +/// # extern crate rustc_errors; +/// # use rustc_errors::Applicability; +/// # extern crate rustc_span; +/// # use rustc_span::{symbol::Ident, Span}; +/// # extern crate rust_middle; +/// # use rustc_middle::ty::Ty; +/// #[derive(SessionDiagnostic)] +/// #[code = "E0505"] +/// #[error = "cannot move out of {name} because it is borrowed"] +/// pub struct MoveOutOfBorrowError<'tcx> { +/// pub name: Ident, +/// pub ty: Ty<'tcx>, +/// #[label = "cannot move out of borrow"] +/// pub span: Span, +/// #[label = "`{ty}` first borrowed here"] +/// pub other_span: Span, +/// #[suggestion(message = "consider cloning here", code = "{name}.clone()")] +/// pub opt_sugg: Option<(Span, Applicability)> +/// } +/// ``` +/// Then, later, to emit the error: +/// +/// ```ignore (pseudo-rust) +/// sess.emit_err(MoveOutOfBorrowError { +/// expected, +/// actual, +/// span, +/// other_span, +/// opt_sugg: Some(suggestion, Applicability::MachineApplicable), +/// }); +/// ``` +pub fn session_diagnostic_derive(s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { + // Names for the diagnostic we build and the session we build it from. + let diag = format_ident!("diag"); + let sess = format_ident!("sess"); + + SessionDiagnosticDerive::new(diag, sess, s).into_tokens() +} + +// Checks whether the type name of `ty` matches `name`. +// +// Given some struct at a::b::c::Foo, this will return true for c::Foo, b::c::Foo, or +// a::b::c::Foo. This reasonably allows qualified names to be used in the macro. +fn type_matches_path(ty: &syn::Type, name: &[&str]) -> bool { + if let syn::Type::Path(ty) = ty { + ty.path + .segments + .iter() + .map(|s| s.ident.to_string()) + .rev() + .zip(name.iter().rev()) + .all(|(x, y)| &x.as_str() == y) + } else { + false + } +} + +/// The central struct for constructing the as_error method from an annotated struct. +struct SessionDiagnosticDerive<'a> { + structure: synstructure::Structure<'a>, + builder: SessionDiagnosticDeriveBuilder<'a>, +} + +impl std::convert::From<syn::Error> for SessionDiagnosticDeriveError { + fn from(e: syn::Error) -> Self { + SessionDiagnosticDeriveError::SynError(e) + } +} + +/// Equivalent to rustc:errors::diagnostic::DiagnosticId, except stores the quoted expression to +/// initialise the code with. +enum DiagnosticId { + Error(proc_macro2::TokenStream), + Lint(proc_macro2::TokenStream), +} + +#[derive(Debug)] +enum SessionDiagnosticDeriveError { + SynError(syn::Error), + ErrorHandled, +} + +impl SessionDiagnosticDeriveError { + fn to_compile_error(self) -> proc_macro2::TokenStream { + match self { + SessionDiagnosticDeriveError::SynError(e) => e.to_compile_error(), + SessionDiagnosticDeriveError::ErrorHandled => { + // Return ! to avoid having to create a blank DiagnosticBuilder to return when an + // error has already been emitted to the compiler. + quote! { + unreachable!() + } + } + } + } +} + +fn span_err(span: impl proc_macro::MultiSpan, msg: &str) -> proc_macro::Diagnostic { + Diagnostic::spanned(span, proc_macro::Level::Error, msg) +} + +/// For methods that return a Result<_, SessionDiagnosticDeriveError>: emit a diagnostic on +/// span $span with msg $msg (and, optionally, perform additional decoration using the FnOnce +/// passed in `diag`). Then, return Err(ErrorHandled). +macro_rules! throw_span_err { + ($span:expr, $msg:expr) => {{ throw_span_err!($span, $msg, |diag| diag) }}; + ($span:expr, $msg:expr, $f:expr) => {{ + return Err(_throw_span_err($span, $msg, $f)); + }}; +} + +/// When possible, prefer using throw_span_err! over using this function directly. This only exists +/// as a function to constrain `f` to an impl FnOnce. +fn _throw_span_err( + span: impl proc_macro::MultiSpan, + msg: &str, + f: impl FnOnce(proc_macro::Diagnostic) -> proc_macro::Diagnostic, +) -> SessionDiagnosticDeriveError { + let diag = span_err(span, msg); + f(diag).emit(); + SessionDiagnosticDeriveError::ErrorHandled +} + +impl<'a> SessionDiagnosticDerive<'a> { + fn new(diag: syn::Ident, sess: syn::Ident, structure: synstructure::Structure<'a>) -> Self { + // Build the mapping of field names to fields. This allows attributes to peek values from + // other fields. + let mut fields_map = HashMap::new(); + + // Convenience bindings. + let ast = structure.ast(); + + if let syn::Data::Struct(syn::DataStruct { fields, .. }) = &ast.data { + for field in fields.iter() { + if let Some(ident) = &field.ident { + fields_map.insert(ident.to_string(), field); + } + } + } + + Self { + builder: SessionDiagnosticDeriveBuilder { diag, sess, fields: fields_map, kind: None }, + structure, + } + } + fn into_tokens(self) -> proc_macro2::TokenStream { + let SessionDiagnosticDerive { structure, mut builder } = self; + + let ast = structure.ast(); + let attrs = &ast.attrs; + + let implementation = { + if let syn::Data::Struct(..) = ast.data { + let preamble = { + let preamble = attrs.iter().map(|attr| { + builder + .generate_structure_code(attr) + .unwrap_or_else(|v| v.to_compile_error()) + }); + quote! { + #(#preamble)*; + } + }; + + let body = structure.each(|field_binding| { + let field = field_binding.ast(); + let result = field.attrs.iter().map(|attr| { + builder + .generate_field_code( + attr, + FieldInfo { + vis: &field.vis, + binding: field_binding, + ty: &field.ty, + span: &field.span(), + }, + ) + .unwrap_or_else(|v| v.to_compile_error()) + }); + return quote! { + #(#result);* + }; + }); + // Finally, putting it altogether. + match builder.kind { + None => { + span_err(ast.span().unwrap(), "`code` not specified") + .help("use the [code = \"...\"] attribute to set this diagnostic's error code ") + .emit(); + SessionDiagnosticDeriveError::ErrorHandled.to_compile_error() + } + Some((kind, _)) => match kind { + DiagnosticId::Lint(_lint) => todo!(), + DiagnosticId::Error(code) => { + let (diag, sess) = (&builder.diag, &builder.sess); + quote! { + let mut #diag = #sess.struct_err_with_code("", rustc_errors::DiagnosticId::Error(#code)); + #preamble + match self { + #body + } + #diag + } + } + }, + } + } else { + span_err( + ast.span().unwrap(), + "`#[derive(SessionDiagnostic)]` can only be used on structs", + ) + .emit(); + SessionDiagnosticDeriveError::ErrorHandled.to_compile_error() + } + }; + + let sess = &builder.sess; + structure.gen_impl(quote! { + gen impl<'__session_diagnostic_sess> rustc_session::SessionDiagnostic<'__session_diagnostic_sess> + for @Self + { + fn into_diagnostic( + self, + #sess: &'__session_diagnostic_sess rustc_session::Session + ) -> rustc_errors::DiagnosticBuilder<'__session_diagnostic_sess> { + #implementation + } + } + }) + } +} + +/// Field information passed to the builder. Deliberately omits attrs to discourage the generate_* +/// methods from walking the attributes themselves. +struct FieldInfo<'a> { + vis: &'a syn::Visibility, + binding: &'a synstructure::BindingInfo<'a>, + ty: &'a syn::Type, + span: &'a proc_macro2::Span, +} + +/// Tracks persistent information required for building up the individual calls to diagnostic +/// methods for the final generated method. This is a separate struct to SessionDerive only to be +/// able to destructure and split self.builder and the self.structure up to avoid a double mut +/// borrow later on. +struct SessionDiagnosticDeriveBuilder<'a> { + /// Name of the session parameter that's passed in to the as_error method. + sess: syn::Ident, + + /// Store a map of field name to its corresponding field. This is built on construction of the + /// derive builder. + fields: HashMap<String, &'a syn::Field>, + + /// The identifier to use for the generated DiagnosticBuilder instance. + diag: syn::Ident, + + /// Whether this is a lint or an error. This dictates how the diag will be initialised. Span + /// stores at what Span the kind was first set at (for error reporting purposes, if the kind + /// was multiply specified). + kind: Option<(DiagnosticId, proc_macro2::Span)>, +} + +impl<'a> SessionDiagnosticDeriveBuilder<'a> { + fn generate_structure_code( + &mut self, + attr: &syn::Attribute, + ) -> Result<proc_macro2::TokenStream, SessionDiagnosticDeriveError> { + Ok(match attr.parse_meta()? { + syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => { + let formatted_str = self.build_format(&s.value(), attr.span()); + let name = attr.path.segments.last().unwrap().ident.to_string(); + let name = name.as_str(); + match name { + "message" => { + let diag = &self.diag; + quote! { + #diag.set_primary_message(#formatted_str); + } + } + attr @ "error" | attr @ "lint" => { + self.set_kind_once( + if attr == "error" { + DiagnosticId::Error(formatted_str) + } else if attr == "lint" { + DiagnosticId::Lint(formatted_str) + } else { + unreachable!() + }, + s.span(), + )?; + // This attribute is only allowed to be applied once, and the attribute + // will be set in the initialisation code. + quote! {} + } + other => throw_span_err!( + attr.span().unwrap(), + &format!( + "`#[{} = ...]` is not a valid SessionDiagnostic struct attribute", + other + ) + ), + } + } + _ => todo!("unhandled meta kind"), + }) + } + + #[must_use] + fn set_kind_once( + &mut self, + kind: DiagnosticId, + span: proc_macro2::Span, + ) -> Result<(), SessionDiagnosticDeriveError> { + if self.kind.is_none() { + self.kind = Some((kind, span)); + Ok(()) + } else { + let kind_str = |kind: &DiagnosticId| match kind { + DiagnosticId::Lint(..) => "lint", + DiagnosticId::Error(..) => "error", + }; + + let existing_kind = kind_str(&self.kind.as_ref().unwrap().0); + let this_kind = kind_str(&kind); + + let msg = if this_kind == existing_kind { + format!("`{}` specified multiple times", existing_kind) + } else { + format!("`{}` specified when `{}` was already specified", this_kind, existing_kind) + }; + throw_span_err!(span.unwrap(), &msg); + } + } + + fn generate_field_code( + &mut self, + attr: &syn::Attribute, + info: FieldInfo<'_>, + ) -> Result<proc_macro2::TokenStream, SessionDiagnosticDeriveError> { + let field_binding = &info.binding.binding; + + let option_ty = option_inner_ty(&info.ty); + + let generated_code = self.generate_non_option_field_code( + attr, + FieldInfo { + vis: info.vis, + binding: info.binding, + ty: option_ty.unwrap_or(&info.ty), + span: info.span, + }, + )?; + Ok(if option_ty.is_none() { + quote! { #generated_code } + } else { + quote! { + if let Some(#field_binding) = #field_binding { + #generated_code + } + } + }) + } + + fn generate_non_option_field_code( + &mut self, + attr: &syn::Attribute, + info: FieldInfo<'_>, + ) -> Result<proc_macro2::TokenStream, SessionDiagnosticDeriveError> { + let diag = &self.diag; + let field_binding = &info.binding.binding; + let name = attr.path.segments.last().unwrap().ident.to_string(); + let name = name.as_str(); + // At this point, we need to dispatch based on the attribute key + the + // type. + let meta = attr.parse_meta()?; + Ok(match meta { + syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => { + let formatted_str = self.build_format(&s.value(), attr.span()); + match name { + "message" => { + if type_matches_path(&info.ty, &["rustc_span", "Span"]) { + quote! { + #diag.set_span(*#field_binding); + #diag.set_primary_message(#formatted_str); + } + } else { + throw_span_err!( + attr.span().unwrap(), + "the `#[message = \"...\"]` attribute can only be applied to fields of type Span" + ); + } + } + "label" => { + if type_matches_path(&info.ty, &["rustc_span", "Span"]) { + quote! { + #diag.span_label(*#field_binding, #formatted_str); + } + } else { + throw_span_err!( + attr.span().unwrap(), + "The `#[label = ...]` attribute can only be applied to fields of type Span" + ); + } + } + other => throw_span_err!( + attr.span().unwrap(), + &format!( + "`#[{} = ...]` is not a valid SessionDiagnostic field attribute", + other + ) + ), + } + } + syn::Meta::List(list) => { + match list.path.segments.iter().last().unwrap().ident.to_string().as_str() { + suggestion_kind @ "suggestion" + | suggestion_kind @ "suggestion_short" + | suggestion_kind @ "suggestion_hidden" + | suggestion_kind @ "suggestion_verbose" => { + // For suggest, we need to ensure we are running on a (Span, + // Applicability) pair. + let (span, applicability) = (|| match &info.ty { + ty @ syn::Type::Path(..) + if type_matches_path(ty, &["rustc_span", "Span"]) => + { + let binding = &info.binding.binding; + Ok(( + quote!(*#binding), + quote!(rustc_errors::Applicability::Unspecified), + )) + } + syn::Type::Tuple(tup) => { + let mut span_idx = None; + let mut applicability_idx = None; + for (idx, elem) in tup.elems.iter().enumerate() { + if type_matches_path(elem, &["rustc_span", "Span"]) { + if span_idx.is_none() { + span_idx = Some(syn::Index::from(idx)); + } else { + throw_span_err!( + info.span.clone().unwrap(), + "type of field annotated with `#[suggestion(...)]` contains more than one Span" + ); + } + } else if type_matches_path( + elem, + &["rustc_errors", "Applicability"], + ) { + if applicability_idx.is_none() { + applicability_idx = Some(syn::Index::from(idx)); + } else { + throw_span_err!( + info.span.clone().unwrap(), + "type of field annotated with `#[suggestion(...)]` contains more than one Applicability" + ); + } + } + } + if let Some(span_idx) = span_idx { + let binding = &info.binding.binding; + let span = quote!(#binding.#span_idx); + let applicability = applicability_idx + .map( + |applicability_idx| quote!(#binding.#applicability_idx), + ) + .unwrap_or(quote!( + rustc_errors::Applicability::Unspecified + )); + return Ok((span, applicability)); + } + throw_span_err!( + info.span.clone().unwrap(), + "wrong types for suggestion", + |diag| { + diag.help("#[suggestion(...)] on a tuple field must be applied to fields of type (Span, Applicability)") + } + ); + } + _ => throw_span_err!( + info.span.clone().unwrap(), + "wrong field type for suggestion", + |diag| { + diag.help("#[suggestion(...)] should be applied to fields of type Span or (Span, Applicability)") + } + ), + })()?; + // Now read the key-value pairs. + let mut msg = None; + let mut code = None; + + for arg in list.nested.iter() { + if let syn::NestedMeta::Meta(syn::Meta::NameValue(arg_name_value)) = arg + { + if let syn::MetaNameValue { lit: syn::Lit::Str(s), .. } = + arg_name_value + { + let name = arg_name_value + .path + .segments + .last() + .unwrap() + .ident + .to_string(); + let name = name.as_str(); + let formatted_str = self.build_format(&s.value(), arg.span()); + match name { + "message" => { + msg = Some(formatted_str); + } + "code" => { + code = Some(formatted_str); + } + other => throw_span_err!( + arg.span().unwrap(), + &format!( + "`{}` is not a valid key for `#[suggestion(...)]`", + other + ) + ), + } + } + } + } + let msg = if let Some(msg) = msg { + quote!(#msg.as_str()) + } else { + throw_span_err!( + list.span().unwrap(), + "missing suggestion message", + |diag| { + diag.help("provide a suggestion message using #[suggestion(message = \"...\")]") + } + ); + }; + let code = code.unwrap_or_else(|| quote! { String::new() }); + // Now build it out: + let suggestion_method = format_ident!("span_{}", suggestion_kind); + quote! { + #diag.#suggestion_method(#span, #msg, #code, #applicability); + } + } + other => throw_span_err!( + list.span().unwrap(), + &format!("invalid annotation list `#[{}(...)]`", other) + ), + } + } + _ => panic!("unhandled meta kind"), + }) + } + + /// In the strings in the attributes supplied to this macro, we want callers to be able to + /// reference fields in the format string. Take this, for example: + /// ```ignore (not-usage-example) + /// struct Point { + /// #[error = "Expected a point greater than ({x}, {y})"] + /// x: i32, + /// y: i32, + /// } + /// ``` + /// We want to automatically pick up that {x} refers `self.x` and {y} refers to `self.y`, then + /// generate this call to format!: + /// ```ignore (not-usage-example) + /// format!("Expected a point greater than ({x}, {y})", x = self.x, y = self.y) + /// ``` + /// This function builds the entire call to format!. + fn build_format(&self, input: &String, span: proc_macro2::Span) -> proc_macro2::TokenStream { + // This set is used later to generate the final format string. To keep builds reproducible, + // the iteration order needs to be deterministic, hence why we use a BTreeSet here instead + // of a HashSet. + let mut referenced_fields: BTreeSet<String> = BTreeSet::new(); + + // At this point, we can start parsing the format string. + let mut it = input.chars().peekable(); + // Once the start of a format string has been found, process the format string and spit out + // the referenced fields. Leaves `it` sitting on the closing brace of the format string, so the + // next call to `it.next()` retrieves the next character. + while let Some(c) = it.next() { + if c == '{' && *it.peek().unwrap_or(&'\0') != '{' { + #[must_use] + let mut eat_argument = || -> Option<String> { + let mut result = String::new(); + // Format specifiers look like + // format := '{' [ argument ] [ ':' format_spec ] '}' . + // Therefore, we only need to eat until ':' or '}' to find the argument. + while let Some(c) = it.next() { + result.push(c); + let next = *it.peek().unwrap_or(&'\0'); + if next == '}' { + break; + } else if next == ':' { + // Eat the ':' character. + assert_eq!(it.next().unwrap(), ':'); + break; + } + } + // Eat until (and including) the matching '}' + while it.next()? != '}' { + continue; + } + Some(result) + }; + + if let Some(referenced_field) = eat_argument() { + referenced_fields.insert(referenced_field); + } + } + } + // At this point, `referenced_fields` contains a set of the unique fields that were + // referenced in the format string. Generate the corresponding "x = self.x" format + // string parameters: + let args = referenced_fields.into_iter().map(|field: String| { + let field_ident = format_ident!("{}", field); + let value = if self.fields.contains_key(&field) { + quote! { + &self.#field_ident + } + } else { + // This field doesn't exist. Emit a diagnostic. + Diagnostic::spanned( + span.unwrap(), + proc_macro::Level::Error, + format!("`{}` doesn't refer to a field on this type", field), + ) + .emit(); + quote! { + "{#field}" + } + }; + quote! { + #field_ident = #value + } + }); + quote! { + format!(#input #(,#args)*) + } + } +} + +/// If `ty` is an Option, returns Some(inner type). Else, returns None. +fn option_inner_ty(ty: &syn::Type) -> Option<&syn::Type> { + if type_matches_path(ty, &["std", "option", "Option"]) { + if let syn::Type::Path(ty_path) = ty { + let path = &ty_path.path; + let ty = path.segments.iter().last().unwrap(); + if let syn::PathArguments::AngleBracketed(bracketed) = &ty.arguments { + if bracketed.args.len() == 1 { + if let syn::GenericArgument::Type(ty) = &bracketed.args[0] { + return Some(ty); + } + } + } + } + } + None +} diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 36ff65fc5eb..94abfac19c6 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -18,7 +18,7 @@ use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::utils::NativeLibKind; use rustc_session::{CrateDisambiguator, Session}; -use rustc_span::source_map::{self, Span, Spanned}; +use rustc_span::source_map::{Span, Spanned}; use rustc_span::symbol::Symbol; use rustc_data_structures::sync::Lrc; @@ -421,7 +421,11 @@ impl CStore { span, attrs: attrs.to_vec(), kind: ast::ItemKind::MacroDef(data.get_macro(id.index, sess)), - vis: source_map::respan(span.shrink_to_lo(), ast::VisibilityKind::Inherited), + vis: ast::Visibility { + span: span.shrink_to_lo(), + kind: ast::VisibilityKind::Inherited, + tokens: None, + }, tokens: None, }, data.root.edition, diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index 302a907538c..a5a860a38b3 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -26,7 +26,6 @@ rustc_index = { path = "../rustc_index" } rustc_serialize = { path = "../rustc_serialize" } rustc_ast = { path = "../rustc_ast" } rustc_span = { path = "../rustc_span" } -byteorder = { version = "1.3" } chalk-ir = "0.21.0" smallvec = { version = "1.0", features = ["union", "may_dangle"] } measureme = "0.7.1" diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 505939d56ed..ee1ea816e01 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -345,10 +345,8 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> { /// Reads a *non-ZST* scalar. /// - /// ZSTs can't be read for two reasons: - /// * byte-order cannot work with zero-element buffers; - /// * in order to obtain a `Pointer`, we need to check for ZSTness anyway due to integer - /// pointers being valid for ZSTs. + /// ZSTs can't be read because in order to obtain a `Pointer`, we need to check + /// for ZSTness anyway due to integer pointers being valid for ZSTs. /// /// It is the caller's responsibility to check bounds and alignment beforehand. /// Most likely, you want to call `InterpCx::read_scalar` instead of this method. @@ -397,10 +395,8 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> { /// Writes a *non-ZST* scalar. /// - /// ZSTs can't be read for two reasons: - /// * byte-order cannot work with zero-element buffers; - /// * in order to obtain a `Pointer`, we need to check for ZSTness anyway due to integer - /// pointers being valid for ZSTs. + /// ZSTs can't be read because in order to obtain a `Pointer`, we need to check + /// for ZSTness anyway due to integer pointers being valid for ZSTs. /// /// It is the caller's responsibility to check bounds and alignment beforehand. /// Most likely, you want to call `InterpCx::write_scalar` instead of this method. diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index c7e32dd0708..cbc362d934f 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -98,10 +98,10 @@ mod value; use std::convert::TryFrom; use std::fmt; use std::io; +use std::io::{Read, Write}; use std::num::NonZeroU32; use std::sync::atomic::{AtomicU32, Ordering}; -use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt}; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::{HashMapExt, Lock}; @@ -561,19 +561,33 @@ pub fn write_target_uint( mut target: &mut [u8], data: u128, ) -> Result<(), io::Error> { - let len = target.len(); + // This u128 holds an "any-size uint" (since smaller uints can fits in it) + // So we do not write all bytes of the u128, just the "payload". match endianness { - Endian::Little => target.write_uint128::<LittleEndian>(data, len), - Endian::Big => target.write_uint128::<BigEndian>(data, len), - } + Endian::Little => target.write(&data.to_le_bytes())?, + Endian::Big => target.write(&data.to_be_bytes()[16 - target.len()..])?, + }; + debug_assert!(target.len() == 0); // We should have filled the target buffer. + Ok(()) } #[inline] pub fn read_target_uint(endianness: Endian, mut source: &[u8]) -> Result<u128, io::Error> { - match endianness { - Endian::Little => source.read_uint128::<LittleEndian>(source.len()), - Endian::Big => source.read_uint128::<BigEndian>(source.len()), - } + // This u128 holds an "any-size uint" (since smaller uints can fits in it) + let mut buf = [0u8; std::mem::size_of::<u128>()]; + // So we do not read exactly 16 bytes into the u128, just the "payload". + let uint = match endianness { + Endian::Little => { + source.read(&mut buf)?; + Ok(u128::from_le_bytes(buf)) + } + Endian::Big => { + source.read(&mut buf[16 - source.len()..])?; + Ok(u128::from_be_bytes(buf)) + } + }; + debug_assert!(source.len() == 0); // We should have consumed the source buffer. + uint } //////////////////////////////////////////////////////////////////////////////// diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 1181ba6bbf9..d32a7a4062e 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -186,6 +186,23 @@ pub struct Body<'tcx> { /// FIXME(oli-obk): rewrite the promoted during promotion to eliminate the cell components. pub ignore_interior_mut_in_const_validation: bool, + /// Does this body use generic parameters. This is used for the `ConstEvaluatable` check. + /// + /// Note that this does not actually mean that this body is not computable right now. + /// The repeat count in the following example is polymorphic, but can still be evaluated + /// without knowing anything about the type parameter `T`. + /// + /// ```rust + /// fn test<T>() { + /// let _ = [0; std::mem::size_of::<*mut T>()]; + /// } + /// ``` + /// + /// **WARNING**: Do not change this flags after the MIR was originally created, even if an optimization + /// removed the last mention of all generic params. We do not want to rely on optimizations and + /// potentially allow things like `[u8; std::mem::size_of::<T>() * 0]` due to this. + pub is_polymorphic: bool, + predecessor_cache: PredecessorCache, } @@ -208,7 +225,7 @@ impl<'tcx> Body<'tcx> { local_decls.len() ); - Body { + let mut body = Body { phase: MirPhase::Build, basic_blocks, source_scopes, @@ -224,8 +241,11 @@ impl<'tcx> Body<'tcx> { span, required_consts: Vec::new(), ignore_interior_mut_in_const_validation: false, + is_polymorphic: false, predecessor_cache: PredecessorCache::new(), - } + }; + body.is_polymorphic = body.has_param_types_or_consts(); + body } /// Returns a partially initialized MIR body containing only a list of basic blocks. @@ -234,7 +254,7 @@ impl<'tcx> Body<'tcx> { /// is only useful for testing but cannot be `#[cfg(test)]` because it is used in a different /// crate. pub fn new_cfg_only(basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>) -> Self { - Body { + let mut body = Body { phase: MirPhase::Build, basic_blocks, source_scopes: IndexVec::new(), @@ -250,8 +270,11 @@ impl<'tcx> Body<'tcx> { generator_kind: None, var_debug_info: Vec::new(), ignore_interior_mut_in_const_validation: false, + is_polymorphic: false, predecessor_cache: PredecessorCache::new(), - } + }; + body.is_polymorphic = body.has_param_types_or_consts(); + body } #[inline] @@ -899,6 +922,8 @@ pub enum LocalInfo<'tcx> { User(ClearCrossCrate<BindingForm<'tcx>>), /// A temporary created that references the static with the given `DefId`. StaticRef { def_id: DefId, is_thread_local: bool }, + /// A temporary created that references the const with the given `DefId` + ConstRef { def_id: DefId }, } impl<'tcx> LocalDecl<'tcx> { @@ -1954,6 +1979,15 @@ impl<'tcx> Operand<'tcx> { Operand::Constant(_) => None, } } + + /// Returns the `Constant` that is the target of this `Operand`, or `None` if this `Operand` is a + /// place. + pub fn constant(&self) -> Option<&Constant<'tcx>> { + match self { + Operand::Constant(x) => Some(&**x), + Operand::Copy(_) | Operand::Move(_) => None, + } + } } /////////////////////////////////////////////////////////////////////////// diff --git a/compiler/rustc_middle/src/mir/type_foldable.rs b/compiler/rustc_middle/src/mir/type_foldable.rs index 6bb6abe0289..ad2eae0298c 100644 --- a/compiler/rustc_middle/src/mir/type_foldable.rs +++ b/compiler/rustc_middle/src/mir/type_foldable.rs @@ -175,7 +175,7 @@ impl<'tcx> TypeFoldable<'tcx> for Rvalue<'tcx> { use crate::mir::Rvalue::*; match *self { Use(ref op) => Use(op.fold_with(folder)), - Repeat(ref op, len) => Repeat(op.fold_with(folder), len), + Repeat(ref op, len) => Repeat(op.fold_with(folder), len.fold_with(folder)), ThreadLocalRef(did) => ThreadLocalRef(did.fold_with(folder)), Ref(region, bk, ref place) => { Ref(region.fold_with(folder), bk, place.fold_with(folder)) diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 6515ae31b46..a008bd5f75f 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -1150,8 +1150,6 @@ pub enum NonUseContext { StorageDead, /// User type annotation assertions for NLL. AscribeUserTy, - /// Coverage code region and counter metadata. - Coverage, /// The data of an user variable, for debug info. VarDebugInfo, } diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index ffa6e6f5324..f9cadb3bb2d 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -190,74 +190,6 @@ impl<'tcx> FromIterator<DtorckConstraint<'tcx>> for DtorckConstraint<'tcx> { } } -/// This returns true if the type `ty` is "trivial" for -/// dropck-outlives -- that is, if it doesn't require any types to -/// outlive. This is similar but not *quite* the same as the -/// `needs_drop` test in the compiler already -- that is, for every -/// type T for which this function return true, needs-drop would -/// return `false`. But the reverse does not hold: in particular, -/// `needs_drop` returns false for `PhantomData`, but it is not -/// trivial for dropck-outlives. -/// -/// Note also that `needs_drop` requires a "global" type (i.e., one -/// with erased regions), but this function does not. -pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { - match ty.kind() { - // None of these types have a destructor and hence they do not - // require anything in particular to outlive the dtor's - // execution. - ty::Infer(ty::FreshIntTy(_)) - | ty::Infer(ty::FreshFloatTy(_)) - | ty::Bool - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Never - | ty::FnDef(..) - | ty::FnPtr(_) - | ty::Char - | ty::GeneratorWitness(..) - | ty::RawPtr(_) - | ty::Ref(..) - | ty::Str - | ty::Foreign(..) - | ty::Error(_) => true, - - // [T; N] and [T] have same properties as T. - ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, ty), - - // (T1..Tn) and closures have same properties as T1..Tn -- - // check if *any* of those are trivial. - ty::Tuple(ref tys) => tys.iter().all(|t| trivial_dropck_outlives(tcx, t.expect_ty())), - ty::Closure(_, ref substs) => { - substs.as_closure().upvar_tys().all(|t| trivial_dropck_outlives(tcx, t)) - } - - ty::Adt(def, _) => { - if Some(def.did) == tcx.lang_items().manually_drop() { - // `ManuallyDrop` never has a dtor. - true - } else { - // Other types might. Moreover, PhantomData doesn't - // have a dtor, but it is considered to own its - // content, so it is non-trivial. Unions can have `impl Drop`, - // and hence are non-trivial as well. - false - } - } - - // The following *might* require a destructor: needs deeper inspection. - ty::Dynamic(..) - | ty::Projection(..) - | ty::Param(_) - | ty::Opaque(..) - | ty::Placeholder(..) - | ty::Infer(_) - | ty::Bound(..) - | ty::Generator(..) => false, - } -} - #[derive(Debug, HashStable)] pub struct CandidateStep<'tcx> { pub self_ty: Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>, diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index 86fe3ac3751..9d5b558234b 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -167,7 +167,7 @@ impl<'tcx> TyCtxt<'tcx> { } } - /// Returns a vector containing all impls + /// Returns an iterator containing all impls pub fn all_impls(self, def_id: DefId) -> impl Iterator<Item = DefId> + 'tcx { let TraitImpls { blanket_impls, non_blanket_impls } = self.trait_impls_of(def_id); diff --git a/compiler/rustc_mir/Cargo.toml b/compiler/rustc_mir/Cargo.toml index 6b0412ece7a..0a22bc7d762 100644 --- a/compiler/rustc_mir/Cargo.toml +++ b/compiler/rustc_mir/Cargo.toml @@ -14,6 +14,7 @@ itertools = "0.8" tracing = "0.1" log_settings = "0.1.1" polonius-engine = "0.12.0" +regex = "1" rustc_middle = { path = "../rustc_middle" } rustc_attr = { path = "../rustc_attr" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_mir/src/borrow_check/def_use.rs b/compiler/rustc_mir/src/borrow_check/def_use.rs index 6574e584406..689ec249a2f 100644 --- a/compiler/rustc_mir/src/borrow_check/def_use.rs +++ b/compiler/rustc_mir/src/borrow_check/def_use.rs @@ -72,8 +72,7 @@ pub fn categorize(context: PlaceContext) -> Option<DefUse> { PlaceContext::MutatingUse(MutatingUseContext::Drop) => Some(DefUse::Drop), - // Coverage and debug info are neither def nor use. - PlaceContext::NonUse(NonUseContext::Coverage) | + // Debug info is neither def nor use. PlaceContext::NonUse(NonUseContext::VarDebugInfo) => None, } } diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs index 88ff0271228..3cee32834be 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs @@ -150,8 +150,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { Some(mut descr) => { // Surround descr with `backticks`. descr.reserve(2); - descr.insert_str(0, "`"); - descr.push_str("`"); + descr.insert(0, '`'); + descr.push('`'); descr } None => "value".to_string(), @@ -222,7 +222,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if self.upvars[var_index].by_ref { buf.push_str(&name); } else { - buf.push_str(&format!("*{}", &name)); + buf.push('*'); + buf.push_str(&name); } } else { if autoderef { @@ -234,7 +235,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { &including_downcast, )?; } else { - buf.push_str(&"*"); + buf.push('*'); self.append_place_to_string( PlaceRef { local, projection: proj_base }, buf, @@ -272,7 +273,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { autoderef, &including_downcast, )?; - buf.push_str(&format!(".{}", field_name)); + buf.push('.'); + buf.push_str(&field_name); } } ProjectionElem::Index(index) => { @@ -284,11 +286,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { autoderef, &including_downcast, )?; - buf.push_str("["); + buf.push('['); if self.append_local_to_string(*index, buf).is_err() { - buf.push_str("_"); + buf.push('_'); } - buf.push_str("]"); + buf.push(']'); } ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { autoderef = true; @@ -301,7 +303,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { autoderef, &including_downcast, )?; - buf.push_str(&"[..]"); + buf.push_str("[..]"); } }; } @@ -648,7 +650,7 @@ impl UseSpans { " in closure".to_string() } } - _ => "".to_string(), + _ => String::new(), } } @@ -804,68 +806,51 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { debug!("move_spans: target_temp = {:?}", target_temp); if let Some(Terminator { - kind: TerminatorKind::Call { func, args, fn_span, from_hir_call, .. }, - .. + kind: TerminatorKind::Call { fn_span, from_hir_call, .. }, .. }) = &self.body[location.block].terminator { - let mut method_did = None; - if let Operand::Constant(box Constant { literal: ty::Const { ty, .. }, .. }) = func { - if let ty::FnDef(def_id, _) = *ty.kind() { - debug!("move_spans: fn = {:?}", def_id); - if let Some(ty::AssocItem { fn_has_self_parameter, .. }) = - self.infcx.tcx.opt_associated_item(def_id) - { - if *fn_has_self_parameter { - method_did = Some(def_id); - } - } - } - } + let method_did = if let Some(method_did) = + crate::util::find_self_call(self.infcx.tcx, &self.body, target_temp, location.block) + { + method_did + } else { + return normal_ret; + }; let tcx = self.infcx.tcx; - let method_did = if let Some(did) = method_did { did } else { return normal_ret }; - - if let [Operand::Move(self_place), ..] = **args { - if self_place.as_local() == Some(target_temp) { - let parent = tcx.parent(method_did); - let is_fn_once = parent == tcx.lang_items().fn_once_trait(); - let is_operator = !from_hir_call - && parent.map_or(false, |p| { - tcx.lang_items().group(LangItemGroup::Op).contains(&p) - }); - let fn_call_span = *fn_span; - - let self_arg = tcx.fn_arg_names(method_did)[0]; - - let kind = if is_fn_once { - FnSelfUseKind::FnOnceCall - } else if is_operator { - FnSelfUseKind::Operator { self_arg } - } else { - debug!( - "move_spans: method_did={:?}, fn_call_span={:?}", - method_did, fn_call_span - ); - let implicit_into_iter = matches!( - fn_call_span.desugaring_kind(), - Some(DesugaringKind::ForLoop(ForLoopLoc::IntoIter)) - ); - FnSelfUseKind::Normal { self_arg, implicit_into_iter } - }; - return FnSelfUse { - var_span: stmt.source_info.span, - fn_call_span, - fn_span: self - .infcx - .tcx - .sess - .source_map() - .guess_head_span(self.infcx.tcx.def_span(method_did)), - kind, - }; - } - } + let parent = tcx.parent(method_did); + let is_fn_once = parent == tcx.lang_items().fn_once_trait(); + let is_operator = !from_hir_call + && parent.map_or(false, |p| tcx.lang_items().group(LangItemGroup::Op).contains(&p)); + let fn_call_span = *fn_span; + + let self_arg = tcx.fn_arg_names(method_did)[0]; + + let kind = if is_fn_once { + FnSelfUseKind::FnOnceCall + } else if is_operator { + FnSelfUseKind::Operator { self_arg } + } else { + debug!("move_spans: method_did={:?}, fn_call_span={:?}", method_did, fn_call_span); + let implicit_into_iter = matches!( + fn_call_span.desugaring_kind(), + Some(DesugaringKind::ForLoop(ForLoopLoc::IntoIter)) + ); + FnSelfUseKind::Normal { self_arg, implicit_into_iter } + }; + + return FnSelfUse { + var_span: stmt.source_info.span, + fn_call_span, + fn_span: self + .infcx + .tcx + .sess + .source_map() + .guess_head_span(self.infcx.tcx.def_span(method_did)), + kind, + }; } normal_ret } diff --git a/compiler/rustc_mir/src/borrow_check/region_infer/values.rs b/compiler/rustc_mir/src/borrow_check/region_infer/values.rs index 8a5a600cfdd..f247d07e1f0 100644 --- a/compiler/rustc_mir/src/borrow_check/region_infer/values.rs +++ b/compiler/rustc_mir/src/borrow_check/region_infer/values.rs @@ -417,7 +417,7 @@ crate fn location_set_str( fn region_value_str(elements: impl IntoIterator<Item = RegionElement>) -> String { let mut result = String::new(); - result.push_str("{"); + result.push('{'); // Set to Some(l1, l2) when we have observed all the locations // from l1..=l2 (inclusive) but not yet printed them. This @@ -478,7 +478,7 @@ fn region_value_str(elements: impl IntoIterator<Item = RegionElement>) -> String push_location_range(&mut result, location1, location2); } - result.push_str("}"); + result.push('}'); return result; diff --git a/compiler/rustc_mir/src/dataflow/framework/cursor.rs b/compiler/rustc_mir/src/dataflow/framework/cursor.rs index 4f5930dc3f5..4942bed656c 100644 --- a/compiler/rustc_mir/src/dataflow/framework/cursor.rs +++ b/compiler/rustc_mir/src/dataflow/framework/cursor.rs @@ -4,6 +4,7 @@ use std::borrow::Borrow; use std::cmp::Ordering; use rustc_index::bit_set::BitSet; +use rustc_index::vec::Idx; use rustc_middle::mir::{self, BasicBlock, Location}; use super::{Analysis, Direction, Effect, EffectIndex, Results}; @@ -26,7 +27,7 @@ where { body: &'mir mir::Body<'tcx>, results: R, - state: BitSet<A::Idx>, + state: A::Domain, pos: CursorPosition, @@ -46,17 +47,16 @@ where { /// Returns a new cursor that can inspect `results`. pub fn new(body: &'mir mir::Body<'tcx>, results: R) -> Self { - let bits_per_block = results.borrow().entry_set_for_block(mir::START_BLOCK).domain_size(); - + let bottom_value = results.borrow().analysis.bottom_value(body); ResultsCursor { body, results, - // Initialize to an empty `BitSet` and set `state_needs_reset` to tell the cursor that + // Initialize to the `bottom_value` and set `state_needs_reset` to tell the cursor that // it needs to reset to block entry before the first seek. The cursor position is // immaterial. state_needs_reset: true, - state: BitSet::new_empty(bits_per_block), + state: bottom_value, pos: CursorPosition::block_entry(mir::START_BLOCK), #[cfg(debug_assertions)] @@ -68,23 +68,21 @@ where self.body } - /// Returns the `Analysis` used to generate the underlying results. + /// Returns the underlying `Results`. + pub fn results(&self) -> &Results<'tcx, A> { + &self.results.borrow() + } + + /// Returns the `Analysis` used to generate the underlying `Results`. pub fn analysis(&self) -> &A { &self.results.borrow().analysis } /// Returns the dataflow state at the current location. - pub fn get(&self) -> &BitSet<A::Idx> { + pub fn get(&self) -> &A::Domain { &self.state } - /// Returns `true` if the dataflow state at the current location contains the given element. - /// - /// Shorthand for `self.get().contains(elem)` - pub fn contains(&self, elem: A::Idx) -> bool { - self.state.contains(elem) - } - /// Resets the cursor to hold the entry set for the given basic block. /// /// For forward dataflow analyses, this is the dataflow state prior to the first statement. @@ -94,7 +92,7 @@ where #[cfg(debug_assertions)] assert!(self.reachable_blocks.contains(block)); - self.state.overwrite(&self.results.borrow().entry_set_for_block(block)); + self.state.clone_from(&self.results.borrow().entry_set_for_block(block)); self.pos = CursorPosition::block_entry(block); self.state_needs_reset = false; } @@ -202,12 +200,23 @@ where /// /// This can be used, e.g., to apply the call return effect directly to the cursor without /// creating an extra copy of the dataflow state. - pub fn apply_custom_effect(&mut self, f: impl FnOnce(&A, &mut BitSet<A::Idx>)) { + pub fn apply_custom_effect(&mut self, f: impl FnOnce(&A, &mut A::Domain)) { f(&self.results.borrow().analysis, &mut self.state); self.state_needs_reset = true; } } +impl<'mir, 'tcx, A, R, T> ResultsCursor<'mir, 'tcx, A, R> +where + A: Analysis<'tcx, Domain = BitSet<T>>, + T: Idx, + R: Borrow<Results<'tcx, A>>, +{ + pub fn contains(&self, elem: T) -> bool { + self.get().contains(elem) + } +} + #[derive(Clone, Copy, Debug)] struct CursorPosition { block: BasicBlock, diff --git a/compiler/rustc_mir/src/dataflow/framework/direction.rs b/compiler/rustc_mir/src/dataflow/framework/direction.rs index 1a3b13f0d1f..76c48100371 100644 --- a/compiler/rustc_mir/src/dataflow/framework/direction.rs +++ b/compiler/rustc_mir/src/dataflow/framework/direction.rs @@ -18,7 +18,7 @@ pub trait Direction { /// `effects.start()` must precede or equal `effects.end()` in this direction. fn apply_effects_in_range<A>( analysis: &A, - state: &mut BitSet<A::Idx>, + state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, effects: RangeInclusive<EffectIndex>, @@ -27,7 +27,7 @@ pub trait Direction { fn apply_effects_in_block<A>( analysis: &A, - state: &mut BitSet<A::Idx>, + state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, ) where @@ -55,9 +55,9 @@ pub trait Direction { tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, dead_unwinds: Option<&BitSet<BasicBlock>>, - exit_state: &mut BitSet<A::Idx>, + exit_state: &mut A::Domain, block: (BasicBlock, &'_ mir::BasicBlockData<'tcx>), - propagate: impl FnMut(BasicBlock, &BitSet<A::Idx>), + propagate: impl FnMut(BasicBlock, &A::Domain), ) where A: Analysis<'tcx>; } @@ -72,7 +72,7 @@ impl Direction for Backward { fn apply_effects_in_block<A>( analysis: &A, - state: &mut BitSet<A::Idx>, + state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, ) where @@ -112,7 +112,7 @@ impl Direction for Backward { fn apply_effects_in_range<A>( analysis: &A, - state: &mut BitSet<A::Idx>, + state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, effects: RangeInclusive<EffectIndex>, @@ -224,9 +224,9 @@ impl Direction for Backward { _tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, dead_unwinds: Option<&BitSet<BasicBlock>>, - exit_state: &mut BitSet<A::Idx>, + exit_state: &mut A::Domain, (bb, _bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>), - mut propagate: impl FnMut(BasicBlock, &BitSet<A::Idx>), + mut propagate: impl FnMut(BasicBlock, &A::Domain), ) where A: Analysis<'tcx>, { @@ -281,7 +281,7 @@ impl Direction for Forward { fn apply_effects_in_block<A>( analysis: &A, - state: &mut BitSet<A::Idx>, + state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, ) where @@ -321,7 +321,7 @@ impl Direction for Forward { fn apply_effects_in_range<A>( analysis: &A, - state: &mut BitSet<A::Idx>, + state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, effects: RangeInclusive<EffectIndex>, @@ -428,9 +428,9 @@ impl Direction for Forward { tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, dead_unwinds: Option<&BitSet<BasicBlock>>, - exit_state: &mut BitSet<A::Idx>, + exit_state: &mut A::Domain, (bb, bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>), - mut propagate: impl FnMut(BasicBlock, &BitSet<A::Idx>), + mut propagate: impl FnMut(BasicBlock, &A::Domain), ) where A: Analysis<'tcx>, { @@ -499,7 +499,7 @@ impl Direction for Forward { // MIR building adds discriminants to the `values` array in the same order as they // are yielded by `AdtDef::discriminants`. We rely on this to match each // discriminant in `values` to its corresponding variant in linear time. - let mut tmp = BitSet::new_empty(exit_state.domain_size()); + let mut tmp = analysis.bottom_value(body); let mut discriminants = enum_def.discriminants(tcx); for (value, target) in values.iter().zip(targets.iter().copied()) { let (variant_idx, _) = @@ -508,7 +508,7 @@ impl Direction for Forward { from that of `SwitchInt::values`", ); - tmp.overwrite(exit_state); + tmp.clone_from(exit_state); analysis.apply_discriminant_switch_effect( &mut tmp, bb, diff --git a/compiler/rustc_mir/src/dataflow/framework/engine.rs b/compiler/rustc_mir/src/dataflow/framework/engine.rs index b703852b1de..0b5b437d186 100644 --- a/compiler/rustc_mir/src/dataflow/framework/engine.rs +++ b/compiler/rustc_mir/src/dataflow/framework/engine.rs @@ -1,5 +1,6 @@ //! A solver for dataflow problems. +use std::borrow::BorrowMut; use std::ffi::OsString; use std::fs; use std::path::PathBuf; @@ -9,14 +10,16 @@ use rustc_data_structures::work_queue::WorkQueue; use rustc_graphviz as dot; use rustc_hir::def_id::DefId; use rustc_index::bit_set::BitSet; -use rustc_index::vec::IndexVec; +use rustc_index::vec::{Idx, IndexVec}; use rustc_middle::mir::{self, traversal, BasicBlock}; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::symbol::{sym, Symbol}; +use super::fmt::DebugWithContext; use super::graphviz; use super::{ - visit_results, Analysis, Direction, GenKillAnalysis, GenKillSet, ResultsCursor, ResultsVisitor, + visit_results, Analysis, Direction, GenKill, GenKillAnalysis, GenKillSet, JoinSemiLattice, + ResultsCursor, ResultsVisitor, }; use crate::util::pretty::dump_enabled; @@ -26,7 +29,7 @@ where A: Analysis<'tcx>, { pub analysis: A, - pub(super) entry_sets: IndexVec<BasicBlock, BitSet<A::Idx>>, + pub(super) entry_sets: IndexVec<BasicBlock, A::Domain>, } impl<A> Results<'tcx, A> @@ -39,7 +42,7 @@ where } /// Gets the dataflow state for the given block. - pub fn entry_set_for_block(&self, block: BasicBlock) -> &BitSet<A::Idx> { + pub fn entry_set_for_block(&self, block: BasicBlock) -> &A::Domain { &self.entry_sets[block] } @@ -47,7 +50,7 @@ where &self, body: &'mir mir::Body<'tcx>, blocks: impl IntoIterator<Item = BasicBlock>, - vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = BitSet<A::Idx>>, + vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>, ) { visit_results(body, blocks, self, vis) } @@ -55,7 +58,7 @@ where pub fn visit_reachable_with( &self, body: &'mir mir::Body<'tcx>, - vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = BitSet<A::Idx>>, + vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>, ) { let blocks = mir::traversal::reachable(body); visit_results(body, blocks.map(|(bb, _)| bb), self, vis) @@ -64,7 +67,7 @@ where pub fn visit_in_rpo_with( &self, body: &'mir mir::Body<'tcx>, - vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = BitSet<A::Idx>>, + vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>, ) { let blocks = mir::traversal::reverse_postorder(body); visit_results(body, blocks.map(|(bb, _)| bb), self, vis) @@ -76,21 +79,27 @@ pub struct Engine<'a, 'tcx, A> where A: Analysis<'tcx>, { - bits_per_block: usize, tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, def_id: DefId, dead_unwinds: Option<&'a BitSet<BasicBlock>>, - entry_sets: IndexVec<BasicBlock, BitSet<A::Idx>>, + entry_sets: IndexVec<BasicBlock, A::Domain>, analysis: A, /// Cached, cumulative transfer functions for each block. - trans_for_block: Option<IndexVec<BasicBlock, GenKillSet<A::Idx>>>, + // + // FIXME(ecstaticmorse): This boxed `Fn` trait object is invoked inside a tight loop for + // gen/kill problems on cyclic CFGs. This is not ideal, but it doesn't seem to degrade + // performance in practice. I've tried a few ways to avoid this, but they have downsides. See + // the message for the commit that added this FIXME for more information. + apply_trans_for_block: Option<Box<dyn Fn(BasicBlock, &mut A::Domain)>>, } -impl<A> Engine<'a, 'tcx, A> +impl<A, D, T> Engine<'a, 'tcx, A> where - A: GenKillAnalysis<'tcx>, + A: GenKillAnalysis<'tcx, Idx = T, Domain = D>, + D: Clone + JoinSemiLattice + GenKill<T> + BorrowMut<BitSet<T>>, + T: Idx, { /// Creates a new `Engine` to solve a gen-kill dataflow problem. pub fn new_gen_kill( @@ -109,22 +118,26 @@ where // Otherwise, compute and store the cumulative transfer function for each block. - let bits_per_block = analysis.bits_per_block(body); - let mut trans_for_block = - IndexVec::from_elem(GenKillSet::identity(bits_per_block), body.basic_blocks()); + let identity = GenKillSet::identity(analysis.bottom_value(body).borrow().domain_size()); + let mut trans_for_block = IndexVec::from_elem(identity, body.basic_blocks()); for (block, block_data) in body.basic_blocks().iter_enumerated() { let trans = &mut trans_for_block[block]; A::Direction::gen_kill_effects_in_block(&analysis, trans, block, block_data); } - Self::new(tcx, body, def_id, analysis, Some(trans_for_block)) + let apply_trans = Box::new(move |bb: BasicBlock, state: &mut A::Domain| { + trans_for_block[bb].apply(state.borrow_mut()); + }); + + Self::new(tcx, body, def_id, analysis, Some(apply_trans as Box<_>)) } } -impl<A> Engine<'a, 'tcx, A> +impl<A, D> Engine<'a, 'tcx, A> where - A: Analysis<'tcx>, + A: Analysis<'tcx, Domain = D>, + D: Clone + JoinSemiLattice, { /// Creates a new `Engine` to solve a dataflow problem with an arbitrary transfer /// function. @@ -145,32 +158,24 @@ where body: &'a mir::Body<'tcx>, def_id: DefId, analysis: A, - trans_for_block: Option<IndexVec<BasicBlock, GenKillSet<A::Idx>>>, + apply_trans_for_block: Option<Box<dyn Fn(BasicBlock, &mut A::Domain)>>, ) -> Self { - let bits_per_block = analysis.bits_per_block(body); - - let bottom_value_set = if A::BOTTOM_VALUE { - BitSet::new_filled(bits_per_block) - } else { - BitSet::new_empty(bits_per_block) - }; - - let mut entry_sets = IndexVec::from_elem(bottom_value_set.clone(), body.basic_blocks()); + let bottom_value = analysis.bottom_value(body); + let mut entry_sets = IndexVec::from_elem(bottom_value.clone(), body.basic_blocks()); analysis.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]); - if A::Direction::is_backward() && entry_sets[mir::START_BLOCK] != bottom_value_set { + if A::Direction::is_backward() && entry_sets[mir::START_BLOCK] != bottom_value { bug!("`initialize_start_block` is not yet supported for backward dataflow analyses"); } Engine { analysis, - bits_per_block, tcx, body, def_id, dead_unwinds: None, entry_sets, - trans_for_block, + apply_trans_for_block, } } @@ -185,16 +190,18 @@ where } /// Computes the fixpoint for this dataflow problem and returns it. - pub fn iterate_to_fixpoint(self) -> Results<'tcx, A> { + pub fn iterate_to_fixpoint(self) -> Results<'tcx, A> + where + A::Domain: DebugWithContext<A>, + { let Engine { analysis, - bits_per_block, body, dead_unwinds, def_id, mut entry_sets, tcx, - trans_for_block, + apply_trans_for_block, .. } = self; @@ -213,14 +220,14 @@ where } } - let mut state = BitSet::new_empty(bits_per_block); + let mut state = analysis.bottom_value(body); while let Some(bb) = dirty_queue.pop() { let bb_data = &body[bb]; // Apply the block transfer function, using the cached one if it exists. - state.overwrite(&entry_sets[bb]); - match &trans_for_block { - Some(trans_for_block) => trans_for_block[bb].apply(&mut state), + state.clone_from(&entry_sets[bb]); + match &apply_trans_for_block { + Some(apply) => apply(bb, &mut state), None => A::Direction::apply_effects_in_block(&analysis, &mut state, bb, bb_data), } @@ -231,8 +238,8 @@ where dead_unwinds, &mut state, (bb, bb_data), - |target: BasicBlock, state: &BitSet<A::Idx>| { - let set_changed = analysis.join(&mut entry_sets[target], state); + |target: BasicBlock, state: &A::Domain| { + let set_changed = entry_sets[target].join(state); if set_changed { dirty_queue.insert(target); } @@ -242,7 +249,7 @@ where let results = Results { analysis, entry_sets }; - let res = write_graphviz_results(tcx, def_id, &body, &results, trans_for_block); + let res = write_graphviz_results(tcx, def_id, &body, &results); if let Err(e) = res { warn!("Failed to write graphviz dataflow results: {}", e); } @@ -260,10 +267,10 @@ fn write_graphviz_results<A>( def_id: DefId, body: &mir::Body<'tcx>, results: &Results<'tcx, A>, - block_transfer_functions: Option<IndexVec<BasicBlock, GenKillSet<A::Idx>>>, ) -> std::io::Result<()> where A: Analysis<'tcx>, + A::Domain: DebugWithContext<A>, { let attrs = match RustcMirAttrs::parse(tcx, def_id) { Ok(attrs) => attrs, @@ -290,27 +297,20 @@ where None => return Ok(()), }; - let bits_per_block = results.analysis.bits_per_block(body); - - let mut formatter: Box<dyn graphviz::StateFormatter<'tcx, _>> = match attrs.formatter { - Some(sym::two_phase) => Box::new(graphviz::TwoPhaseDiff::new(bits_per_block)), - Some(sym::gen_kill) => { - if let Some(trans_for_block) = block_transfer_functions { - Box::new(graphviz::BlockTransferFunc::new(body, trans_for_block)) - } else { - Box::new(graphviz::SimpleDiff::new(body, &results)) - } - } - - // Default to the `SimpleDiff` output style. - _ => Box::new(graphviz::SimpleDiff::new(body, &results)), + let style = match attrs.formatter { + Some(sym::two_phase) => graphviz::OutputStyle::BeforeAndAfter, + _ => graphviz::OutputStyle::AfterOnly, }; debug!("printing dataflow results for {:?} to {}", def_id, path.display()); let mut buf = Vec::new(); - let graphviz = graphviz::Formatter::new(body, def_id, results, &mut *formatter); - dot::render_opts(&graphviz, &mut buf, &[dot::RenderOption::Monospace])?; + let graphviz = graphviz::Formatter::new(body, def_id, results, style); + let mut render_opts = vec![dot::RenderOption::Monospace]; + if tcx.sess.opts.debugging_opts.graphviz_dark_mode { + render_opts.push(dot::RenderOption::DarkTheme); + } + dot::render_opts(&graphviz, &mut buf, &render_opts)?; if let Some(parent) = path.parent() { fs::create_dir_all(parent)?; diff --git a/compiler/rustc_mir/src/dataflow/framework/fmt.rs b/compiler/rustc_mir/src/dataflow/framework/fmt.rs new file mode 100644 index 00000000000..0140a750544 --- /dev/null +++ b/compiler/rustc_mir/src/dataflow/framework/fmt.rs @@ -0,0 +1,172 @@ +//! Custom formatting traits used when outputting Graphviz diagrams with the results of a dataflow +//! analysis. + +use rustc_index::bit_set::{BitSet, HybridBitSet}; +use rustc_index::vec::Idx; +use std::fmt; + +/// An extension to `fmt::Debug` for data that can be better printed with some auxiliary data `C`. +pub trait DebugWithContext<C>: Eq + fmt::Debug { + fn fmt_with(&self, _ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self, f) + } + + /// Print the difference between `self` and `old`. + /// + /// This should print nothing if `self == old`. + /// + /// `+` and `-` are typically used to indicate differences. However, these characters are + /// fairly common and may be needed to print a types representation. If using them to indicate + /// a diff, prefix them with the "Unit Separator" control character (␟ U+001F). + fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self == old { + return Ok(()); + } + + write!(f, "\u{001f}+")?; + self.fmt_with(ctxt, f)?; + + if f.alternate() { + write!(f, "\n")?; + } else { + write!(f, "\t")?; + } + + write!(f, "\u{001f}-")?; + self.fmt_with(ctxt, f) + } +} + +/// Implements `fmt::Debug` by deferring to `<T as DebugWithContext<C>>::fmt_with`. +pub struct DebugWithAdapter<'a, T, C> { + pub this: T, + pub ctxt: &'a C, +} + +impl<T, C> fmt::Debug for DebugWithAdapter<'_, T, C> +where + T: DebugWithContext<C>, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.this.fmt_with(self.ctxt, f) + } +} + +/// Implements `fmt::Debug` by deferring to `<T as DebugWithContext<C>>::fmt_diff_with`. +pub struct DebugDiffWithAdapter<'a, T, C> { + pub new: T, + pub old: T, + pub ctxt: &'a C, +} + +impl<T, C> fmt::Debug for DebugDiffWithAdapter<'_, T, C> +where + T: DebugWithContext<C>, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.new.fmt_diff_with(&self.old, self.ctxt, f) + } +} + +// Impls + +impl<T, C> DebugWithContext<C> for BitSet<T> +where + T: Idx + DebugWithContext<C>, +{ + fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_set().entries(self.iter().map(|i| DebugWithAdapter { this: i, ctxt })).finish() + } + + fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let size = self.domain_size(); + assert_eq!(size, old.domain_size()); + + let mut set_in_self = HybridBitSet::new_empty(size); + let mut cleared_in_self = HybridBitSet::new_empty(size); + + for i in (0..size).map(T::new) { + match (self.contains(i), old.contains(i)) { + (true, false) => set_in_self.insert(i), + (false, true) => cleared_in_self.insert(i), + _ => continue, + }; + } + + let mut first = true; + for idx in set_in_self.iter() { + let delim = if first { + "\u{001f}+" + } else if f.alternate() { + "\n\u{001f}+" + } else { + ", " + }; + + write!(f, "{}", delim)?; + idx.fmt_with(ctxt, f)?; + first = false; + } + + if !f.alternate() { + first = true; + if !set_in_self.is_empty() && !cleared_in_self.is_empty() { + write!(f, "\t")?; + } + } + + for idx in cleared_in_self.iter() { + let delim = if first { + "\u{001f}-" + } else if f.alternate() { + "\n\u{001f}-" + } else { + ", " + }; + + write!(f, "{}", delim)?; + idx.fmt_with(ctxt, f)?; + first = false; + } + + Ok(()) + } +} + +impl<T, C> DebugWithContext<C> for &'_ T +where + T: DebugWithContext<C>, +{ + fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (*self).fmt_with(ctxt, f) + } + + fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (*self).fmt_diff_with(*old, ctxt, f) + } +} + +impl<C> DebugWithContext<C> for rustc_middle::mir::Local {} +impl<C> DebugWithContext<C> for crate::dataflow::move_paths::InitIndex {} + +impl<'tcx, C> DebugWithContext<C> for crate::dataflow::move_paths::MovePathIndex +where + C: crate::dataflow::move_paths::HasMoveData<'tcx>, +{ + fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", ctxt.move_data().move_paths[*self]) + } +} + +impl<T, C> DebugWithContext<C> for crate::dataflow::lattice::Dual<T> +where + T: DebugWithContext<C>, +{ + fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (self.0).fmt_with(ctxt, f) + } + + fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (self.0).fmt_diff_with(&old.0, ctxt, f) + } +} diff --git a/compiler/rustc_mir/src/dataflow/framework/graphviz.rs b/compiler/rustc_mir/src/dataflow/framework/graphviz.rs index 896616a2175..179c471cf48 100644 --- a/compiler/rustc_mir/src/dataflow/framework/graphviz.rs +++ b/compiler/rustc_mir/src/dataflow/framework/graphviz.rs @@ -1,26 +1,40 @@ //! A helpful diagram for debugging dataflow problems. -use std::cell::RefCell; +use std::borrow::Cow; use std::{io, ops, str}; +use regex::Regex; use rustc_graphviz as dot; use rustc_hir::def_id::DefId; -use rustc_index::bit_set::{BitSet, HybridBitSet}; -use rustc_index::vec::{Idx, IndexVec}; use rustc_middle::mir::{self, BasicBlock, Body, Location}; -use super::{Analysis, Direction, GenKillSet, Results, ResultsRefCursor}; +use super::fmt::{DebugDiffWithAdapter, DebugWithAdapter, DebugWithContext}; +use super::{Analysis, Direction, Results, ResultsRefCursor, ResultsVisitor}; use crate::util::graphviz_safe_def_name; +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum OutputStyle { + AfterOnly, + BeforeAndAfter, +} + +impl OutputStyle { + fn num_state_columns(&self) -> usize { + match self { + Self::AfterOnly => 1, + Self::BeforeAndAfter => 2, + } + } +} + pub struct Formatter<'a, 'tcx, A> where A: Analysis<'tcx>, { body: &'a Body<'tcx>, def_id: DefId, - - // This must be behind a `RefCell` because `dot::Labeller` takes `&self`. - block_formatter: RefCell<BlockFormatter<'a, 'tcx, A>>, + results: &'a Results<'tcx, A>, + style: OutputStyle, } impl<A> Formatter<'a, 'tcx, A> @@ -31,15 +45,9 @@ where body: &'a Body<'tcx>, def_id: DefId, results: &'a Results<'tcx, A>, - state_formatter: &'a mut dyn StateFormatter<'tcx, A>, + style: OutputStyle, ) -> Self { - let block_formatter = BlockFormatter { - bg: Background::Light, - results: ResultsRefCursor::new(body, results), - state_formatter, - }; - - Formatter { body, def_id, block_formatter: RefCell::new(block_formatter) } + Formatter { body, def_id, results, style } } } @@ -62,6 +70,7 @@ fn dataflow_successors(body: &Body<'tcx>, bb: BasicBlock) -> Vec<CfgEdge> { impl<A> dot::Labeller<'_> for Formatter<'a, 'tcx, A> where A: Analysis<'tcx>, + A::Domain: DebugWithContext<A>, { type Node = BasicBlock; type Edge = CfgEdge; @@ -77,7 +86,13 @@ where fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> { let mut label = Vec::new(); - self.block_formatter.borrow_mut().write_node_label(&mut label, self.body, *block).unwrap(); + let mut fmt = BlockFormatter { + results: ResultsRefCursor::new(self.body, self.results), + style: self.style, + bg: Background::Light, + }; + + fmt.write_node_label(&mut label, self.body, *block).unwrap(); dot::LabelText::html(String::from_utf8(label).unwrap()) } @@ -126,19 +141,16 @@ where { results: ResultsRefCursor<'a, 'a, 'tcx, A>, bg: Background, - state_formatter: &'a mut dyn StateFormatter<'tcx, A>, + style: OutputStyle, } impl<A> BlockFormatter<'a, 'tcx, A> where A: Analysis<'tcx>, + A::Domain: DebugWithContext<A>, { const HEADER_COLOR: &'static str = "#a0a0a0"; - fn num_state_columns(&self) -> usize { - std::cmp::max(1, self.state_formatter.column_names().len()) - } - fn toggle_background(&mut self) -> Background { let bg = self.bg; self.bg = !bg; @@ -187,40 +199,30 @@ where write!(w, r#"<table{fmt}>"#, fmt = table_fmt)?; // A + B: Block header - if self.state_formatter.column_names().is_empty() { - self.write_block_header_simple(w, block)?; - } else { - self.write_block_header_with_state_columns(w, block)?; + match self.style { + OutputStyle::AfterOnly => self.write_block_header_simple(w, block)?, + OutputStyle::BeforeAndAfter => { + self.write_block_header_with_state_columns(w, block, &["BEFORE", "AFTER"])? + } } // C: State at start of block self.bg = Background::Light; self.results.seek_to_block_start(block); - let block_entry_state = self.results.get().clone(); - + let block_start_state = self.results.get().clone(); self.write_row_with_full_state(w, "", "(on start)")?; - // D: Statement transfer functions - for (i, statement) in body[block].statements.iter().enumerate() { - let location = Location { block, statement_index: i }; - let statement_str = format!("{:?}", statement); - self.write_row_for_location(w, &i.to_string(), &statement_str, location)?; - } - - // E: Terminator transfer function - let terminator = body[block].terminator(); - let terminator_loc = body.terminator_loc(block); - let mut terminator_str = String::new(); - terminator.kind.fmt_head(&mut terminator_str).unwrap(); - - self.write_row_for_location(w, "T", &terminator_str, terminator_loc)?; + // D + E: Statement and terminator transfer functions + self.write_statements_and_terminator(w, body, block)?; // F: State at end of block + let terminator = body[block].terminator(); + // Write the full dataflow state immediately after the terminator if it differs from the // state at block entry. self.results.seek_to_block_end(block); - if self.results.get() != &block_entry_state || A::Direction::is_backward() { + if self.results.get() != &block_start_state || A::Direction::is_backward() { let after_terminator_name = match terminator.kind { mir::TerminatorKind::Call { destination: Some(_), .. } => "(on unwind)", _ => "(on end)", @@ -229,8 +231,11 @@ where self.write_row_with_full_state(w, "", after_terminator_name)?; } - // Write any changes caused by terminator-specific effects - let num_state_columns = self.num_state_columns(); + // Write any changes caused by terminator-specific effects. + // + // FIXME: These should really be printed as part of each outgoing edge rather than the node + // for the basic block itself. That way, we could display terminator-specific effects for + // backward dataflow analyses as well as effects for `SwitchInt` terminators. match terminator.kind { mir::TerminatorKind::Call { destination: Some((return_place, _)), @@ -239,44 +244,43 @@ where .. } => { self.write_row(w, "", "(on successful return)", |this, w, fmt| { - write!( - w, - r#"<td balign="left" colspan="{colspan}" {fmt} align="left">"#, - colspan = num_state_columns, - fmt = fmt, - )?; - let state_on_unwind = this.results.get().clone(); this.results.apply_custom_effect(|analysis, state| { analysis.apply_call_return_effect(state, block, func, args, return_place); }); - write_diff(w, this.results.analysis(), &state_on_unwind, this.results.get())?; - write!(w, "</td>") + write!( + w, + r#"<td balign="left" colspan="{colspan}" {fmt} align="left">{diff}</td>"#, + colspan = this.style.num_state_columns(), + fmt = fmt, + diff = diff_pretty( + this.results.get(), + &state_on_unwind, + this.results.analysis() + ), + ) })?; } mir::TerminatorKind::Yield { resume, resume_arg, .. } => { self.write_row(w, "", "(on yield resume)", |this, w, fmt| { - write!( - w, - r#"<td balign="left" colspan="{colspan}" {fmt} align="left">"#, - colspan = num_state_columns, - fmt = fmt, - )?; - let state_on_generator_drop = this.results.get().clone(); this.results.apply_custom_effect(|analysis, state| { analysis.apply_yield_resume_effect(state, resume, resume_arg); }); - write_diff( + write!( w, - this.results.analysis(), - &state_on_generator_drop, - this.results.get(), - )?; - write!(w, "</td>") + r#"<td balign="left" colspan="{colspan}" {fmt} align="left">{diff}</td>"#, + colspan = this.style.num_state_columns(), + fmt = fmt, + diff = diff_pretty( + this.results.get(), + &state_on_generator_drop, + this.results.analysis() + ), + ) })?; } @@ -322,6 +326,7 @@ where &mut self, w: &mut impl io::Write, block: BasicBlock, + state_column_names: &[&str], ) -> io::Result<()> { // +------------------------------------+-------------+ // A | bb4 | STATE | @@ -330,8 +335,6 @@ where // +-+----------------------------------+------+------+ // | | ... | | | - let state_column_names = self.state_formatter.column_names(); - // A write!( w, @@ -357,6 +360,56 @@ where write!(w, "</tr>") } + fn write_statements_and_terminator( + &mut self, + w: &mut impl io::Write, + body: &'a Body<'tcx>, + block: BasicBlock, + ) -> io::Result<()> { + let diffs = StateDiffCollector::run(body, block, self.results.results(), self.style); + + let mut befores = diffs.before.map(|v| v.into_iter()); + let mut afters = diffs.after.into_iter(); + + let next_in_dataflow_order = |it: &mut std::vec::IntoIter<_>| { + if A::Direction::is_forward() { it.next().unwrap() } else { it.next_back().unwrap() } + }; + + for (i, statement) in body[block].statements.iter().enumerate() { + let statement_str = format!("{:?}", statement); + let index_str = format!("{}", i); + + let after = next_in_dataflow_order(&mut afters); + let before = befores.as_mut().map(next_in_dataflow_order); + + self.write_row(w, &index_str, &statement_str, |_this, w, fmt| { + if let Some(before) = before { + write!(w, r#"<td {fmt} align="left">{diff}</td>"#, fmt = fmt, diff = before)?; + } + + write!(w, r#"<td {fmt} align="left">{diff}</td>"#, fmt = fmt, diff = after) + })?; + } + + let after = next_in_dataflow_order(&mut afters); + let before = befores.as_mut().map(next_in_dataflow_order); + + assert!(afters.is_empty()); + assert!(befores.as_ref().map_or(true, ExactSizeIterator::is_empty)); + + let terminator = body[block].terminator(); + let mut terminator_str = String::new(); + terminator.kind.fmt_head(&mut terminator_str).unwrap(); + + self.write_row(w, "T", &terminator_str, |_this, w, fmt| { + if let Some(before) = before { + write!(w, r#"<td {fmt} align="left">{diff}</td>"#, fmt = fmt, diff = before)?; + } + + write!(w, r#"<td {fmt} align="left">{diff}</td>"#, fmt = fmt, diff = after) + }) + } + /// Write a row with the given index and MIR, using the function argument to fill in the /// "STATE" column(s). fn write_row<W: io::Write>( @@ -397,319 +450,169 @@ where let state = this.results.get(); let analysis = this.results.analysis(); + // FIXME: The full state vector can be quite long. It would be nice to split on commas + // and use some text wrapping algorithm. write!( w, - r#"<td colspan="{colspan}" {fmt} align="left">{{"#, - colspan = this.num_state_columns(), + r#"<td colspan="{colspan}" {fmt} align="left">{state}</td>"#, + colspan = this.style.num_state_columns(), fmt = fmt, - )?; - pretty_print_state_elems(w, analysis, state.iter(), ", ", LIMIT_30_ALIGN_1)?; - write!(w, "}}</td>") - }) - } - - fn write_row_for_location( - &mut self, - w: &mut impl io::Write, - i: &str, - mir: &str, - location: Location, - ) -> io::Result<()> { - self.write_row(w, i, mir, |this, w, fmt| { - this.state_formatter.write_state_for_location(w, fmt, &mut this.results, location) + state = format!("{:?}", DebugWithAdapter { this: state, ctxt: analysis }), + ) }) } } -/// Controls what gets printed under the `STATE` header. -pub trait StateFormatter<'tcx, A> +struct StateDiffCollector<'a, 'tcx, A> where A: Analysis<'tcx>, { - /// The columns that will get printed under `STATE`. - fn column_names(&self) -> &[&str]; - - fn write_state_for_location( - &mut self, - w: &mut dyn io::Write, - fmt: &str, - results: &mut ResultsRefCursor<'_, '_, 'tcx, A>, - location: Location, - ) -> io::Result<()>; + analysis: &'a A, + prev_state: A::Domain, + before: Option<Vec<String>>, + after: Vec<String>, } -/// Prints a single column containing the state vector immediately *after* each statement. -pub struct SimpleDiff<'a, 'tcx, A> +impl<A> StateDiffCollector<'a, 'tcx, A> where A: Analysis<'tcx>, + A::Domain: DebugWithContext<A>, { - prev_state: ResultsRefCursor<'a, 'a, 'tcx, A>, -} + fn run( + body: &'a mir::Body<'tcx>, + block: BasicBlock, + results: &'a Results<'tcx, A>, + style: OutputStyle, + ) -> Self { + let mut collector = StateDiffCollector { + analysis: &results.analysis, + prev_state: results.analysis.bottom_value(body), + after: vec![], + before: (style == OutputStyle::BeforeAndAfter).then_some(vec![]), + }; -impl<A> SimpleDiff<'a, 'tcx, A> -where - A: Analysis<'tcx>, -{ - pub fn new(body: &'a Body<'tcx>, results: &'a Results<'tcx, A>) -> Self { - SimpleDiff { prev_state: ResultsRefCursor::new(body, results) } + results.visit_with(body, std::iter::once(block), &mut collector); + collector } } -impl<A> StateFormatter<'tcx, A> for SimpleDiff<'_, 'tcx, A> +impl<A> ResultsVisitor<'a, 'tcx> for StateDiffCollector<'a, 'tcx, A> where A: Analysis<'tcx>, + A::Domain: DebugWithContext<A>, { - fn column_names(&self) -> &[&str] { - &[] - } + type FlowState = A::Domain; - fn write_state_for_location( + fn visit_block_start( &mut self, - mut w: &mut dyn io::Write, - fmt: &str, - results: &mut ResultsRefCursor<'_, '_, 'tcx, A>, - location: Location, - ) -> io::Result<()> { + state: &Self::FlowState, + _block_data: &'mir mir::BasicBlockData<'tcx>, + _block: BasicBlock, + ) { if A::Direction::is_forward() { - if location.statement_index == 0 { - self.prev_state.seek_to_block_start(location.block); - } else { - self.prev_state.seek_after_primary_effect(Location { - statement_index: location.statement_index - 1, - ..location - }); - } - } else { - if location == results.body().terminator_loc(location.block) { - self.prev_state.seek_to_block_end(location.block); - } else { - self.prev_state.seek_after_primary_effect(location.successor_within_block()); - } + self.prev_state.clone_from(state); } - - write!(w, r#"<td {fmt} balign="left" align="left">"#, fmt = fmt)?; - results.seek_after_primary_effect(location); - let curr_state = results.get(); - write_diff(&mut w, results.analysis(), self.prev_state.get(), curr_state)?; - write!(w, "</td>") } -} -/// Prints two state columns, one containing only the "before" effect of each statement and one -/// containing the full effect. -pub struct TwoPhaseDiff<T: Idx> { - prev_state: BitSet<T>, - prev_loc: Location, -} + fn visit_block_end( + &mut self, + state: &Self::FlowState, + _block_data: &'mir mir::BasicBlockData<'tcx>, + _block: BasicBlock, + ) { + if A::Direction::is_backward() { + self.prev_state.clone_from(state); + } + } -impl<T: Idx> TwoPhaseDiff<T> { - pub fn new(bits_per_block: usize) -> Self { - TwoPhaseDiff { prev_state: BitSet::new_empty(bits_per_block), prev_loc: Location::START } + fn visit_statement_before_primary_effect( + &mut self, + state: &Self::FlowState, + _statement: &'mir mir::Statement<'tcx>, + _location: Location, + ) { + if let Some(before) = self.before.as_mut() { + before.push(diff_pretty(state, &self.prev_state, self.analysis)); + self.prev_state.clone_from(state) + } } -} -impl<A> StateFormatter<'tcx, A> for TwoPhaseDiff<A::Idx> -where - A: Analysis<'tcx>, -{ - fn column_names(&self) -> &[&str] { - &["BEFORE", " AFTER"] + fn visit_statement_after_primary_effect( + &mut self, + state: &Self::FlowState, + _statement: &'mir mir::Statement<'tcx>, + _location: Location, + ) { + self.after.push(diff_pretty(state, &self.prev_state, self.analysis)); + self.prev_state.clone_from(state) } - fn write_state_for_location( + fn visit_terminator_before_primary_effect( &mut self, - mut w: &mut dyn io::Write, - fmt: &str, - results: &mut ResultsRefCursor<'_, '_, 'tcx, A>, - location: Location, - ) -> io::Result<()> { - if location.statement_index == 0 { - results.seek_to_block_entry(location.block); - self.prev_state.overwrite(results.get()); - } else { - // Ensure that we are visiting statements in order, so `prev_state` is correct. - assert_eq!(self.prev_loc.successor_within_block(), location); + state: &Self::FlowState, + _terminator: &'mir mir::Terminator<'tcx>, + _location: Location, + ) { + if let Some(before) = self.before.as_mut() { + before.push(diff_pretty(state, &self.prev_state, self.analysis)); + self.prev_state.clone_from(state) } - - self.prev_loc = location; - - // Before - - write!(w, r#"<td {fmt} align="left">"#, fmt = fmt)?; - results.seek_before_primary_effect(location); - let curr_state = results.get(); - write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?; - self.prev_state.overwrite(curr_state); - write!(w, "</td>")?; - - // After - - write!(w, r#"<td {fmt} align="left">"#, fmt = fmt)?; - results.seek_after_primary_effect(location); - let curr_state = results.get(); - write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?; - self.prev_state.overwrite(curr_state); - write!(w, "</td>") } -} - -/// Prints the gen/kill set for the entire block. -pub struct BlockTransferFunc<'a, 'tcx, T: Idx> { - body: &'a mir::Body<'tcx>, - trans_for_block: IndexVec<BasicBlock, GenKillSet<T>>, -} -impl<T: Idx> BlockTransferFunc<'mir, 'tcx, T> { - pub fn new( - body: &'mir mir::Body<'tcx>, - trans_for_block: IndexVec<BasicBlock, GenKillSet<T>>, - ) -> Self { - BlockTransferFunc { body, trans_for_block } + fn visit_terminator_after_primary_effect( + &mut self, + state: &Self::FlowState, + _terminator: &'mir mir::Terminator<'tcx>, + _location: Location, + ) { + self.after.push(diff_pretty(state, &self.prev_state, self.analysis)); + self.prev_state.clone_from(state) } } -impl<A> StateFormatter<'tcx, A> for BlockTransferFunc<'mir, 'tcx, A::Idx> +fn diff_pretty<T, C>(new: T, old: T, ctxt: &C) -> String where - A: Analysis<'tcx>, + T: DebugWithContext<C>, { - fn column_names(&self) -> &[&str] { - &["GEN", "KILL"] + if new == old { + return String::new(); } - fn write_state_for_location( - &mut self, - mut w: &mut dyn io::Write, - fmt: &str, - results: &mut ResultsRefCursor<'_, '_, 'tcx, A>, - location: Location, - ) -> io::Result<()> { - // Only print a single row. - if location.statement_index != 0 { - return Ok(()); - } + let re = Regex::new("\u{001f}([+-])").unwrap(); - let block_trans = &self.trans_for_block[location.block]; - let rowspan = self.body.basic_blocks()[location.block].statements.len(); + let raw_diff = format!("{:#?}", DebugDiffWithAdapter { new, old, ctxt }); - for set in &[&block_trans.gen, &block_trans.kill] { - write!( - w, - r#"<td {fmt} rowspan="{rowspan}" balign="left" align="left">"#, - fmt = fmt, - rowspan = rowspan - )?; + // Replace newlines in the `Debug` output with `<br/>` + let raw_diff = raw_diff.replace('\n', r#"<br align="left"/>"#); - pretty_print_state_elems(&mut w, results.analysis(), set.iter(), BR_LEFT, None)?; - write!(w, "</td>")?; + let mut inside_font_tag = false; + let html_diff = re.replace_all(&raw_diff, |captures: ®ex::Captures<'_>| { + let mut ret = String::new(); + if inside_font_tag { + ret.push_str(r#"</font>"#); } - Ok(()) - } -} - -/// Writes two lines, one containing the added bits and one the removed bits. -fn write_diff<A: Analysis<'tcx>>( - w: &mut impl io::Write, - analysis: &A, - from: &BitSet<A::Idx>, - to: &BitSet<A::Idx>, -) -> io::Result<()> { - assert_eq!(from.domain_size(), to.domain_size()); - let len = from.domain_size(); - - let mut set = HybridBitSet::new_empty(len); - let mut clear = HybridBitSet::new_empty(len); - - // FIXME: Implement a lazy iterator over the symmetric difference of two bitsets. - for i in (0..len).map(A::Idx::new) { - match (from.contains(i), to.contains(i)) { - (false, true) => set.insert(i), - (true, false) => clear.insert(i), - _ => continue, + let tag = match &captures[1] { + "+" => r#"<font color="darkgreen">+"#, + "-" => r#"<font color="red">-"#, + _ => unreachable!(), }; - } - - if !set.is_empty() { - write!(w, r#"<font color="darkgreen">+"#)?; - pretty_print_state_elems(w, analysis, set.iter(), ", ", LIMIT_30_ALIGN_1)?; - write!(w, r#"</font>"#)?; - } - - if !set.is_empty() && !clear.is_empty() { - write!(w, "{}", BR_LEFT)?; - } - - if !clear.is_empty() { - write!(w, r#"<font color="red">-"#)?; - pretty_print_state_elems(w, analysis, clear.iter(), ", ", LIMIT_30_ALIGN_1)?; - write!(w, r#"</font>"#)?; - } - - Ok(()) -} -const BR_LEFT: &str = r#"<br align="left"/>"#; -const BR_LEFT_SPACE: &str = r#"<br align="left"/> "#; + inside_font_tag = true; + ret.push_str(tag); + ret + }); -/// Line break policy that breaks at 40 characters and starts the next line with a single space. -const LIMIT_30_ALIGN_1: Option<LineBreak> = Some(LineBreak { sequence: BR_LEFT_SPACE, limit: 30 }); - -struct LineBreak { - sequence: &'static str, - limit: usize, -} - -/// Formats each `elem` using the pretty printer provided by `analysis` into a list with the given -/// separator (`sep`). -/// -/// Optionally, it will break lines using the given character sequence (usually `<br/>`) and -/// character limit. -fn pretty_print_state_elems<A>( - w: &mut impl io::Write, - analysis: &A, - elems: impl Iterator<Item = A::Idx>, - sep: &str, - line_break: Option<LineBreak>, -) -> io::Result<bool> -where - A: Analysis<'tcx>, -{ - let sep_width = sep.chars().count(); - - let mut buf = Vec::new(); - - let mut first = true; - let mut curr_line_width = 0; - let mut line_break_inserted = false; - - for idx in elems { - buf.clear(); - analysis.pretty_print_idx(&mut buf, idx)?; - let idx_str = - str::from_utf8(&buf).expect("Output of `pretty_print_idx` must be valid UTF-8"); - let escaped = dot::escape_html(idx_str); - let escaped_width = escaped.chars().count(); - - if first { - first = false; - } else { - write!(w, "{}", sep)?; - curr_line_width += sep_width; - - if let Some(line_break) = &line_break { - if curr_line_width + sep_width + escaped_width > line_break.limit { - write!(w, "{}", line_break.sequence)?; - line_break_inserted = true; - curr_line_width = 0; - } - } - } + let mut html_diff = match html_diff { + Cow::Borrowed(_) => return raw_diff, + Cow::Owned(s) => s, + }; - write!(w, "{}", escaped)?; - curr_line_width += escaped_width; + if inside_font_tag { + html_diff.push_str("</font>"); } - Ok(line_break_inserted) + html_diff } /// The background color used for zebra-striping the table. diff --git a/compiler/rustc_mir/src/dataflow/framework/lattice.rs b/compiler/rustc_mir/src/dataflow/framework/lattice.rs new file mode 100644 index 00000000000..e7ef9267db5 --- /dev/null +++ b/compiler/rustc_mir/src/dataflow/framework/lattice.rs @@ -0,0 +1,230 @@ +//! Traits used to represent [lattices] for use as the domain of a dataflow analysis. +//! +//! # Overview +//! +//! The most common lattice is a powerset of some set `S`, ordered by [set inclusion]. The [Hasse +//! diagram] for the powerset of a set with two elements (`X` and `Y`) is shown below. Note that +//! distinct elements at the same height in a Hasse diagram (e.g. `{X}` and `{Y}`) are +//! *incomparable*, not equal. +//! +//! ```text +//! {X, Y} <- top +//! / \ +//! {X} {Y} +//! \ / +//! {} <- bottom +//! +//! ``` +//! +//! The defining characteristic of a lattice—the one that differentiates it from a [partially +//! ordered set][poset]—is the existence of a *unique* least upper and greatest lower bound for +//! every pair of elements. The lattice join operator (`∨`) returns the least upper bound, and the +//! lattice meet operator (`∧`) returns the greatest lower bound. Types that implement one operator +//! but not the other are known as semilattices. Dataflow analysis only uses the join operator and +//! will work with any join-semilattice, but both should be specified when possible. +//! +//! ## `PartialOrd` +//! +//! Given that they represent partially ordered sets, you may be surprised that [`JoinSemiLattice`] +//! and [`MeetSemiLattice`] do not have [`PartialOrd`][std::cmp::PartialOrd] as a supertrait. This +//! is because most standard library types use lexicographic ordering instead of set inclusion for +//! their `PartialOrd` impl. Since we do not actually need to compare lattice elements to run a +//! dataflow analysis, there's no need for a newtype wrapper with a custom `PartialOrd` impl. The +//! only benefit would be the ability to check that the least upper (or greatest lower) bound +//! returned by the lattice join (or meet) operator was in fact greater (or lower) than the inputs. +//! +//! [lattices]: https://en.wikipedia.org/wiki/Lattice_(order) +//! [set inclusion]: https://en.wikipedia.org/wiki/Subset +//! [Hasse diagram]: https://en.wikipedia.org/wiki/Hasse_diagram +//! [poset]: https://en.wikipedia.org/wiki/Partially_ordered_set + +use rustc_index::bit_set::BitSet; +use rustc_index::vec::{Idx, IndexVec}; + +/// A [partially ordered set][poset] that has a [least upper bound][lub] for any pair of elements +/// in the set. +/// +/// [lub]: https://en.wikipedia.org/wiki/Infimum_and_supremum +/// [poset]: https://en.wikipedia.org/wiki/Partially_ordered_set +pub trait JoinSemiLattice: Eq { + /// Computes the least upper bound of two elements, storing the result in `self` and returning + /// `true` if `self` has changed. + /// + /// The lattice join operator is abbreviated as `∨`. + fn join(&mut self, other: &Self) -> bool; +} + +/// A [partially ordered set][poset] that has a [greatest lower bound][glb] for any pair of +/// elements in the set. +/// +/// Dataflow analyses only require that their domains implement [`JoinSemiLattice`], not +/// `MeetSemiLattice`. However, types that will be used as dataflow domains should implement both +/// so that they can be used with [`Dual`]. +/// +/// [glb]: https://en.wikipedia.org/wiki/Infimum_and_supremum +/// [poset]: https://en.wikipedia.org/wiki/Partially_ordered_set +pub trait MeetSemiLattice: Eq { + /// Computes the greatest lower bound of two elements, storing the result in `self` and + /// returning `true` if `self` has changed. + /// + /// The lattice meet operator is abbreviated as `∧`. + fn meet(&mut self, other: &Self) -> bool; +} + +/// A `bool` is a "two-point" lattice with `true` as the top element and `false` as the bottom: +/// +/// ```text +/// true +/// | +/// false +/// ``` +impl JoinSemiLattice for bool { + fn join(&mut self, other: &Self) -> bool { + if let (false, true) = (*self, *other) { + *self = true; + return true; + } + + false + } +} + +impl MeetSemiLattice for bool { + fn meet(&mut self, other: &Self) -> bool { + if let (true, false) = (*self, *other) { + *self = false; + return true; + } + + false + } +} + +/// A tuple (or list) of lattices is itself a lattice whose least upper bound is the concatenation +/// of the least upper bounds of each element of the tuple (or list). +/// +/// In other words: +/// (A₀, A₁, ..., Aₙ) ∨ (B₀, B₁, ..., Bₙ) = (A₀∨B₀, A₁∨B₁, ..., Aₙ∨Bₙ) +impl<I: Idx, T: JoinSemiLattice> JoinSemiLattice for IndexVec<I, T> { + fn join(&mut self, other: &Self) -> bool { + assert_eq!(self.len(), other.len()); + + let mut changed = false; + for (a, b) in self.iter_mut().zip(other.iter()) { + changed |= a.join(b); + } + changed + } +} + +impl<I: Idx, T: MeetSemiLattice> MeetSemiLattice for IndexVec<I, T> { + fn meet(&mut self, other: &Self) -> bool { + assert_eq!(self.len(), other.len()); + + let mut changed = false; + for (a, b) in self.iter_mut().zip(other.iter()) { + changed |= a.meet(b); + } + changed + } +} + +/// A `BitSet` represents the lattice formed by the powerset of all possible values of +/// the index type `T` ordered by inclusion. Equivalently, it is a tuple of "two-point" lattices, +/// one for each possible value of `T`. +impl<T: Idx> JoinSemiLattice for BitSet<T> { + fn join(&mut self, other: &Self) -> bool { + self.union(other) + } +} + +impl<T: Idx> MeetSemiLattice for BitSet<T> { + fn meet(&mut self, other: &Self) -> bool { + self.intersect(other) + } +} + +/// The counterpart of a given semilattice `T` using the [inverse order]. +/// +/// The dual of a join-semilattice is a meet-semilattice and vice versa. For example, the dual of a +/// powerset has the empty set as its top element and the full set as its bottom element and uses +/// set *intersection* as its join operator. +/// +/// [inverse order]: https://en.wikipedia.org/wiki/Duality_(order_theory) +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Dual<T>(pub T); + +impl<T> std::borrow::Borrow<T> for Dual<T> { + fn borrow(&self) -> &T { + &self.0 + } +} + +impl<T> std::borrow::BorrowMut<T> for Dual<T> { + fn borrow_mut(&mut self) -> &mut T { + &mut self.0 + } +} + +impl<T: MeetSemiLattice> JoinSemiLattice for Dual<T> { + fn join(&mut self, other: &Self) -> bool { + self.0.meet(&other.0) + } +} + +impl<T: JoinSemiLattice> MeetSemiLattice for Dual<T> { + fn meet(&mut self, other: &Self) -> bool { + self.0.join(&other.0) + } +} + +/// Extends a type `T` with top and bottom elements to make it a partially ordered set in which no +/// value of `T` is comparable with any other. A flat set has the following [Hasse diagram]: +/// +/// ```text +/// top +/// / / \ \ +/// all possible values of `T` +/// \ \ / / +/// bottom +/// ``` +/// +/// [Hasse diagram]: https://en.wikipedia.org/wiki/Hasse_diagram +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum FlatSet<T> { + Bottom, + Elem(T), + Top, +} + +impl<T: Clone + Eq> JoinSemiLattice for FlatSet<T> { + fn join(&mut self, other: &Self) -> bool { + let result = match (&*self, other) { + (Self::Top, _) | (_, Self::Bottom) => return false, + (Self::Elem(a), Self::Elem(b)) if a == b => return false, + + (Self::Bottom, Self::Elem(x)) => Self::Elem(x.clone()), + + _ => Self::Top, + }; + + *self = result; + true + } +} + +impl<T: Clone + Eq> MeetSemiLattice for FlatSet<T> { + fn meet(&mut self, other: &Self) -> bool { + let result = match (&*self, other) { + (Self::Bottom, _) | (_, Self::Top) => return false, + (Self::Elem(ref a), Self::Elem(ref b)) if a == b => return false, + + (Self::Top, Self::Elem(ref x)) => Self::Elem(x.clone()), + + _ => Self::Bottom, + }; + + *self = result; + true + } +} diff --git a/compiler/rustc_mir/src/dataflow/framework/mod.rs b/compiler/rustc_mir/src/dataflow/framework/mod.rs index a21bbacb467..eefa1395a62 100644 --- a/compiler/rustc_mir/src/dataflow/framework/mod.rs +++ b/compiler/rustc_mir/src/dataflow/framework/mod.rs @@ -30,8 +30,8 @@ //! //! [gen-kill]: https://en.wikipedia.org/wiki/Data-flow_analysis#Bit_vector_problems +use std::borrow::BorrowMut; use std::cmp::Ordering; -use std::io; use rustc_hir::def_id::DefId; use rustc_index::bit_set::{BitSet, HybridBitSet}; @@ -43,69 +43,27 @@ use rustc_target::abi::VariantIdx; mod cursor; mod direction; mod engine; +pub mod fmt; mod graphviz; +pub mod lattice; mod visitor; pub use self::cursor::{ResultsCursor, ResultsRefCursor}; pub use self::direction::{Backward, Direction, Forward}; pub use self::engine::{Engine, Results}; +pub use self::lattice::{JoinSemiLattice, MeetSemiLattice}; pub use self::visitor::{visit_results, ResultsVisitor}; pub use self::visitor::{BorrowckFlowState, BorrowckResults}; -/// Parameterization for the precise form of data flow that is used. -/// -/// `BottomValue` determines whether the initial entry set for each basic block is empty or full. -/// This also determines the semantics of the lattice `join` operator used to merge dataflow -/// results, since dataflow works by starting at the bottom and moving monotonically to a fixed -/// point. -/// -/// This means, for propagation across the graph, that you either want to start at all-zeroes and -/// then use Union as your merge when propagating, or you start at all-ones and then use Intersect -/// as your merge when propagating. -pub trait BottomValue { - /// Specifies the initial value for each bit in the entry set for each basic block. - const BOTTOM_VALUE: bool; - - /// Merges `in_set` into `inout_set`, returning `true` if `inout_set` changed. - /// - /// It is almost certainly wrong to override this, since it automatically applies - /// * `inout_set & in_set` if `BOTTOM_VALUE == true` - /// * `inout_set | in_set` if `BOTTOM_VALUE == false` - /// - /// This means that if a bit is not `BOTTOM_VALUE`, it is propagated into all target blocks. - /// For clarity, the above statement again from a different perspective: - /// A bit in the block's entry set is `!BOTTOM_VALUE` if *any* predecessor block's bit value is - /// `!BOTTOM_VALUE`. - /// - /// There are situations where you want the opposite behaviour: propagate only if *all* - /// predecessor blocks's value is `!BOTTOM_VALUE`. - /// E.g. if you want to know whether a bit is *definitely* set at a specific location. This - /// means that all code paths leading to the location must have set the bit, instead of any - /// code path leading there. - /// - /// If you want this kind of "definitely set" analysis, you need to - /// 1. Invert `BOTTOM_VALUE` - /// 2. Reset the `entry_set` in `start_block_effect` to `!BOTTOM_VALUE` - /// 3. Override `join` to do the opposite from what it's doing now. - #[inline] - fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool { - if !Self::BOTTOM_VALUE { inout_set.union(in_set) } else { inout_set.intersect(in_set) } - } -} - /// Define the domain of a dataflow problem. /// -/// This trait specifies the lattice on which this analysis operates. For now, this must be a -/// powerset of values of type `Idx`. The elements of this lattice are represented with a `BitSet` -/// and referred to as the state vector. -/// -/// This trait also defines the initial value for the dataflow state upon entry to the -/// `START_BLOCK`, as well as some names used to refer to this analysis when debugging. -pub trait AnalysisDomain<'tcx>: BottomValue { - /// The type of the elements in the state vector. - type Idx: Idx; +/// This trait specifies the lattice on which this analysis operates (the domain) as well as its +/// initial value at the entry point of each basic block. +pub trait AnalysisDomain<'tcx> { + /// The type that holds the dataflow state at any given point in the program. + type Domain: Clone + JoinSemiLattice; - /// The direction of this analyis. Either `Forward` or `Backward`. + /// The direction of this analysis. Either `Forward` or `Backward`. type Direction: Direction = Forward; /// A descriptive name for this analysis. Used only for debugging. @@ -114,11 +72,10 @@ pub trait AnalysisDomain<'tcx>: BottomValue { /// suitable as part of a filename. const NAME: &'static str; - /// The size of the state vector. - fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize; + /// The initial value of the dataflow state upon entry to each basic block. + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain; - /// Mutates the entry set of the `START_BLOCK` to contain the initial state for dataflow - /// analysis. + /// Mutates the initial value of the dataflow state upon entry to the `START_BLOCK`. /// /// For backward analyses, initial state besides the bottom value is not yet supported. Trying /// to mutate the initial state will result in a panic. @@ -126,20 +83,30 @@ pub trait AnalysisDomain<'tcx>: BottomValue { // FIXME: For backward dataflow analyses, the initial state should be applied to every basic // block where control flow could exit the MIR body (e.g., those terminated with `return` or // `resume`). It's not obvious how to handle `yield` points in generators, however. - fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>); - - /// Prints an element in the state vector for debugging. - fn pretty_print_idx(&self, w: &mut impl io::Write, idx: Self::Idx) -> io::Result<()> { - write!(w, "{:?}", idx) - } + fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain); } /// A dataflow problem with an arbitrarily complex transfer function. +/// +/// # Convergence +/// +/// When implementing this trait directly (not via [`GenKillAnalysis`]), it's possible to choose a +/// transfer function such that the analysis does not reach fixpoint. To guarantee convergence, +/// your transfer functions must maintain the following invariant: +/// +/// > If the dataflow state **before** some point in the program changes to be greater +/// than the prior state **before** that point, the dataflow state **after** that point must +/// also change to be greater than the prior state **after** that point. +/// +/// This invariant guarantees that the dataflow state at a given point in the program increases +/// monotonically until fixpoint is reached. Note that this monotonicity requirement only applies +/// to the same point in the program at different points in time. The dataflow state at a given +/// point in the program may or may not be greater than the state at any preceding point. pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// Updates the current dataflow state with the effect of evaluating a statement. fn apply_statement_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut Self::Domain, statement: &mir::Statement<'tcx>, location: Location, ); @@ -152,7 +119,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// analyses should not implement this without implementing `apply_statement_effect`. fn apply_before_statement_effect( &self, - _state: &mut BitSet<Self::Idx>, + _state: &mut Self::Domain, _statement: &mir::Statement<'tcx>, _location: Location, ) { @@ -166,7 +133,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// initialized here. fn apply_terminator_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut Self::Domain, terminator: &mir::Terminator<'tcx>, location: Location, ); @@ -179,7 +146,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// analyses should not implement this without implementing `apply_terminator_effect`. fn apply_before_terminator_effect( &self, - _state: &mut BitSet<Self::Idx>, + _state: &mut Self::Domain, _terminator: &mir::Terminator<'tcx>, _location: Location, ) { @@ -192,7 +159,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// edges. fn apply_call_return_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut Self::Domain, block: BasicBlock, func: &mir::Operand<'tcx>, args: &[mir::Operand<'tcx>], @@ -207,7 +174,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// By default, no effects happen. fn apply_yield_resume_effect( &self, - _state: &mut BitSet<Self::Idx>, + _state: &mut Self::Domain, _resume_block: BasicBlock, _resume_place: mir::Place<'tcx>, ) { @@ -222,7 +189,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// FIXME: This class of effects is not supported for backward dataflow analyses. fn apply_discriminant_switch_effect( &self, - _state: &mut BitSet<Self::Idx>, + _state: &mut Self::Domain, _block: BasicBlock, _enum_place: mir::Place<'tcx>, _adt: &ty::AdtDef, @@ -264,6 +231,8 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// /// `Analysis` is automatically implemented for all implementers of `GenKillAnalysis`. pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> { + type Idx: Idx; + /// See `Analysis::apply_statement_effect`. fn statement_effect( &self, @@ -332,10 +301,11 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> { impl<A> Analysis<'tcx> for A where A: GenKillAnalysis<'tcx>, + A::Domain: GenKill<A::Idx> + BorrowMut<BitSet<A::Idx>>, { fn apply_statement_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut A::Domain, statement: &mir::Statement<'tcx>, location: Location, ) { @@ -344,7 +314,7 @@ where fn apply_before_statement_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut A::Domain, statement: &mir::Statement<'tcx>, location: Location, ) { @@ -353,7 +323,7 @@ where fn apply_terminator_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut A::Domain, terminator: &mir::Terminator<'tcx>, location: Location, ) { @@ -362,7 +332,7 @@ where fn apply_before_terminator_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut A::Domain, terminator: &mir::Terminator<'tcx>, location: Location, ) { @@ -371,7 +341,7 @@ where fn apply_call_return_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut A::Domain, block: BasicBlock, func: &mir::Operand<'tcx>, args: &[mir::Operand<'tcx>], @@ -382,7 +352,7 @@ where fn apply_yield_resume_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut A::Domain, resume_block: BasicBlock, resume_place: mir::Place<'tcx>, ) { @@ -391,7 +361,7 @@ where fn apply_discriminant_switch_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut A::Domain, block: BasicBlock, enum_place: mir::Place<'tcx>, adt: &ty::AdtDef, @@ -450,7 +420,7 @@ pub trait GenKill<T> { /// applied multiple times efficiently. When there are multiple calls to `gen` and/or `kill` for /// the same element, the most recent one takes precedence. #[derive(Clone)] -pub struct GenKillSet<T: Idx> { +pub struct GenKillSet<T> { gen: HybridBitSet<T>, kill: HybridBitSet<T>, } @@ -464,7 +434,6 @@ impl<T: Idx> GenKillSet<T> { } } - /// Applies this transfer function to the given state vector. pub fn apply(&self, state: &mut BitSet<T>) { state.union(&self.gen); state.subtract(&self.kill); @@ -493,6 +462,16 @@ impl<T: Idx> GenKill<T> for BitSet<T> { } } +impl<T: Idx> GenKill<T> for lattice::Dual<BitSet<T>> { + fn gen(&mut self, elem: T) { + self.0.insert(elem); + } + + fn kill(&mut self, elem: T) { + self.0.remove(elem); + } +} + // NOTE: DO NOT CHANGE VARIANT ORDER. The derived `Ord` impls rely on the current order. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum Effect { diff --git a/compiler/rustc_mir/src/dataflow/framework/tests.rs b/compiler/rustc_mir/src/dataflow/framework/tests.rs index 9349f5133a5..a5989121679 100644 --- a/compiler/rustc_mir/src/dataflow/framework/tests.rs +++ b/compiler/rustc_mir/src/dataflow/framework/tests.rs @@ -9,7 +9,6 @@ use rustc_middle::ty; use rustc_span::DUMMY_SP; use super::*; -use crate::dataflow::BottomValue; /// Creates a `mir::Body` with a few disconnected basic blocks. /// @@ -92,13 +91,13 @@ impl<D: Direction> MockAnalysis<'tcx, D> { /// The entry set for each `BasicBlock` is the ID of that block offset by a fixed amount to /// avoid colliding with the statement/terminator effects. fn mock_entry_set(&self, bb: BasicBlock) -> BitSet<usize> { - let mut ret = BitSet::new_empty(self.bits_per_block(self.body)); + let mut ret = self.bottom_value(self.body); ret.insert(Self::BASIC_BLOCK_OFFSET + bb.index()); ret } fn mock_entry_sets(&self) -> IndexVec<BasicBlock, BitSet<usize>> { - let empty = BitSet::new_empty(self.bits_per_block(self.body)); + let empty = self.bottom_value(self.body); let mut ret = IndexVec::from_elem(empty, &self.body.basic_blocks()); for (bb, _) in self.body.basic_blocks().iter_enumerated() { @@ -130,7 +129,7 @@ impl<D: Direction> MockAnalysis<'tcx, D> { /// would be `[102, 0, 1, 2, 3, 4]`. fn expected_state_at_target(&self, target: SeekTarget) -> BitSet<usize> { let block = target.block(); - let mut ret = BitSet::new_empty(self.bits_per_block(self.body)); + let mut ret = self.bottom_value(self.body); ret.insert(Self::BASIC_BLOCK_OFFSET + block.index()); let target = match target { @@ -161,21 +160,17 @@ impl<D: Direction> MockAnalysis<'tcx, D> { } } -impl<D: Direction> BottomValue for MockAnalysis<'tcx, D> { - const BOTTOM_VALUE: bool = false; -} - impl<D: Direction> AnalysisDomain<'tcx> for MockAnalysis<'tcx, D> { - type Idx = usize; + type Domain = BitSet<usize>; type Direction = D; const NAME: &'static str = "mock"; - fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { - Self::BASIC_BLOCK_OFFSET + body.basic_blocks().len() + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { + BitSet::new_empty(Self::BASIC_BLOCK_OFFSET + body.basic_blocks().len()) } - fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet<Self::Idx>) { + fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { unimplemented!("This is never called since `MockAnalysis` is never iterated to fixpoint"); } } @@ -183,7 +178,7 @@ impl<D: Direction> AnalysisDomain<'tcx> for MockAnalysis<'tcx, D> { impl<D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { fn apply_statement_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut Self::Domain, _statement: &mir::Statement<'tcx>, location: Location, ) { @@ -193,7 +188,7 @@ impl<D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { fn apply_before_statement_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut Self::Domain, _statement: &mir::Statement<'tcx>, location: Location, ) { @@ -203,7 +198,7 @@ impl<D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { fn apply_terminator_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut Self::Domain, _terminator: &mir::Terminator<'tcx>, location: Location, ) { @@ -213,7 +208,7 @@ impl<D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { fn apply_before_terminator_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut Self::Domain, _terminator: &mir::Terminator<'tcx>, location: Location, ) { @@ -223,7 +218,7 @@ impl<D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { fn apply_call_return_effect( &self, - _state: &mut BitSet<Self::Idx>, + _state: &mut Self::Domain, _block: BasicBlock, _func: &mir::Operand<'tcx>, _args: &[mir::Operand<'tcx>], diff --git a/compiler/rustc_mir/src/dataflow/framework/visitor.rs b/compiler/rustc_mir/src/dataflow/framework/visitor.rs index 257f3cb9a6d..82eb734ed06 100644 --- a/compiler/rustc_mir/src/dataflow/framework/visitor.rs +++ b/compiler/rustc_mir/src/dataflow/framework/visitor.rs @@ -1,4 +1,3 @@ -use rustc_index::bit_set::BitSet; use rustc_middle::mir::{self, BasicBlock, Location}; use super::{Analysis, Direction, Results}; @@ -139,16 +138,16 @@ impl<'tcx, A> ResultsVisitable<'tcx> for Results<'tcx, A> where A: Analysis<'tcx>, { - type FlowState = BitSet<A::Idx>; + type FlowState = A::Domain; type Direction = A::Direction; fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState { - BitSet::new_empty(self.analysis.bits_per_block(body)) + self.analysis.bottom_value(body) } fn reset_to_block_entry(&self, state: &mut Self::FlowState, block: BasicBlock) { - state.overwrite(&self.entry_set_for_block(block)); + state.clone_from(&self.entry_set_for_block(block)); } fn reconstruct_before_statement_effect( @@ -217,11 +216,11 @@ macro_rules! impl_visitable { $( $A: Analysis<'tcx, Direction = D>, )* { type Direction = D; - type FlowState = $T<$( BitSet<$A::Idx> ),*>; + type FlowState = $T<$( $A::Domain ),*>; fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState { $T { - $( $field: BitSet::new_empty(self.$field.analysis.bits_per_block(body)) ),* + $( $field: self.$field.analysis.bottom_value(body) ),* } } @@ -230,7 +229,7 @@ macro_rules! impl_visitable { state: &mut Self::FlowState, block: BasicBlock, ) { - $( state.$field.overwrite(&self.$field.entry_set_for_block(block)); )* + $( state.$field.clone_from(&self.$field.entry_set_for_block(block)); )* } fn reconstruct_before_statement_effect( diff --git a/compiler/rustc_mir/src/dataflow/impls/borrowed_locals.rs b/compiler/rustc_mir/src/dataflow/impls/borrowed_locals.rs index a3fc51cad65..65e04ed6831 100644 --- a/compiler/rustc_mir/src/dataflow/impls/borrowed_locals.rs +++ b/compiler/rustc_mir/src/dataflow/impls/borrowed_locals.rs @@ -82,15 +82,15 @@ impl<K> AnalysisDomain<'tcx> for MaybeBorrowedLocals<K> where K: BorrowAnalysisKind<'tcx>, { - type Idx = Local; - + type Domain = BitSet<Local>; const NAME: &'static str = K::ANALYSIS_NAME; - fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { - body.local_decls().len() + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { + // bottom = unborrowed + BitSet::new_empty(body.local_decls().len()) } - fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet<Self::Idx>) { + fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { // No locals are aliased on function entry } } @@ -99,6 +99,8 @@ impl<K> GenKillAnalysis<'tcx> for MaybeBorrowedLocals<K> where K: BorrowAnalysisKind<'tcx>, { + type Idx = Local; + fn statement_effect( &self, trans: &mut impl GenKill<Self::Idx>, @@ -128,11 +130,6 @@ where } } -impl<K> BottomValue for MaybeBorrowedLocals<K> { - // bottom = unborrowed - const BOTTOM_VALUE: bool = false; -} - /// A `Visitor` that defines the transfer function for `MaybeBorrowedLocals`. struct TransferFunction<'a, T, K> { trans: &'a mut T, diff --git a/compiler/rustc_mir/src/dataflow/impls/borrows.rs b/compiler/rustc_mir/src/dataflow/impls/borrows.rs index aeb7ffe3e3b..0be13b6ba81 100644 --- a/compiler/rustc_mir/src/dataflow/impls/borrows.rs +++ b/compiler/rustc_mir/src/dataflow/impls/borrows.rs @@ -8,9 +8,9 @@ use rustc_index::bit_set::BitSet; use crate::borrow_check::{ places_conflict, BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext, ToRegionVid, }; -use crate::dataflow::BottomValue; -use crate::dataflow::{self, GenKill}; +use crate::dataflow::{self, fmt::DebugWithContext, GenKill}; +use std::fmt; use std::rc::Rc; rustc_index::newtype_index! { @@ -227,25 +227,24 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> { } impl<'tcx> dataflow::AnalysisDomain<'tcx> for Borrows<'_, 'tcx> { - type Idx = BorrowIndex; + type Domain = BitSet<BorrowIndex>; const NAME: &'static str = "borrows"; - fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize { - self.borrow_set.len() * 2 + fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { + // bottom = nothing is reserved or activated yet; + BitSet::new_empty(self.borrow_set.len() * 2) } - fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet<Self::Idx>) { + fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { // no borrows of code region_scopes have been taken prior to // function execution, so this method has no effect. } - - fn pretty_print_idx(&self, w: &mut impl std::io::Write, idx: Self::Idx) -> std::io::Result<()> { - write!(w, "{:?}", self.location(idx)) - } } impl<'tcx> dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { + type Idx = BorrowIndex; + fn before_statement_effect( &self, trans: &mut impl GenKill<Self::Idx>, @@ -344,7 +343,8 @@ impl<'tcx> dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { } } -impl<'a, 'tcx> BottomValue for Borrows<'a, 'tcx> { - /// bottom = nothing is reserved or activated yet; - const BOTTOM_VALUE: bool = false; +impl DebugWithContext<Borrows<'_, '_>> for BorrowIndex { + fn fmt_with(&self, ctxt: &Borrows<'_, '_>, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", ctxt.location(*self)) + } } diff --git a/compiler/rustc_mir/src/dataflow/impls/init_locals.rs b/compiler/rustc_mir/src/dataflow/impls/init_locals.rs index 0e7cd1bb0e4..bb7292cd033 100644 --- a/compiler/rustc_mir/src/dataflow/impls/init_locals.rs +++ b/compiler/rustc_mir/src/dataflow/impls/init_locals.rs @@ -2,7 +2,7 @@ //! //! A local will be maybe initialized if *any* projections of that local might be initialized. -use crate::dataflow::{self, BottomValue, GenKill}; +use crate::dataflow::{self, GenKill}; use rustc_index::bit_set::BitSet; use rustc_middle::mir::visit::{PlaceContext, Visitor}; @@ -10,21 +10,17 @@ use rustc_middle::mir::{self, BasicBlock, Local, Location}; pub struct MaybeInitializedLocals; -impl BottomValue for MaybeInitializedLocals { - /// bottom = uninit - const BOTTOM_VALUE: bool = false; -} - impl dataflow::AnalysisDomain<'tcx> for MaybeInitializedLocals { - type Idx = Local; + type Domain = BitSet<Local>; const NAME: &'static str = "maybe_init_locals"; - fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { - body.local_decls.len() + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { + // bottom = uninit + BitSet::new_empty(body.local_decls.len()) } - fn initialize_start_block(&self, body: &mir::Body<'tcx>, entry_set: &mut BitSet<Self::Idx>) { + fn initialize_start_block(&self, body: &mir::Body<'tcx>, entry_set: &mut Self::Domain) { // Function arguments are initialized to begin with. for arg in body.args_iter() { entry_set.insert(arg); @@ -33,6 +29,8 @@ impl dataflow::AnalysisDomain<'tcx> for MaybeInitializedLocals { } impl dataflow::GenKillAnalysis<'tcx> for MaybeInitializedLocals { + type Idx = Local; + fn statement_effect( &self, trans: &mut impl GenKill<Self::Idx>, @@ -99,7 +97,6 @@ where PlaceContext::NonUse( NonUseContext::StorageLive | NonUseContext::AscribeUserTy - | NonUseContext::Coverage | NonUseContext::VarDebugInfo, ) | PlaceContext::NonMutatingUse( diff --git a/compiler/rustc_mir/src/dataflow/impls/liveness.rs b/compiler/rustc_mir/src/dataflow/impls/liveness.rs index 784b0bd9293..b0da28156d1 100644 --- a/compiler/rustc_mir/src/dataflow/impls/liveness.rs +++ b/compiler/rustc_mir/src/dataflow/impls/liveness.rs @@ -2,7 +2,7 @@ use rustc_index::bit_set::BitSet; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{self, Local, Location}; -use crate::dataflow::{AnalysisDomain, Backward, BottomValue, GenKill, GenKillAnalysis}; +use crate::dataflow::{AnalysisDomain, Backward, GenKill, GenKillAnalysis}; /// A [live-variable dataflow analysis][liveness]. /// @@ -22,27 +22,25 @@ impl MaybeLiveLocals { } } -impl BottomValue for MaybeLiveLocals { - // bottom = not live - const BOTTOM_VALUE: bool = false; -} - impl AnalysisDomain<'tcx> for MaybeLiveLocals { - type Idx = Local; + type Domain = BitSet<Local>; type Direction = Backward; const NAME: &'static str = "liveness"; - fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { - body.local_decls.len() + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { + // bottom = not live + BitSet::new_empty(body.local_decls.len()) } - fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet<Self::Idx>) { + fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { // No variables are live until we observe a use } } impl GenKillAnalysis<'tcx> for MaybeLiveLocals { + type Idx = Local; + fn statement_effect( &self, trans: &mut impl GenKill<Self::Idx>, diff --git a/compiler/rustc_mir/src/dataflow/impls/mod.rs b/compiler/rustc_mir/src/dataflow/impls/mod.rs index 8975faec487..c42d5867856 100644 --- a/compiler/rustc_mir/src/dataflow/impls/mod.rs +++ b/compiler/rustc_mir/src/dataflow/impls/mod.rs @@ -13,7 +13,7 @@ use super::MoveDataParamEnv; use crate::util::elaborate_drops::DropFlagState; use super::move_paths::{HasMoveData, InitIndex, InitKind, MoveData, MovePathIndex}; -use super::{AnalysisDomain, BottomValue, GenKill, GenKillAnalysis}; +use super::{lattice, AnalysisDomain, GenKill, GenKillAnalysis}; use super::drop_flag_effects_for_function_entry; use super::drop_flag_effects_for_location; @@ -290,27 +290,25 @@ impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> { } impl<'tcx> AnalysisDomain<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { - type Idx = MovePathIndex; - + type Domain = BitSet<MovePathIndex>; const NAME: &'static str = "maybe_init"; - fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize { - self.move_data().move_paths.len() + fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { + // bottom = uninitialized + BitSet::new_empty(self.move_data().move_paths.len()) } - fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) { + fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) { drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| { assert!(s == DropFlagState::Present); state.insert(path); }); } - - fn pretty_print_idx(&self, w: &mut impl std::io::Write, mpi: Self::Idx) -> std::io::Result<()> { - write!(w, "{}", self.move_data().move_paths[mpi]) - } } impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { + type Idx = MovePathIndex; + fn statement_effect( &self, trans: &mut impl GenKill<Self::Idx>, @@ -376,18 +374,18 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { } impl<'tcx> AnalysisDomain<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { - type Idx = MovePathIndex; + type Domain = BitSet<MovePathIndex>; const NAME: &'static str = "maybe_uninit"; - fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize { - self.move_data().move_paths.len() + fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { + // bottom = initialized (start_block_effect counters this at outset) + BitSet::new_empty(self.move_data().move_paths.len()) } // sets on_entry bits for Arg places - fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) { + fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) { // set all bits to 1 (uninit) before gathering counterevidence - assert!(self.bits_per_block(body) == state.domain_size()); state.insert_all(); drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| { @@ -395,13 +393,11 @@ impl<'tcx> AnalysisDomain<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { state.remove(path); }); } - - fn pretty_print_idx(&self, w: &mut impl std::io::Write, mpi: Self::Idx) -> std::io::Result<()> { - write!(w, "{}", self.move_data().move_paths[mpi]) - } } impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { + type Idx = MovePathIndex; + fn statement_effect( &self, trans: &mut impl GenKill<Self::Idx>, @@ -471,30 +467,30 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { } impl<'a, 'tcx> AnalysisDomain<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> { - type Idx = MovePathIndex; + /// Use set intersection as the join operator. + type Domain = lattice::Dual<BitSet<MovePathIndex>>; const NAME: &'static str = "definite_init"; - fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize { - self.move_data().move_paths.len() + fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { + // bottom = initialized (start_block_effect counters this at outset) + lattice::Dual(BitSet::new_filled(self.move_data().move_paths.len())) } // sets on_entry bits for Arg places - fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) { - state.clear(); + fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) { + state.0.clear(); drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| { assert!(s == DropFlagState::Present); - state.insert(path); + state.0.insert(path); }); } - - fn pretty_print_idx(&self, w: &mut impl std::io::Write, mpi: Self::Idx) -> std::io::Result<()> { - write!(w, "{}", self.move_data().move_paths[mpi]) - } } impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> { + type Idx = MovePathIndex; + fn statement_effect( &self, trans: &mut impl GenKill<Self::Idx>, @@ -540,15 +536,16 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> { } impl<'tcx> AnalysisDomain<'tcx> for EverInitializedPlaces<'_, 'tcx> { - type Idx = InitIndex; + type Domain = BitSet<InitIndex>; const NAME: &'static str = "ever_init"; - fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize { - self.move_data().inits.len() + fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { + // bottom = no initialized variables by default + BitSet::new_empty(self.move_data().inits.len()) } - fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) { + fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) { for arg_init in 0..body.arg_count { state.insert(InitIndex::new(arg_init)); } @@ -556,6 +553,8 @@ impl<'tcx> AnalysisDomain<'tcx> for EverInitializedPlaces<'_, 'tcx> { } impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { + type Idx = InitIndex; + fn statement_effect( &self, trans: &mut impl GenKill<Self::Idx>, @@ -625,23 +624,3 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { } } } - -impl<'a, 'tcx> BottomValue for MaybeInitializedPlaces<'a, 'tcx> { - /// bottom = uninitialized - const BOTTOM_VALUE: bool = false; -} - -impl<'a, 'tcx> BottomValue for MaybeUninitializedPlaces<'a, 'tcx> { - /// bottom = initialized (start_block_effect counters this at outset) - const BOTTOM_VALUE: bool = false; -} - -impl<'a, 'tcx> BottomValue for DefinitelyInitializedPlaces<'a, 'tcx> { - /// bottom = initialized (start_block_effect counters this at outset) - const BOTTOM_VALUE: bool = true; -} - -impl<'a, 'tcx> BottomValue for EverInitializedPlaces<'a, 'tcx> { - /// bottom = no initialized variables by default - const BOTTOM_VALUE: bool = false; -} diff --git a/compiler/rustc_mir/src/dataflow/impls/storage_liveness.rs b/compiler/rustc_mir/src/dataflow/impls/storage_liveness.rs index 21623e3cad5..9250cd40847 100644 --- a/compiler/rustc_mir/src/dataflow/impls/storage_liveness.rs +++ b/compiler/rustc_mir/src/dataflow/impls/storage_liveness.rs @@ -1,6 +1,5 @@ pub use super::*; -use crate::dataflow::BottomValue; use crate::dataflow::{self, GenKill, Results, ResultsRefCursor}; use crate::util::storage::AlwaysLiveLocals; use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor}; @@ -19,15 +18,16 @@ impl MaybeStorageLive { } impl dataflow::AnalysisDomain<'tcx> for MaybeStorageLive { - type Idx = Local; + type Domain = BitSet<Local>; const NAME: &'static str = "maybe_storage_live"; - fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { - body.local_decls.len() + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { + // bottom = dead + BitSet::new_empty(body.local_decls.len()) } - fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut BitSet<Self::Idx>) { + fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut Self::Domain) { assert_eq!(body.local_decls.len(), self.always_live_locals.domain_size()); for local in self.always_live_locals.iter() { on_entry.insert(local); @@ -40,6 +40,8 @@ impl dataflow::AnalysisDomain<'tcx> for MaybeStorageLive { } impl dataflow::GenKillAnalysis<'tcx> for MaybeStorageLive { + type Idx = Local; + fn statement_effect( &self, trans: &mut impl GenKill<Self::Idx>, @@ -74,11 +76,6 @@ impl dataflow::GenKillAnalysis<'tcx> for MaybeStorageLive { } } -impl BottomValue for MaybeStorageLive { - /// bottom = dead - const BOTTOM_VALUE: bool = false; -} - type BorrowedLocalsResults<'a, 'tcx> = ResultsRefCursor<'a, 'a, 'tcx, MaybeBorrowedLocals>; /// Dataflow analysis that determines whether each local requires storage at a @@ -101,15 +98,16 @@ impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> { } impl<'mir, 'tcx> dataflow::AnalysisDomain<'tcx> for MaybeRequiresStorage<'mir, 'tcx> { - type Idx = Local; + type Domain = BitSet<Local>; const NAME: &'static str = "requires_storage"; - fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { - body.local_decls.len() + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { + // bottom = dead + BitSet::new_empty(body.local_decls.len()) } - fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut BitSet<Self::Idx>) { + fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut Self::Domain) { // The resume argument is live on function entry (we don't care about // the `self` argument) for arg in body.args_iter().skip(1) { @@ -119,6 +117,8 @@ impl<'mir, 'tcx> dataflow::AnalysisDomain<'tcx> for MaybeRequiresStorage<'mir, ' } impl<'mir, 'tcx> dataflow::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tcx> { + type Idx = Local; + fn before_statement_effect( &self, trans: &mut impl GenKill<Self::Idx>, @@ -285,11 +285,6 @@ impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> { } } -impl<'mir, 'tcx> BottomValue for MaybeRequiresStorage<'mir, 'tcx> { - /// bottom = dead - const BOTTOM_VALUE: bool = false; -} - struct MoveVisitor<'a, 'mir, 'tcx, T> { borrowed_locals: &'a RefCell<BorrowedLocalsResults<'mir, 'tcx>>, trans: &'a mut T, diff --git a/compiler/rustc_mir/src/dataflow/mod.rs b/compiler/rustc_mir/src/dataflow/mod.rs index a0c24636059..5575a97982f 100644 --- a/compiler/rustc_mir/src/dataflow/mod.rs +++ b/compiler/rustc_mir/src/dataflow/mod.rs @@ -5,9 +5,9 @@ use rustc_span::symbol::{sym, Symbol}; pub(crate) use self::drop_flag_effects::*; pub use self::framework::{ - visit_results, Analysis, AnalysisDomain, Backward, BorrowckFlowState, BorrowckResults, - BottomValue, Engine, Forward, GenKill, GenKillAnalysis, Results, ResultsCursor, - ResultsRefCursor, ResultsVisitor, + fmt, lattice, visit_results, Analysis, AnalysisDomain, Backward, BorrowckFlowState, + BorrowckResults, Engine, Forward, GenKill, GenKillAnalysis, JoinSemiLattice, Results, + ResultsCursor, ResultsRefCursor, ResultsVisitor, }; use self::move_paths::MoveData; diff --git a/compiler/rustc_mir/src/lib.rs b/compiler/rustc_mir/src/lib.rs index 2e3b5084635..42717f27384 100644 --- a/compiler/rustc_mir/src/lib.rs +++ b/compiler/rustc_mir/src/lib.rs @@ -14,6 +14,7 @@ Rust MIR: a lowered representation of Rust. #![feature(crate_visibility_modifier)] #![feature(decl_macro)] #![feature(drain_filter)] +#![feature(exact_size_is_empty)] #![feature(exhaustive_patterns)] #![feature(iter_order_by)] #![feature(never_type)] diff --git a/compiler/rustc_mir/src/monomorphize/partitioning/mod.rs b/compiler/rustc_mir/src/monomorphize/partitioning/mod.rs index e96af77bbb8..0f6f078d968 100644 --- a/compiler/rustc_mir/src/monomorphize/partitioning/mod.rs +++ b/compiler/rustc_mir/src/monomorphize/partitioning/mod.rs @@ -382,7 +382,7 @@ fn collect_and_partition_mono_items<'tcx>( cgus.sort_by_key(|(name, _)| *name); cgus.dedup(); for &(ref cgu_name, (linkage, _)) in cgus.iter() { - output.push_str(" "); + output.push(' '); output.push_str(&cgu_name.as_str()); let linkage_abbrev = match linkage { @@ -399,9 +399,9 @@ fn collect_and_partition_mono_items<'tcx>( Linkage::Common => "Common", }; - output.push_str("["); + output.push('['); output.push_str(linkage_abbrev); - output.push_str("]"); + output.push(']'); } output }) diff --git a/compiler/rustc_mir/src/transform/check_const_item_mutation.rs b/compiler/rustc_mir/src/transform/check_const_item_mutation.rs new file mode 100644 index 00000000000..589268e39bd --- /dev/null +++ b/compiler/rustc_mir/src/transform/check_const_item_mutation.rs @@ -0,0 +1,114 @@ +use rustc_errors::DiagnosticBuilder; +use rustc_middle::lint::LintDiagnosticBuilder; +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::*; +use rustc_middle::ty::TyCtxt; +use rustc_session::lint::builtin::CONST_ITEM_MUTATION; +use rustc_span::def_id::DefId; + +use crate::transform::{MirPass, MirSource}; + +pub struct CheckConstItemMutation; + +impl<'tcx> MirPass<'tcx> for CheckConstItemMutation { + fn run_pass(&self, tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut Body<'tcx>) { + let mut checker = ConstMutationChecker { body, tcx, target_local: None }; + checker.visit_body(&body); + } +} + +struct ConstMutationChecker<'a, 'tcx> { + body: &'a Body<'tcx>, + tcx: TyCtxt<'tcx>, + target_local: Option<Local>, +} + +impl<'a, 'tcx> ConstMutationChecker<'a, 'tcx> { + fn is_const_item(&self, local: Local) -> Option<DefId> { + if let Some(box LocalInfo::ConstRef { def_id }) = self.body.local_decls[local].local_info { + Some(def_id) + } else { + None + } + } + fn lint_const_item_usage( + &self, + const_item: DefId, + location: Location, + decorate: impl for<'b> FnOnce(LintDiagnosticBuilder<'b>) -> DiagnosticBuilder<'b>, + ) { + let source_info = self.body.source_info(location); + let lint_root = self.body.source_scopes[source_info.scope] + .local_data + .as_ref() + .assert_crate_local() + .lint_root; + + self.tcx.struct_span_lint_hir(CONST_ITEM_MUTATION, lint_root, source_info.span, |lint| { + decorate(lint) + .span_note(self.tcx.def_span(const_item), "`const` item defined here") + .emit() + }); + } +} + +impl<'a, 'tcx> Visitor<'tcx> for ConstMutationChecker<'a, 'tcx> { + fn visit_statement(&mut self, stmt: &Statement<'tcx>, loc: Location) { + if let StatementKind::Assign(box (lhs, _)) = &stmt.kind { + // Check for assignment to fields of a constant + // Assigning directly to a constant (e.g. `FOO = true;`) is a hard error, + // so emitting a lint would be redundant. + if !lhs.projection.is_empty() { + if let Some(def_id) = self.is_const_item(lhs.local) { + self.lint_const_item_usage(def_id, loc, |lint| { + let mut lint = lint.build("attempting to modify a `const` item"); + lint.note("each usage of a `const` item creates a new temporary - the original `const` item will not be modified"); + lint + }) + } + } + // We are looking for MIR of the form: + // + // ``` + // _1 = const FOO; + // _2 = &mut _1; + // method_call(_2, ..) + // ``` + // + // Record our current LHS, so that we can detect this + // pattern in `visit_rvalue` + self.target_local = lhs.as_local(); + } + self.super_statement(stmt, loc); + self.target_local = None; + } + fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, loc: Location) { + if let Rvalue::Ref(_, BorrowKind::Mut { .. }, place) = rvalue { + let local = place.local; + if let Some(def_id) = self.is_const_item(local) { + // If this Rvalue is being used as the right-hand side of a + // `StatementKind::Assign`, see if it ends up getting used as + // the `self` parameter of a method call (as the terminator of our current + // BasicBlock). If so, we emit a more specific lint. + let method_did = self.target_local.and_then(|target_local| { + crate::util::find_self_call(self.tcx, &self.body, target_local, loc.block) + }); + let lint_loc = + if method_did.is_some() { self.body.terminator_loc(loc.block) } else { loc }; + self.lint_const_item_usage(def_id, lint_loc, |lint| { + let mut lint = lint.build("taking a mutable reference to a `const` item"); + lint + .note("each usage of a `const` item creates a new temporary") + .note("the mutable reference will refer to this temporary, not the original `const` item"); + + if let Some(method_did) = method_did { + lint.span_note(self.tcx.def_span(method_did), "mutable reference created due to call to this method"); + } + + lint + }); + } + } + self.super_rvalue(rvalue, loc); + } +} diff --git a/compiler/rustc_mir/src/transform/check_consts/resolver.rs b/compiler/rustc_mir/src/transform/check_consts/resolver.rs index b8104292aab..a00301952b3 100644 --- a/compiler/rustc_mir/src/transform/check_consts/resolver.rs +++ b/compiler/rustc_mir/src/transform/check_consts/resolver.rs @@ -165,23 +165,19 @@ where } } -impl<Q> dataflow::BottomValue for FlowSensitiveAnalysis<'_, '_, '_, Q> { - const BOTTOM_VALUE: bool = false; -} - impl<Q> dataflow::AnalysisDomain<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q> where Q: Qualif, { - type Idx = Local; + type Domain = BitSet<Local>; const NAME: &'static str = Q::ANALYSIS_NAME; - fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { - body.local_decls.len() + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { + BitSet::new_empty(body.local_decls.len()) } - fn initialize_start_block(&self, _body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) { + fn initialize_start_block(&self, _body: &mir::Body<'tcx>, state: &mut Self::Domain) { self.transfer_function(state).initialize_state(); } } @@ -192,7 +188,7 @@ where { fn apply_statement_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut Self::Domain, statement: &mir::Statement<'tcx>, location: Location, ) { @@ -201,7 +197,7 @@ where fn apply_terminator_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut Self::Domain, terminator: &mir::Terminator<'tcx>, location: Location, ) { @@ -210,7 +206,7 @@ where fn apply_call_return_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut Self::Domain, block: BasicBlock, func: &mir::Operand<'tcx>, args: &[mir::Operand<'tcx>], diff --git a/compiler/rustc_mir/src/transform/inline.rs b/compiler/rustc_mir/src/transform/inline.rs index 0a3424bb944..d6e6371e886 100644 --- a/compiler/rustc_mir/src/transform/inline.rs +++ b/compiler/rustc_mir/src/transform/inline.rs @@ -4,7 +4,7 @@ use rustc_attr as attr; use rustc_hir::def_id::DefId; use rustc_index::bit_set::BitSet; use rustc_index::vec::{Idx, IndexVec}; -use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; use rustc_middle::ty::subst::{Subst, SubstsRef}; @@ -45,7 +45,8 @@ impl<'tcx> MirPass<'tcx> for Inline { // based function. debug!("function inlining is disabled when compiling with `instrument_coverage`"); } else { - Inliner { tcx, source }.run_pass(body); + Inliner { tcx, source, codegen_fn_attrs: tcx.codegen_fn_attrs(source.def_id()) } + .run_pass(body); } } } @@ -54,6 +55,7 @@ impl<'tcx> MirPass<'tcx> for Inline { struct Inliner<'tcx> { tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, + codegen_fn_attrs: &'tcx CodegenFnAttrs, } impl Inliner<'tcx> { @@ -242,9 +244,19 @@ impl Inliner<'tcx> { return false; } - // Avoid inlining functions marked as no_sanitize if sanitizer is enabled, - // since instrumentation might be enabled and performed on the caller. - if self.tcx.sess.opts.debugging_opts.sanitizer.intersects(codegen_fn_attrs.no_sanitize) { + let self_features = &self.codegen_fn_attrs.target_features; + let callee_features = &codegen_fn_attrs.target_features; + if callee_features.iter().any(|feature| !self_features.contains(feature)) { + debug!("`callee has extra target features - not inlining"); + return false; + } + + let self_no_sanitize = + self.codegen_fn_attrs.no_sanitize & self.tcx.sess.opts.debugging_opts.sanitizer; + let callee_no_sanitize = + codegen_fn_attrs.no_sanitize & self.tcx.sess.opts.debugging_opts.sanitizer; + if self_no_sanitize != callee_no_sanitize { + debug!("`callee has incompatible no_sanitize attribute - not inlining"); return false; } diff --git a/compiler/rustc_mir/src/transform/instcombine.rs b/compiler/rustc_mir/src/transform/instcombine.rs index c6474ba2d73..c4924cf16ab 100644 --- a/compiler/rustc_mir/src/transform/instcombine.rs +++ b/compiler/rustc_mir/src/transform/instcombine.rs @@ -6,7 +6,7 @@ use rustc_hir::Mutability; use rustc_index::vec::Idx; use rustc_middle::mir::visit::{MutVisitor, Visitor}; use rustc_middle::mir::{ - Body, Constant, Local, Location, Operand, Place, PlaceRef, ProjectionElem, Rvalue, + BinOp, Body, Constant, Local, Location, Operand, Place, PlaceRef, ProjectionElem, Rvalue, }; use rustc_middle::ty::{self, TyCtxt}; use std::mem; @@ -66,6 +66,11 @@ impl<'tcx> MutVisitor<'tcx> for InstCombineVisitor<'tcx> { *rvalue = Rvalue::Use(Operand::Constant(box constant)); } + if let Some(operand) = self.optimizations.unneeded_equality_comparison.remove(&location) { + debug!("replacing {:?} with {:?}", rvalue, operand); + *rvalue = Rvalue::Use(operand); + } + self.super_rvalue(rvalue, location) } } @@ -81,6 +86,48 @@ impl OptimizationFinder<'b, 'tcx> { fn new(body: &'b Body<'tcx>, tcx: TyCtxt<'tcx>) -> OptimizationFinder<'b, 'tcx> { OptimizationFinder { body, tcx, optimizations: OptimizationList::default() } } + + fn find_unneeded_equality_comparison(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { + // find Ne(_place, false) or Ne(false, _place) + // or Eq(_place, true) or Eq(true, _place) + if let Rvalue::BinaryOp(op, l, r) = rvalue { + let const_to_find = if *op == BinOp::Ne { + false + } else if *op == BinOp::Eq { + true + } else { + return; + }; + // (const, _place) + if let Some(o) = self.find_operand_in_equality_comparison_pattern(l, r, const_to_find) { + self.optimizations.unneeded_equality_comparison.insert(location, o.clone()); + } + // (_place, const) + else if let Some(o) = + self.find_operand_in_equality_comparison_pattern(r, l, const_to_find) + { + self.optimizations.unneeded_equality_comparison.insert(location, o.clone()); + } + } + } + + fn find_operand_in_equality_comparison_pattern( + &self, + l: &Operand<'tcx>, + r: &'a Operand<'tcx>, + const_to_find: bool, + ) -> Option<&'a Operand<'tcx>> { + let const_ = l.constant()?; + if const_.literal.ty == self.tcx.types.bool + && const_.literal.val.try_to_bool() == Some(const_to_find) + { + if r.place().is_some() { + return Some(r); + } + } + + return None; + } } impl Visitor<'tcx> for OptimizationFinder<'b, 'tcx> { @@ -106,6 +153,8 @@ impl Visitor<'tcx> for OptimizationFinder<'b, 'tcx> { } } + self.find_unneeded_equality_comparison(rvalue, location); + self.super_rvalue(rvalue, location) } } @@ -114,4 +163,5 @@ impl Visitor<'tcx> for OptimizationFinder<'b, 'tcx> { struct OptimizationList<'tcx> { and_stars: FxHashSet<Location>, arrays_lengths: FxHashMap<Location, Constant<'tcx>>, + unneeded_equality_comparison: FxHashMap<Location, Operand<'tcx>>, } diff --git a/compiler/rustc_mir/src/transform/instrument_coverage.rs b/compiler/rustc_mir/src/transform/instrument_coverage.rs index d3a2bd24123..a5b30a25a9b 100644 --- a/compiler/rustc_mir/src/transform/instrument_coverage.rs +++ b/compiler/rustc_mir/src/transform/instrument_coverage.rs @@ -309,7 +309,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { for coverage_region in coverage_regions { span_viewables.push(SpanViewable { span: coverage_region.span, - title: format!("{}", coverage_region.blocks[0].index()), + id: format!("{}", coverage_region.blocks[0].index()), tooltip: self.make_tooltip_text(coverage_region), }); } @@ -353,7 +353,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { if !INCLUDE_COVERAGE_STATEMENTS { continue; } - format!("unreachable") + String::from("unreachable") } }, _ => format!("{:?}", statement), diff --git a/compiler/rustc_mir/src/transform/mod.rs b/compiler/rustc_mir/src/transform/mod.rs index c3a34756122..8025b7c0204 100644 --- a/compiler/rustc_mir/src/transform/mod.rs +++ b/compiler/rustc_mir/src/transform/mod.rs @@ -16,6 +16,7 @@ use std::borrow::Cow; pub mod add_call_guards; pub mod add_moves_for_packed_drops; pub mod add_retag; +pub mod check_const_item_mutation; pub mod check_consts; pub mod check_packed_ref; pub mod check_unsafety; @@ -307,6 +308,7 @@ fn mir_const<'tcx>( &[&[ // MIR-level lints. &check_packed_ref::CheckPackedRef, + &check_const_item_mutation::CheckConstItemMutation, // What we need to do constant evaluation. &simplify::SimplifyCfg::new("initial"), &rustc_peek::SanityCheck, @@ -453,8 +455,9 @@ fn run_optimization_passes<'tcx>( // The main optimizations that we do on MIR. let optimizations: &[&dyn MirPass<'tcx>] = &[ - &instcombine::InstCombine, &match_branches::MatchBranchSimplification, + // inst combine is after MatchBranchSimplification to clean up Ne(_1, false) + &instcombine::InstCombine, &const_prop::ConstProp, &simplify_branches::SimplifyBranches::new("after-const-prop"), &simplify_comparison_integral::SimplifyComparisonIntegral, diff --git a/compiler/rustc_mir/src/transform/promote_consts.rs b/compiler/rustc_mir/src/transform/promote_consts.rs index 852629a45f7..1d2295a37dd 100644 --- a/compiler/rustc_mir/src/transform/promote_consts.rs +++ b/compiler/rustc_mir/src/transform/promote_consts.rs @@ -364,15 +364,7 @@ impl<'tcx> Validator<'_, 'tcx> { // In theory, any zero-sized value could be borrowed // mutably without consequences. However, only &mut [] // is allowed right now, and only in functions. - if self.const_kind - == Some(hir::ConstContext::Static(hir::Mutability::Mut)) - { - // Inside a `static mut`, &mut [...] is also allowed. - match ty.kind() { - ty::Array(..) | ty::Slice(_) => {} - _ => return Err(Unpromotable), - } - } else if let ty::Array(_, len) = ty.kind() { + if let ty::Array(_, len) = ty.kind() { // FIXME(eddyb) the `self.is_non_const_fn` condition // seems unnecessary, given that this is merely a ZST. match len.try_eval_usize(self.tcx, self.param_env) { @@ -673,13 +665,7 @@ impl<'tcx> Validator<'_, 'tcx> { // In theory, any zero-sized value could be borrowed // mutably without consequences. However, only &mut [] // is allowed right now, and only in functions. - if self.const_kind == Some(hir::ConstContext::Static(hir::Mutability::Mut)) { - // Inside a `static mut`, &mut [...] is also allowed. - match ty.kind() { - ty::Array(..) | ty::Slice(_) => {} - _ => return Err(Unpromotable), - } - } else if let ty::Array(_, len) = ty.kind() { + if let ty::Array(_, len) = ty.kind() { // FIXME(eddyb): We only return `Unpromotable` for `&mut []` inside a // const context which seems unnecessary given that this is merely a ZST. match len.try_eval_usize(self.tcx, self.param_env) { diff --git a/compiler/rustc_mir/src/transform/rustc_peek.rs b/compiler/rustc_mir/src/transform/rustc_peek.rs index 242192e75b4..015af44b80f 100644 --- a/compiler/rustc_mir/src/transform/rustc_peek.rs +++ b/compiler/rustc_mir/src/transform/rustc_peek.rs @@ -1,4 +1,6 @@ -use rustc_ast as ast; +use std::borrow::Borrow; + +use rustc_ast::ast; use rustc_span::symbol::sym; use rustc_span::Span; use rustc_target::spec::abi::Abi; @@ -16,7 +18,7 @@ use crate::dataflow::impls::{ use crate::dataflow::move_paths::{HasMoveData, MoveData}; use crate::dataflow::move_paths::{LookupResult, MovePathIndex}; use crate::dataflow::MoveDataParamEnv; -use crate::dataflow::{Analysis, Results, ResultsCursor}; +use crate::dataflow::{Analysis, JoinSemiLattice, Results, ResultsCursor}; pub struct SanityCheck; @@ -248,25 +250,26 @@ pub trait RustcPeekAt<'tcx>: Analysis<'tcx> { &self, tcx: TyCtxt<'tcx>, place: mir::Place<'tcx>, - flow_state: &BitSet<Self::Idx>, + flow_state: &Self::Domain, call: PeekCall, ); } -impl<'tcx, A> RustcPeekAt<'tcx> for A +impl<'tcx, A, D> RustcPeekAt<'tcx> for A where - A: Analysis<'tcx, Idx = MovePathIndex> + HasMoveData<'tcx>, + A: Analysis<'tcx, Domain = D> + HasMoveData<'tcx>, + D: JoinSemiLattice + Clone + Borrow<BitSet<MovePathIndex>>, { fn peek_at( &self, tcx: TyCtxt<'tcx>, place: mir::Place<'tcx>, - flow_state: &BitSet<Self::Idx>, + flow_state: &Self::Domain, call: PeekCall, ) { match self.move_data().rev_lookup.find(place.as_ref()) { LookupResult::Exact(peek_mpi) => { - let bit_state = flow_state.contains(peek_mpi); + let bit_state = flow_state.borrow().contains(peek_mpi); debug!("rustc_peek({:?} = &{:?}) bit_state: {}", call.arg, place, bit_state); if !bit_state { tcx.sess.span_err(call.span, "rustc_peek: bit not set"); diff --git a/compiler/rustc_mir/src/transform/simplify_try.rs b/compiler/rustc_mir/src/transform/simplify_try.rs index 5c9a191e905..a320d00614d 100644 --- a/compiler/rustc_mir/src/transform/simplify_try.rs +++ b/compiler/rustc_mir/src/transform/simplify_try.rs @@ -367,11 +367,7 @@ fn optimization_applies<'tcx>( } impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity { - fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { - if tcx.sess.opts.debugging_opts.mir_opt_level < 2 { - return; - } - + fn run_pass(&self, _tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { trace!("running SimplifyArmIdentity on {:?}", source); let local_uses = LocalUseCounter::get_local_uses(body); let (basic_blocks, local_decls, debug_info) = @@ -678,7 +674,7 @@ impl<'a, 'tcx> SimplifyBranchSameOptimizationFinder<'a, 'tcx> { y_bb_idx: BasicBlock, ) -> StatementEquality { let helper = |rhs: &Rvalue<'tcx>, - place: &Box<Place<'tcx>>, + place: &Place<'tcx>, variant_index: &VariantIdx, side_to_choose| { let place_type = place.ty(self.body, self.tcx).ty; diff --git a/compiler/rustc_mir/src/transform/validate.rs b/compiler/rustc_mir/src/transform/validate.rs index 8f01e942801..d3ca14abdca 100644 --- a/compiler/rustc_mir/src/transform/validate.rs +++ b/compiler/rustc_mir/src/transform/validate.rs @@ -4,8 +4,8 @@ use super::{MirPass, MirSource}; use rustc_middle::mir::visit::Visitor; use rustc_middle::{ mir::{ - AggregateKind, BasicBlock, Body, Location, MirPhase, Operand, Rvalue, Statement, - StatementKind, Terminator, TerminatorKind, + AggregateKind, BasicBlock, Body, BorrowKind, Location, MirPhase, Operand, Rvalue, + Statement, StatementKind, Terminator, TerminatorKind, }, ty::{ self, @@ -274,9 +274,33 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ) } } + Rvalue::Ref(_, BorrowKind::Shallow, _) => { + if self.mir_phase > MirPhase::DropLowering { + self.fail( + location, + "`Assign` statement with a `Shallow` borrow should have been removed after drop lowering phase", + ); + } + } _ => {} } } + StatementKind::AscribeUserType(..) => { + if self.mir_phase > MirPhase::DropLowering { + self.fail( + location, + "`AscribeUserType` should have been removed after drop lowering phase", + ); + } + } + StatementKind::FakeRead(..) => { + if self.mir_phase > MirPhase::DropLowering { + self.fail( + location, + "`FakeRead` should have been removed after drop lowering phase", + ); + } + } _ => {} } } diff --git a/compiler/rustc_mir/src/util/find_self_call.rs b/compiler/rustc_mir/src/util/find_self_call.rs new file mode 100644 index 00000000000..049b5f01214 --- /dev/null +++ b/compiler/rustc_mir/src/util/find_self_call.rs @@ -0,0 +1,35 @@ +use rustc_middle::mir::*; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_span::def_id::DefId; + +/// Checks if the specified `local` is used as the `self` prameter of a method call +/// in the provided `BasicBlock`. If it is, then the `DefId` of the called method is +/// returned. +pub fn find_self_call( + tcx: TyCtxt<'_>, + body: &Body<'_>, + local: Local, + block: BasicBlock, +) -> Option<DefId> { + debug!("find_self_call(local={:?}): terminator={:?}", local, &body[block].terminator); + if let Some(Terminator { kind: TerminatorKind::Call { func, args, .. }, .. }) = + &body[block].terminator + { + debug!("find_self_call: func={:?}", func); + if let Operand::Constant(box Constant { literal: ty::Const { ty, .. }, .. }) = func { + if let ty::FnDef(def_id, _) = *ty.kind() { + if let Some(ty::AssocItem { fn_has_self_parameter: true, .. }) = + tcx.opt_associated_item(def_id) + { + debug!("find_self_call: args={:?}", args); + if let [Operand::Move(self_place) | Operand::Copy(self_place), ..] = **args { + if self_place.as_local() == Some(local) { + return Some(def_id); + } + } + } + } + } + } + None +} diff --git a/compiler/rustc_mir/src/util/graphviz.rs b/compiler/rustc_mir/src/util/graphviz.rs index 50193c4a0db..e89c9437706 100644 --- a/compiler/rustc_mir/src/util/graphviz.rs +++ b/compiler/rustc_mir/src/util/graphviz.rs @@ -55,16 +55,28 @@ where writeln!(w, "{} {}Mir_{} {{", kind, cluster, def_name)?; // Global graph properties - writeln!(w, r#" graph [fontname="monospace"];"#)?; - writeln!(w, r#" node [fontname="monospace"];"#)?; - writeln!(w, r#" edge [fontname="monospace"];"#)?; + let font = r#"fontname="Courier, monospace""#; + let mut graph_attrs = vec![font]; + let mut content_attrs = vec![font]; + + let dark_mode = tcx.sess.opts.debugging_opts.graphviz_dark_mode; + if dark_mode { + graph_attrs.push(r#"bgcolor="black""#); + content_attrs.push(r#"color="white""#); + content_attrs.push(r#"fontcolor="white""#); + } + + writeln!(w, r#" graph [{}];"#, graph_attrs.join(" "))?; + let content_attrs_str = content_attrs.join(" "); + writeln!(w, r#" node [{}];"#, content_attrs_str)?; + writeln!(w, r#" edge [{}];"#, content_attrs_str)?; // Graph label write_graph_label(tcx, def_id, body, w)?; // Nodes for (block, _) in body.basic_blocks().iter_enumerated() { - write_node(def_id, block, body, w)?; + write_node(def_id, block, body, dark_mode, w)?; } // Edges @@ -84,6 +96,7 @@ where pub fn write_node_label<W: Write, INIT, FINI>( block: BasicBlock, body: &Body<'_>, + dark_mode: bool, w: &mut W, num_cols: u32, init: INIT, @@ -100,8 +113,9 @@ where // Basic block number at the top. write!( w, - r#"<tr><td {attrs} colspan="{colspan}">{blk}</td></tr>"#, - attrs = r#"bgcolor="gray" align="center""#, + r#"<tr><td bgcolor="{bgcolor}" {attrs} colspan="{colspan}">{blk}</td></tr>"#, + bgcolor = if dark_mode { "dimgray" } else { "gray" }, + attrs = r#"align="center""#, colspan = num_cols, blk = block.index() )?; @@ -134,11 +148,12 @@ fn write_node<W: Write>( def_id: DefId, block: BasicBlock, body: &Body<'_>, + dark_mode: bool, w: &mut W, ) -> io::Result<()> { // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables. write!(w, r#" {} [shape="none", label=<"#, node(def_id, block))?; - write_node_label(block, body, w, 1, |_| Ok(()), |_| Ok(()))?; + write_node_label(block, body, dark_mode, w, 1, |_| Ok(()), |_| Ok(()))?; // Close the node label and the node itself. writeln!(w, ">];") } diff --git a/compiler/rustc_mir/src/util/mod.rs b/compiler/rustc_mir/src/util/mod.rs index ed0fafb1aac..699f3bcf014 100644 --- a/compiler/rustc_mir/src/util/mod.rs +++ b/compiler/rustc_mir/src/util/mod.rs @@ -7,12 +7,14 @@ pub mod storage; mod alignment; pub mod collect_writes; +mod find_self_call; mod graphviz; pub(crate) mod pretty; pub(crate) mod spanview; pub use self::aggregate::expand_aggregate; pub use self::alignment::is_disaligned; +pub use self::find_self_call::find_self_call; pub use self::graphviz::write_node_label as write_graphviz_node_label; pub use self::graphviz::{graphviz_safe_def_name, write_mir_graphviz}; pub use self::pretty::{dump_enabled, dump_mir, write_mir_pretty, PassWhere}; diff --git a/compiler/rustc_mir/src/util/pretty.rs b/compiler/rustc_mir/src/util/pretty.rs index 54bc248bc5b..75567181b69 100644 --- a/compiler/rustc_mir/src/util/pretty.rs +++ b/compiler/rustc_mir/src/util/pretty.rs @@ -514,7 +514,7 @@ fn write_scope_tree( write!(indented_decl, " as {:?}", user_ty).unwrap(); } } - indented_decl.push_str(";"); + indented_decl.push(';'); let local_name = if local == RETURN_PLACE { " return place".to_string() } else { String::new() }; diff --git a/compiler/rustc_mir/src/util/spanview.rs b/compiler/rustc_mir/src/util/spanview.rs index b2f2b5fc1e6..fe33fffe0ea 100644 --- a/compiler/rustc_mir/src/util/spanview.rs +++ b/compiler/rustc_mir/src/util/spanview.rs @@ -3,13 +3,16 @@ use rustc_middle::hir; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; use rustc_session::config::MirSpanview; -use rustc_span::{BytePos, Pos, Span}; +use rustc_span::{BytePos, Pos, Span, SyntaxContext}; +use std::cmp; use std::io::{self, Write}; -use std::iter::Peekable; pub const TOOLTIP_INDENT: &str = " "; +const CARET: char = '\u{2038}'; // Unicode `CARET` +const ANNOTATION_LEFT_BRACKET: char = '\u{298a}'; // Unicode `Z NOTATION RIGHT BINDING BRACKET +const ANNOTATION_RIGHT_BRACKET: char = '\u{2989}'; // Unicode `Z NOTATION LEFT BINDING BRACKET` const NEW_LINE_SPAN: &str = "</span>\n<span class=\"line\">"; const HEADER: &str = r#"<!DOCTYPE html> <html> @@ -80,7 +83,7 @@ const FOOTER: &str = r#" /// Metadata to highlight the span of a MIR BasicBlock, Statement, or Terminator. pub struct SpanViewable { pub span: Span, - pub title: String, + pub id: String, pub tooltip: String, } @@ -139,16 +142,22 @@ where W: Write, { let fn_span = fn_span(tcx, def_id); - writeln!(w, "{}", HEADER)?; - let mut next_pos = fn_span.lo(); + let mut from_pos = fn_span.lo(); let end_pos = fn_span.hi(); let source_map = tcx.sess.source_map(); - let start = source_map.lookup_char_pos(next_pos); + let start = source_map.lookup_char_pos(from_pos); + let indent_to_initial_start_col = " ".repeat(start.col.to_usize()); + debug!( + "fn_span source is:\n{}{}", + indent_to_initial_start_col, + source_map.span_to_snippet(fn_span).expect("function should have printable source") + ); + writeln!(w, "{}", HEADER)?; write!( w, r#"<div class="code" style="counter-reset: line {}"><span class="line">{}"#, start.line - 1, - " ".repeat(start.col.to_usize()) + indent_to_initial_start_col, )?; span_viewables.sort_unstable_by(|a, b| { let a = a.span; @@ -163,14 +172,43 @@ where } .unwrap() }); - let mut ordered_span_viewables = span_viewables.iter().peekable(); + let mut ordered_viewables = &span_viewables[..]; + const LOWEST_VIEWABLE_LAYER: usize = 1; let mut alt = false; - while ordered_span_viewables.peek().is_some() { - next_pos = write_span_viewables(tcx, next_pos, &mut ordered_span_viewables, false, 1, w)?; - alt = !alt; + while ordered_viewables.len() > 0 { + debug!( + "calling write_next_viewable with from_pos={}, end_pos={}, and viewables len={}", + from_pos.to_usize(), + end_pos.to_usize(), + ordered_viewables.len() + ); + let (next_from_pos, next_ordered_viewables) = write_next_viewable_with_overlaps( + tcx, + from_pos, + end_pos, + ordered_viewables, + alt, + LOWEST_VIEWABLE_LAYER, + w, + )?; + debug!( + "DONE calling write_next_viewable, with new from_pos={}, \ + and remaining viewables len={}", + next_from_pos.to_usize(), + next_ordered_viewables.len() + ); + assert!( + from_pos != next_from_pos || ordered_viewables.len() != next_ordered_viewables.len(), + "write_next_viewable_with_overlaps() must make a state change" + ); + from_pos = next_from_pos; + if next_ordered_viewables.len() != ordered_viewables.len() { + ordered_viewables = next_ordered_viewables; + alt = !alt; + } } - if next_pos < end_pos { - write_coverage_gap(tcx, next_pos, end_pos, w)?; + if from_pos < end_pos { + write_coverage_gap(tcx, from_pos, end_pos, w)?; } write!(w, r#"</span></div>"#)?; writeln!(w, "{}", FOOTER)?; @@ -233,9 +271,9 @@ fn statement_span_viewable<'tcx>( if !body_span.contains(span) { return None; } - let title = format!("bb{}[{}]", bb.index(), i); - let tooltip = tooltip(tcx, &title, span, vec![statement.clone()], &None); - Some(SpanViewable { span, title, tooltip }) + let id = format!("{}[{}]", bb.index(), i); + let tooltip = tooltip(tcx, &id, span, vec![statement.clone()], &None); + Some(SpanViewable { span, id, tooltip }) } fn terminator_span_viewable<'tcx>( @@ -249,9 +287,9 @@ fn terminator_span_viewable<'tcx>( if !body_span.contains(span) { return None; } - let title = format!("bb{}`{}`", bb.index(), terminator_kind_name(term)); - let tooltip = tooltip(tcx, &title, span, vec![], &data.terminator); - Some(SpanViewable { span, title, tooltip }) + let id = format!("{}:{}", bb.index(), terminator_kind_name(term)); + let tooltip = tooltip(tcx, &id, span, vec![], &data.terminator); + Some(SpanViewable { span, id, tooltip }) } fn block_span_viewable<'tcx>( @@ -264,16 +302,16 @@ fn block_span_viewable<'tcx>( if !body_span.contains(span) { return None; } - let title = format!("bb{}", bb.index()); - let tooltip = tooltip(tcx, &title, span, data.statements.clone(), &data.terminator); - Some(SpanViewable { span, title, tooltip }) + let id = format!("{}", bb.index()); + let tooltip = tooltip(tcx, &id, span, data.statements.clone(), &data.terminator); + Some(SpanViewable { span, id, tooltip }) } fn compute_block_span<'tcx>(data: &BasicBlockData<'tcx>, body_span: Span) -> Span { let mut span = data.terminator().source_info.span; for statement_span in data.statements.iter().map(|statement| statement.source_info.span) { - // Only combine Spans from the function's body_span. - if body_span.contains(statement_span) { + // Only combine Spans from the root context, and within the function's body_span. + if statement_span.ctxt() == SyntaxContext::root() && body_span.contains(statement_span) { span = span.to(statement_span); } } @@ -286,100 +324,217 @@ fn compute_block_span<'tcx>(data: &BasicBlockData<'tcx>, body_span: Span) -> Spa /// The `layer` is incremented for each overlap, and the `alt` bool alternates between true /// and false, for each adjacent non-overlapping span. Source code between the spans (code /// that is not in any coverage region) has neutral styling. -fn write_span_viewables<'tcx, 'b, W>( +fn write_next_viewable_with_overlaps<'tcx, 'b, W>( tcx: TyCtxt<'tcx>, - next_pos: BytePos, - ordered_span_viewables: &mut Peekable<impl Iterator<Item = &'b SpanViewable>>, + mut from_pos: BytePos, + mut to_pos: BytePos, + ordered_viewables: &'b [SpanViewable], alt: bool, layer: usize, w: &mut W, -) -> io::Result<BytePos> +) -> io::Result<(BytePos, &'b [SpanViewable])> where W: Write, { - let span_viewable = - ordered_span_viewables.next().expect("ordered_span_viewables should have some"); - if next_pos < span_viewable.span.lo() { - write_coverage_gap(tcx, next_pos, span_viewable.span.lo(), w)?; + let debug_indent = " ".repeat(layer); + let (viewable, mut remaining_viewables) = + ordered_viewables.split_first().expect("ordered_viewables should have some"); + + if from_pos < viewable.span.lo() { + debug!( + "{}advance from_pos to next SpanViewable (from from_pos={} to viewable.span.lo()={} \ + of {:?}), with to_pos={}", + debug_indent, + from_pos.to_usize(), + viewable.span.lo().to_usize(), + viewable.span, + to_pos.to_usize() + ); + let hi = cmp::min(viewable.span.lo(), to_pos); + write_coverage_gap(tcx, from_pos, hi, w)?; + from_pos = hi; + if from_pos < viewable.span.lo() { + debug!( + "{}EARLY RETURN: stopped before getting to next SpanViewable, at {}", + debug_indent, + from_pos.to_usize() + ); + return Ok((from_pos, ordered_viewables)); + } } - let mut remaining_span = span_viewable.span; + + if from_pos < viewable.span.hi() { + // Set to_pos to the end of this `viewable` to ensure the recursive calls stop writing + // with room to print the tail. + to_pos = cmp::min(viewable.span.hi(), to_pos); + debug!( + "{}update to_pos (if not closer) to viewable.span.hi()={}; to_pos is now {}", + debug_indent, + viewable.span.hi().to_usize(), + to_pos.to_usize() + ); + } + let mut subalt = false; - loop { - let next_span_viewable = match ordered_span_viewables.peek() { - None => break, - Some(span_viewable) => *span_viewable, + while remaining_viewables.len() > 0 && remaining_viewables[0].span.overlaps(viewable.span) { + let overlapping_viewable = &remaining_viewables[0]; + debug!("{}overlapping_viewable.span={:?}", debug_indent, overlapping_viewable.span); + + let span = + trim_span(viewable.span, from_pos, cmp::min(overlapping_viewable.span.lo(), to_pos)); + let mut some_html_snippet = if from_pos <= viewable.span.hi() || viewable.span.is_empty() { + // `viewable` is not yet fully rendered, so start writing the span, up to either the + // `to_pos` or the next `overlapping_viewable`, whichever comes first. + debug!( + "{}make html_snippet (may not write it if early exit) for partial span {:?} \ + of viewable.span {:?}", + debug_indent, span, viewable.span + ); + from_pos = span.hi(); + make_html_snippet(tcx, span, Some(&viewable)) + } else { + None }; - if !next_span_viewable.span.overlaps(remaining_span) { - break; + + // Defer writing the HTML snippet (until after early return checks) ONLY for empty spans. + // An empty Span with Some(html_snippet) is probably a tail marker. If there is an early + // exit, there should be another opportunity to write the tail marker. + if !span.is_empty() { + if let Some(ref html_snippet) = some_html_snippet { + debug!( + "{}write html_snippet for that partial span of viewable.span {:?}", + debug_indent, viewable.span + ); + write_span(html_snippet, &viewable.tooltip, alt, layer, w)?; + } + some_html_snippet = None; } - write_span( - tcx, - remaining_span.until(next_span_viewable.span), - Some(span_viewable), - alt, - layer, - w, - )?; - let next_pos = write_span_viewables( + + if from_pos < overlapping_viewable.span.lo() { + debug!( + "{}EARLY RETURN: from_pos={} has not yet reached the \ + overlapping_viewable.span {:?}", + debug_indent, + from_pos.to_usize(), + overlapping_viewable.span + ); + // must have reached `to_pos` before reaching the start of the + // `overlapping_viewable.span` + return Ok((from_pos, ordered_viewables)); + } + + if from_pos == to_pos + && !(from_pos == overlapping_viewable.span.lo() && overlapping_viewable.span.is_empty()) + { + debug!( + "{}EARLY RETURN: from_pos=to_pos={} and overlapping_viewable.span {:?} is not \ + empty, or not from_pos", + debug_indent, + to_pos.to_usize(), + overlapping_viewable.span + ); + // `to_pos` must have occurred before the overlapping viewable. Return + // `ordered_viewables` so we can continue rendering the `viewable`, from after the + // `to_pos`. + return Ok((from_pos, ordered_viewables)); + } + + if let Some(ref html_snippet) = some_html_snippet { + debug!( + "{}write html_snippet for that partial span of viewable.span {:?}", + debug_indent, viewable.span + ); + write_span(html_snippet, &viewable.tooltip, alt, layer, w)?; + } + + debug!( + "{}recursively calling write_next_viewable with from_pos={}, to_pos={}, \ + and viewables len={}", + debug_indent, + from_pos.to_usize(), + to_pos.to_usize(), + remaining_viewables.len() + ); + // Write the overlaps (and the overlaps' overlaps, if any) up to `to_pos`. + let (next_from_pos, next_remaining_viewables) = write_next_viewable_with_overlaps( tcx, - next_span_viewable.span.lo(), - ordered_span_viewables, + from_pos, + to_pos, + &remaining_viewables, subalt, layer + 1, w, )?; - subalt = !subalt; - if next_pos < remaining_span.hi() { - remaining_span = remaining_span.with_lo(next_pos); - } else { - return Ok(next_pos); + debug!( + "{}DONE recursively calling write_next_viewable, with new from_pos={}, and remaining \ + viewables len={}", + debug_indent, + next_from_pos.to_usize(), + next_remaining_viewables.len() + ); + assert!( + from_pos != next_from_pos + || remaining_viewables.len() != next_remaining_viewables.len(), + "write_next_viewable_with_overlaps() must make a state change" + ); + from_pos = next_from_pos; + if next_remaining_viewables.len() != remaining_viewables.len() { + remaining_viewables = next_remaining_viewables; + subalt = !subalt; + } + } + if from_pos <= viewable.span.hi() { + let span = trim_span(viewable.span, from_pos, to_pos); + debug!( + "{}After overlaps, writing (end span?) {:?} of viewable.span {:?}", + debug_indent, span, viewable.span + ); + if let Some(ref html_snippet) = make_html_snippet(tcx, span, Some(&viewable)) { + from_pos = span.hi(); + write_span(html_snippet, &viewable.tooltip, alt, layer, w)?; } } - write_span(tcx, remaining_span, Some(span_viewable), alt, layer, w) + debug!("{}RETURN: No more overlap", debug_indent); + Ok(( + from_pos, + if from_pos < viewable.span.hi() { ordered_viewables } else { remaining_viewables }, + )) } +#[inline(always)] fn write_coverage_gap<'tcx, W>( tcx: TyCtxt<'tcx>, lo: BytePos, hi: BytePos, w: &mut W, -) -> io::Result<BytePos> +) -> io::Result<()> where W: Write, { - write_span(tcx, Span::with_root_ctxt(lo, hi), None, false, 0, w) + let span = Span::with_root_ctxt(lo, hi); + if let Some(ref html_snippet) = make_html_snippet(tcx, span, None) { + write_span(html_snippet, "", false, 0, w) + } else { + Ok(()) + } } -fn write_span<'tcx, W>( - tcx: TyCtxt<'tcx>, - span: Span, - span_viewable: Option<&SpanViewable>, +fn write_span<W>( + html_snippet: &str, + tooltip: &str, alt: bool, layer: usize, w: &mut W, -) -> io::Result<BytePos> +) -> io::Result<()> where W: Write, { - let source_map = tcx.sess.source_map(); - let snippet = source_map - .span_to_snippet(span) - .unwrap_or_else(|err| bug!("span_to_snippet error for span {:?}: {:?}", span, err)); - let labeled_snippet = if let Some(SpanViewable { title, .. }) = span_viewable { - if span.is_empty() { - format!(r#"<span class="annotation">@{}</span>"#, title) - } else { - format!(r#"<span class="annotation">@{}:</span> {}"#, title, escape_html(&snippet)) - } - } else { - snippet - }; - let maybe_alt = if layer > 0 { + let maybe_alt_class = if layer > 0 { if alt { " odd" } else { " even" } } else { "" }; - let maybe_tooltip = if let Some(SpanViewable { tooltip, .. }) = span_viewable { + let maybe_title_attr = if !tooltip.is_empty() { format!(" title=\"{}\"", escape_attr(tooltip)) } else { "".to_owned() @@ -387,32 +542,73 @@ where if layer == 1 { write!(w, "<span>")?; } - for (i, line) in labeled_snippet.lines().enumerate() { + for (i, line) in html_snippet.lines().enumerate() { if i > 0 { write!(w, "{}", NEW_LINE_SPAN)?; } write!( w, r#"<span class="code{}" style="--layer: {}"{}>{}</span>"#, - maybe_alt, layer, maybe_tooltip, line + maybe_alt_class, layer, maybe_title_attr, line )?; } + // Check for and translate trailing newlines, because `str::lines()` ignores them + if html_snippet.ends_with('\n') { + write!(w, "{}", NEW_LINE_SPAN)?; + } if layer == 1 { write!(w, "</span>")?; } - Ok(span.hi()) + Ok(()) +} + +fn make_html_snippet<'tcx>( + tcx: TyCtxt<'tcx>, + span: Span, + some_viewable: Option<&SpanViewable>, +) -> Option<String> { + let source_map = tcx.sess.source_map(); + let snippet = source_map + .span_to_snippet(span) + .unwrap_or_else(|err| bug!("span_to_snippet error for span {:?}: {:?}", span, err)); + let html_snippet = if let Some(viewable) = some_viewable { + let is_head = span.lo() == viewable.span.lo(); + let is_tail = span.hi() == viewable.span.hi(); + let mut labeled_snippet = if is_head { + format!(r#"<span class="annotation">{}{}</span>"#, viewable.id, ANNOTATION_LEFT_BRACKET) + } else { + "".to_owned() + }; + if span.is_empty() { + if is_head && is_tail { + labeled_snippet.push(CARET); + } + } else { + labeled_snippet.push_str(&escape_html(&snippet)); + }; + if is_tail { + labeled_snippet.push_str(&format!( + r#"<span class="annotation">{}{}</span>"#, + ANNOTATION_RIGHT_BRACKET, viewable.id + )); + } + labeled_snippet + } else { + escape_html(&snippet) + }; + if html_snippet.is_empty() { None } else { Some(html_snippet) } } fn tooltip<'tcx>( tcx: TyCtxt<'tcx>, - title: &str, + spanview_id: &str, span: Span, statements: Vec<Statement<'tcx>>, terminator: &Option<Terminator<'tcx>>, ) -> String { let source_map = tcx.sess.source_map(); let mut text = Vec::new(); - text.push(format!("{}: {}:", title, &source_map.span_to_string(span))); + text.push(format!("{}: {}:", spanview_id, &source_map.span_to_string(span))); for statement in statements { let source_range = source_range_no_file(tcx, &statement.source_info.span); text.push(format!( @@ -436,10 +632,25 @@ fn tooltip<'tcx>( text.join("") } +fn trim_span(span: Span, from_pos: BytePos, to_pos: BytePos) -> Span { + trim_span_hi(trim_span_lo(span, from_pos), to_pos) +} + +fn trim_span_lo(span: Span, from_pos: BytePos) -> Span { + if from_pos <= span.lo() { span } else { span.with_lo(cmp::min(span.hi(), from_pos)) } +} + +fn trim_span_hi(span: Span, to_pos: BytePos) -> Span { + if to_pos >= span.hi() { span } else { span.with_hi(cmp::max(span.lo(), to_pos)) } +} + fn fn_span<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Span { let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.as_local().expect("expected DefId is local")); - tcx.hir().span(hir_id) + let fn_decl_span = tcx.hir().span(hir_id); + let body_span = hir_body(tcx, def_id).value.span; + debug_assert_eq!(fn_decl_span.ctxt(), body_span.ctxt()); + fn_decl_span.to(body_span) } fn hir_body<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx rustc_hir::Body<'tcx> { diff --git a/compiler/rustc_mir_build/src/build/expr/as_constant.rs b/compiler/rustc_mir_build/src/build/expr/as_constant.rs index 982aefcf604..244a70f83b0 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_constant.rs @@ -21,7 +21,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let Expr { ty, temp_lifetime: _, span, kind } = expr; match kind { ExprKind::Scope { region_scope: _, lint_level: _, value } => this.as_constant(value), - ExprKind::Literal { literal, user_ty } => { + ExprKind::Literal { literal, user_ty, const_id: _ } => { let user_ty = user_ty.map(|user_ty| { this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation { span, 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 a9cc0cc2f24..9984b527ffd 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_temp.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_temp.rs @@ -76,6 +76,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { local_decl.local_info = Some(box LocalInfo::StaticRef { def_id, is_thread_local: true }); } + ExprKind::Literal { const_id: Some(def_id), .. } => { + local_decl.local_info = Some(box LocalInfo::ConstRef { def_id }); + } _ => {} } this.local_decls.push(local_decl) diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs index 313bb979a51..e55180ff4be 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs @@ -6,6 +6,7 @@ #![feature(box_syntax)] #![feature(const_fn)] #![feature(const_panic)] +#![feature(control_flow_enum)] #![feature(crate_visibility_modifier)] #![feature(bool_to_option)] #![feature(or_patterns)] diff --git a/compiler/rustc_mir_build/src/lints.rs b/compiler/rustc_mir_build/src/lints.rs index 22ce278cee0..a8d7c612a84 100644 --- a/compiler/rustc_mir_build/src/lints.rs +++ b/compiler/rustc_mir_build/src/lints.rs @@ -117,7 +117,7 @@ impl<'mir, 'tcx> TriColorVisitor<&'mir Body<'tcx>> for Search<'mir, 'tcx> { // A diverging InlineAsm is treated as non-recursing TerminatorKind::InlineAsm { destination, .. } => { if destination.is_some() { - ControlFlow::Continue + ControlFlow::CONTINUE } else { ControlFlow::Break(NonRecursive) } @@ -131,7 +131,7 @@ impl<'mir, 'tcx> TriColorVisitor<&'mir Body<'tcx>> for Search<'mir, 'tcx> { | TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } | TerminatorKind::Goto { .. } - | TerminatorKind::SwitchInt { .. } => ControlFlow::Continue, + | TerminatorKind::SwitchInt { .. } => ControlFlow::CONTINUE, } } @@ -144,7 +144,7 @@ impl<'mir, 'tcx> TriColorVisitor<&'mir Body<'tcx>> for Search<'mir, 'tcx> { } } - ControlFlow::Continue + ControlFlow::CONTINUE } fn ignore_edge(&mut self, bb: BasicBlock, target: BasicBlock) -> bool { diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index e9e49d054b8..70c7abc2654 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -247,6 +247,7 @@ fn make_mirror_unadjusted<'a, 'tcx>( hir::ExprKind::Lit(ref lit) => ExprKind::Literal { literal: cx.const_eval_literal(&lit.node, expr_ty, lit.span, false), user_ty: None, + const_id: None, }, hir::ExprKind::Binary(op, ref lhs, ref rhs) => { @@ -306,6 +307,7 @@ fn make_mirror_unadjusted<'a, 'tcx>( ExprKind::Literal { literal: cx.const_eval_literal(&lit.node, expr_ty, lit.span, true), user_ty: None, + const_id: None, } } else { ExprKind::Unary { op: UnOp::Neg, arg: arg.to_ref() } @@ -447,6 +449,7 @@ fn make_mirror_unadjusted<'a, 'tcx>( kind: ExprKind::Literal { literal: ty::Const::zero_sized(cx.tcx, ty), user_ty, + const_id: None, }, } .to_ref(), @@ -473,6 +476,7 @@ fn make_mirror_unadjusted<'a, 'tcx>( kind: ExprKind::Literal { literal: ty::Const::zero_sized(cx.tcx, ty), user_ty: None, + const_id: None, }, } .to_ref(), @@ -585,7 +589,7 @@ fn make_mirror_unadjusted<'a, 'tcx>( temp_lifetime, ty: var_ty, span: expr.span, - kind: ExprKind::Literal { literal, user_ty: None }, + kind: ExprKind::Literal { literal, user_ty: None, const_id: None }, } .to_ref() }; @@ -714,7 +718,11 @@ fn method_callee<'a, 'tcx>( temp_lifetime, ty, span, - kind: ExprKind::Literal { literal: ty::Const::zero_sized(cx.tcx(), ty), user_ty }, + kind: ExprKind::Literal { + literal: ty::Const::zero_sized(cx.tcx(), ty), + user_ty, + const_id: None, + }, } } @@ -777,6 +785,7 @@ fn convert_path_expr<'a, 'tcx>( ExprKind::Literal { literal: ty::Const::zero_sized(cx.tcx, cx.typeck_results().node_type(expr.hir_id)), user_ty, + const_id: None, } } @@ -794,6 +803,7 @@ fn convert_path_expr<'a, 'tcx>( .tcx .mk_const(ty::Const { val, ty: cx.typeck_results().node_type(expr.hir_id) }), user_ty: None, + const_id: Some(def_id), } } @@ -810,6 +820,7 @@ fn convert_path_expr<'a, 'tcx>( ty: cx.typeck_results().node_type(expr.hir_id), }), user_ty, + const_id: Some(def_id), } } diff --git a/compiler/rustc_mir_build/src/thir/mod.rs b/compiler/rustc_mir_build/src/thir/mod.rs index 2837bfa040f..4d57fd5c64f 100644 --- a/compiler/rustc_mir_build/src/thir/mod.rs +++ b/compiler/rustc_mir_build/src/thir/mod.rs @@ -273,6 +273,10 @@ crate enum ExprKind<'tcx> { Literal { literal: &'tcx Const<'tcx>, user_ty: Option<Canonical<'tcx, UserType<'tcx>>>, + /// The `DefId` of the `const` item this literal + /// was produced from, if this is not a user-written + /// literal value. + const_id: Option<DefId>, }, /// A literal containing the address of a `static`. /// diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 034442b798b..32b124970cf 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -1,22 +1,19 @@ use rustc_ast::ast::AttrStyle; use rustc_ast::token::{self, CommentKind, Token, TokenKind}; -use rustc_ast::tokenstream::IsJoint; -use rustc_data_structures::sync::Lrc; -use rustc_errors::{error_code, Applicability, DiagnosticBuilder, FatalError}; -use rustc_lexer::Base; -use rustc_lexer::{unescape, RawStrError}; +use rustc_ast::tokenstream::{Spacing, TokenStream}; +use rustc_errors::{error_code, Applicability, DiagnosticBuilder, FatalError, PResult}; +use rustc_lexer::unescape::{self, Mode}; +use rustc_lexer::{Base, DocStyle, RawStrError}; use rustc_session::parse::ParseSess; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{BytePos, Pos, Span}; -use std::char; use tracing::debug; mod tokentrees; mod unescape_error_reporting; mod unicode_chars; -use rustc_lexer::{unescape::Mode, DocStyle}; use unescape_error_reporting::{emit_unescape_error, push_escaped_char}; #[derive(Clone, Debug)] @@ -28,7 +25,17 @@ pub struct UnmatchedBrace { pub candidate_span: Option<Span>, } -crate struct StringReader<'a> { +crate fn parse_token_trees<'a>( + sess: &'a ParseSess, + src: &'a str, + start_pos: BytePos, + override_span: Option<Span>, +) -> (PResult<'a, TokenStream>, Vec<UnmatchedBrace>) { + StringReader { sess, start_pos, pos: start_pos, end_src_index: src.len(), src, override_span } + .into_token_trees() +} + +struct StringReader<'a> { sess: &'a ParseSess, /// Initial position, read-only. start_pos: BytePos, @@ -37,38 +44,18 @@ crate struct StringReader<'a> { /// Stop reading src at this index. end_src_index: usize, /// Source text to tokenize. - src: Lrc<String>, + src: &'a str, override_span: Option<Span>, } impl<'a> StringReader<'a> { - crate fn new( - sess: &'a ParseSess, - source_file: Lrc<rustc_span::SourceFile>, - override_span: Option<Span>, - ) -> Self { - let src = source_file.src.clone().unwrap_or_else(|| { - sess.span_diagnostic - .bug(&format!("cannot lex `source_file` without source: {}", source_file.name)); - }); - - StringReader { - sess, - start_pos: source_file.start_pos, - pos: source_file.start_pos, - end_src_index: src.len(), - src, - override_span, - } - } - fn mk_sp(&self, lo: BytePos, hi: BytePos) -> Span { self.override_span.unwrap_or_else(|| Span::with_root_ctxt(lo, hi)) } /// Returns the next token, and info about preceding whitespace, if any. - fn next_token(&mut self) -> (IsJoint, Token) { - let mut is_joint = IsJoint::Joint; + fn next_token(&mut self) -> (Spacing, Token) { + let mut spacing = Spacing::Joint; // Skip `#!` at the start of the file let start_src_index = self.src_index(self.pos); @@ -77,7 +64,7 @@ impl<'a> StringReader<'a> { if is_beginning_of_file { if let Some(shebang_len) = rustc_lexer::strip_shebang(text) { self.pos = self.pos + BytePos::from_usize(shebang_len); - is_joint = IsJoint::NonJoint; + spacing = Spacing::Alone; } } @@ -88,7 +75,7 @@ impl<'a> StringReader<'a> { if text.is_empty() { let span = self.mk_sp(self.pos, self.pos); - return (is_joint, Token::new(token::Eof, span)); + return (spacing, Token::new(token::Eof, span)); } let token = rustc_lexer::first_token(text); @@ -101,9 +88,9 @@ impl<'a> StringReader<'a> { match self.cook_lexer_token(token.kind, start) { Some(kind) => { let span = self.mk_sp(start, self.pos); - return (is_joint, Token::new(kind, span)); + return (spacing, Token::new(kind, span)); } - None => is_joint = IsJoint::NonJoint, + None => spacing = Spacing::Alone, } } } diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs index d5977ca3c7d..0f364bffb13 100644 --- a/compiler/rustc_parse/src/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -3,8 +3,8 @@ use super::{StringReader, UnmatchedBrace}; use rustc_ast::token::{self, DelimToken, Token}; use rustc_ast::tokenstream::{ DelimSpan, - IsJoint::{self, *}, - TokenStream, TokenTree, TreeAndJoint, + Spacing::{self, *}, + TokenStream, TokenTree, TreeAndSpacing, }; use rustc_ast_pretty::pprust::token_to_string; use rustc_data_structures::fx::FxHashMap; @@ -12,7 +12,7 @@ use rustc_errors::PResult; use rustc_span::Span; impl<'a> StringReader<'a> { - crate fn into_token_trees(self) -> (PResult<'a, TokenStream>, Vec<UnmatchedBrace>) { + pub(super) fn into_token_trees(self) -> (PResult<'a, TokenStream>, Vec<UnmatchedBrace>) { let mut tt_reader = TokenTreesReader { string_reader: self, token: Token::dummy(), @@ -77,7 +77,7 @@ impl<'a> TokenTreesReader<'a> { } } - fn parse_token_tree(&mut self) -> PResult<'a, TreeAndJoint> { + fn parse_token_tree(&mut self) -> PResult<'a, TreeAndSpacing> { let sm = self.string_reader.sess.source_map(); match self.token.kind { @@ -262,29 +262,29 @@ impl<'a> TokenTreesReader<'a> { } _ => { let tt = TokenTree::Token(self.token.take()); - let mut is_joint = self.bump(); + let mut spacing = self.bump(); if !self.token.is_op() { - is_joint = NonJoint; + spacing = Alone; } - Ok((tt, is_joint)) + Ok((tt, spacing)) } } } - fn bump(&mut self) -> IsJoint { - let (joint_to_prev, token) = self.string_reader.next_token(); + fn bump(&mut self) -> Spacing { + let (spacing, token) = self.string_reader.next_token(); self.token = token; - joint_to_prev + spacing } } #[derive(Default)] struct TokenStreamBuilder { - buf: Vec<TreeAndJoint>, + buf: Vec<TreeAndSpacing>, } impl TokenStreamBuilder { - fn push(&mut self, (tree, joint): TreeAndJoint) { + fn push(&mut self, (tree, joint): TreeAndSpacing) { if let Some((TokenTree::Token(prev_token), Joint)) = self.buf.last() { if let TokenTree::Token(token) = &tree { if let Some(glued) = prev_token.glue(token) { diff --git a/compiler/rustc_parse/src/lexer/unicode_chars.rs b/compiler/rustc_parse/src/lexer/unicode_chars.rs index 8dc0db01ecb..40e2e34aa05 100644 --- a/compiler/rustc_parse/src/lexer/unicode_chars.rs +++ b/compiler/rustc_parse/src/lexer/unicode_chars.rs @@ -332,7 +332,7 @@ const ASCII_ARRAY: &[(char, &str, Option<token::TokenKind>)] = &[ ('"', "Quotation Mark", None), ]; -crate fn check_for_substitution<'a>( +pub(super) fn check_for_substitution<'a>( reader: &StringReader<'a>, pos: BytePos, ch: char, diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 462279b0a9e..dedb9850b5a 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -8,7 +8,7 @@ use rustc_ast as ast; use rustc_ast::token::{self, DelimToken, Nonterminal, Token, TokenKind}; -use rustc_ast::tokenstream::{self, IsJoint, TokenStream, TokenTree}; +use rustc_ast::tokenstream::{self, Spacing, TokenStream, TokenTree}; use rustc_ast_pretty::pprust; use rustc_data_structures::sync::Lrc; use rustc_errors::{Diagnostic, FatalError, Level, PResult}; @@ -109,7 +109,7 @@ pub fn maybe_new_parser_from_source_str( } /// Creates a new parser, handling errors as appropriate if the file doesn't exist. -/// If a span is given, that is used on an error as the as the source of the problem. +/// If a span is given, that is used on an error as the source of the problem. pub fn new_parser_from_file<'a>(sess: &'a ParseSess, path: &Path, sp: Option<Span>) -> Parser<'a> { source_file_to_parser(sess, file_to_source_file(sess, path, sp)) } @@ -200,8 +200,13 @@ pub fn maybe_file_to_stream( source_file: Lrc<SourceFile>, override_span: Option<Span>, ) -> Result<(TokenStream, Vec<lexer::UnmatchedBrace>), Vec<Diagnostic>> { - let srdr = lexer::StringReader::new(sess, source_file, override_span); - let (token_trees, unmatched_braces) = srdr.into_token_trees(); + let src = source_file.src.as_ref().unwrap_or_else(|| { + sess.span_diagnostic + .bug(&format!("cannot lex `source_file` without source: {}", source_file.name)); + }); + + let (token_trees, unmatched_braces) = + lexer::parse_token_trees(sess, src.as_str(), source_file.start_pos, override_span); match token_trees { Ok(stream) => Ok((stream, unmatched_braces)), @@ -263,21 +268,32 @@ pub fn nt_to_tokenstream(nt: &Nonterminal, sess: &ParseSess, span: Span) -> Toke Nonterminal::NtItem(ref item) => { prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span) } + Nonterminal::NtBlock(ref block) => block.tokens.clone(), + Nonterminal::NtStmt(ref stmt) => { + // FIXME: We currently only collect tokens for `:stmt` + // matchers in `macro_rules!` macros. When we start collecting + // tokens for attributes on statements, we will need to prepend + // attributes here + stmt.tokens.clone() + } Nonterminal::NtPat(ref pat) => pat.tokens.clone(), + Nonterminal::NtTy(ref ty) => ty.tokens.clone(), Nonterminal::NtIdent(ident, is_raw) => { Some(tokenstream::TokenTree::token(token::Ident(ident.name, is_raw), ident.span).into()) } Nonterminal::NtLifetime(ident) => { Some(tokenstream::TokenTree::token(token::Lifetime(ident.name), ident.span).into()) } + Nonterminal::NtMeta(ref attr) => attr.tokens.clone(), + Nonterminal::NtPath(ref path) => path.tokens.clone(), + Nonterminal::NtVis(ref vis) => vis.tokens.clone(), Nonterminal::NtTT(ref tt) => Some(tt.clone().into()), - Nonterminal::NtExpr(ref expr) => { + Nonterminal::NtExpr(ref expr) | Nonterminal::NtLiteral(ref expr) => { if expr.tokens.is_none() { debug!("missing tokens for expr {:?}", expr); } prepend_attrs(sess, &expr.attrs, expr.tokens.as_ref(), span) } - _ => None, }; // FIXME(#43081): Avoid this pretty-print + reparse hack @@ -432,7 +448,7 @@ pub fn tokenstream_probably_equal_for_proc_macro( // issue #75734 tracks resolving this. nt_to_tokenstream(nt, sess, *span).into_trees() } else { - TokenStream::new(vec![(tree, IsJoint::NonJoint)]).into_trees() + TokenStream::new(vec![(tree, Spacing::Alone)]).into_trees() } }; diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 4e4429e461f..98f94098bfc 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -162,7 +162,7 @@ impl<'a> Parser<'a> { } else { let path = self.parse_path(PathStyle::Mod)?; let args = self.parse_attr_args()?; - ast::AttrItem { path, args } + ast::AttrItem { path, args, tokens: None } }) } diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 12efe391fb9..e2a735188f9 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -28,7 +28,7 @@ pub(super) fn dummy_arg(ident: Ident) -> Param { span: ident.span, tokens: None, }); - let ty = Ty { kind: TyKind::Err, span: ident.span, id: ast::DUMMY_NODE_ID }; + let ty = Ty { kind: TyKind::Err, span: ident.span, id: ast::DUMMY_NODE_ID, tokens: None }; Param { attrs: AttrVec::default(), id: ast::DUMMY_NODE_ID, @@ -75,7 +75,12 @@ impl RecoverQPath for Ty { Some(P(self.clone())) } fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self { - Self { span: path.span, kind: TyKind::Path(qself, path), id: ast::DUMMY_NODE_ID } + Self { + span: path.span, + kind: TyKind::Path(qself, path), + id: ast::DUMMY_NODE_ID, + tokens: None, + } } } @@ -896,7 +901,7 @@ impl<'a> Parser<'a> { ) -> PResult<'a, P<T>> { self.expect(&token::ModSep)?; - let mut path = ast::Path { segments: Vec::new(), span: DUMMY_SP }; + let mut path = ast::Path { segments: Vec::new(), span: DUMMY_SP, tokens: None }; self.parse_path_segments(&mut path.segments, T::PATH_STYLE)?; path.span = ty_span.to(self.prev_token.span); diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 9143af651df..26ca9980127 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -28,7 +28,7 @@ impl<'a> Parser<'a> { /// Parses a source module as a crate. This is the main entry point for the parser. pub fn parse_crate_mod(&mut self) -> PResult<'a, ast::Crate> { let lo = self.token.span; - let (module, attrs) = self.parse_mod(&token::Eof)?; + let (module, attrs) = self.parse_mod(&token::Eof, Unsafe::No)?; let span = lo.to(self.token.span); let proc_macros = Vec::new(); // Filled in by `proc_macro_harness::inject()`. Ok(ast::Crate { attrs, module, span, proc_macros }) @@ -36,27 +36,38 @@ impl<'a> Parser<'a> { /// Parses a `mod <foo> { ... }` or `mod <foo>;` item. fn parse_item_mod(&mut self, attrs: &mut Vec<Attribute>) -> PResult<'a, ItemInfo> { + let unsafety = self.parse_unsafety(); + self.expect_keyword(kw::Mod)?; let id = self.parse_ident()?; let (module, mut inner_attrs) = if self.eat(&token::Semi) { - Default::default() + (Mod { inner: Span::default(), unsafety, items: Vec::new(), inline: false }, Vec::new()) } else { self.expect(&token::OpenDelim(token::Brace))?; - self.parse_mod(&token::CloseDelim(token::Brace))? + self.parse_mod(&token::CloseDelim(token::Brace), unsafety)? }; attrs.append(&mut inner_attrs); Ok((id, ItemKind::Mod(module))) } /// Parses the contents of a module (inner attributes followed by module items). - pub fn parse_mod(&mut self, term: &TokenKind) -> PResult<'a, (Mod, Vec<Attribute>)> { + pub fn parse_mod( + &mut self, + term: &TokenKind, + unsafety: Unsafe, + ) -> PResult<'a, (Mod, Vec<Attribute>)> { let lo = self.token.span; let attrs = self.parse_inner_attributes()?; - let module = self.parse_mod_items(term, lo)?; + let module = self.parse_mod_items(term, lo, unsafety)?; Ok((module, attrs)) } /// Given a termination token, parses all of the items in a module. - fn parse_mod_items(&mut self, term: &TokenKind, inner_lo: Span) -> PResult<'a, Mod> { + fn parse_mod_items( + &mut self, + term: &TokenKind, + inner_lo: Span, + unsafety: Unsafe, + ) -> PResult<'a, Mod> { let mut items = vec![]; while let Some(item) = self.parse_item()? { items.push(item); @@ -75,7 +86,7 @@ impl<'a> Parser<'a> { let hi = if self.token.span.is_dummy() { inner_lo } else { self.prev_token.span }; - Ok(Mod { inner: inner_lo.to(hi), items, inline: true }) + Ok(Mod { inner: inner_lo.to(hi), unsafety, items, inline: true }) } } @@ -176,7 +187,7 @@ impl<'a> Parser<'a> { /// Error in-case a non-inherited visibility was parsed but no item followed. fn error_on_unmatched_vis(&self, vis: &Visibility) { - if let VisibilityKind::Inherited = vis.node { + if let VisibilityKind::Inherited = vis.kind { return; } let vs = pprust::vis_to_string(&vis); @@ -235,8 +246,13 @@ impl<'a> Parser<'a> { self.parse_item_extern_crate()? } else { // EXTERN BLOCK - self.parse_item_foreign_mod(attrs)? + self.parse_item_foreign_mod(attrs, Unsafe::No)? } + } else if self.is_unsafe_foreign_mod() { + // EXTERN BLOCK + let unsafety = self.parse_unsafety(); + self.expect_keyword(kw::Extern)?; + self.parse_item_foreign_mod(attrs, unsafety)? } else if self.is_static_global() { // STATIC ITEM self.bump(); // `static` @@ -256,7 +272,9 @@ impl<'a> Parser<'a> { { // IMPL ITEM self.parse_item_impl(attrs, def())? - } else if self.eat_keyword(kw::Mod) { + } else if self.check_keyword(kw::Mod) + || self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Mod]) + { // MODULE ITEM self.parse_item_mod(attrs)? } else if self.eat_keyword(kw::Type) { @@ -278,7 +296,7 @@ impl<'a> Parser<'a> { } else if self.is_macro_rules_item() { // MACRO_RULES ITEM self.parse_item_macro_rules(vis)? - } else if vis.node.is_pub() && self.isnt_macro_invocation() { + } else if vis.kind.is_pub() && self.isnt_macro_invocation() { self.recover_missing_kw_before_item()?; return Ok(None); } else if macros_allowed && self.check_path() { @@ -492,7 +510,12 @@ impl<'a> Parser<'a> { { let span = self.prev_token.span.between(self.token.span); self.struct_span_err(span, "missing trait in a trait impl").emit(); - P(Ty { kind: TyKind::Path(None, err_path(span)), span, id: DUMMY_NODE_ID }) + P(Ty { + kind: TyKind::Path(None, err_path(span)), + span, + id: DUMMY_NODE_ID, + tokens: None, + }) } else { self.parse_ty()? }; @@ -764,7 +787,7 @@ impl<'a> Parser<'a> { fn parse_use_tree(&mut self) -> PResult<'a, UseTree> { let lo = self.token.span; - let mut prefix = ast::Path { segments: Vec::new(), span: lo.shrink_to_lo() }; + let mut prefix = ast::Path { segments: Vec::new(), span: lo.shrink_to_lo(), tokens: None }; let kind = if self.check(&token::OpenDelim(token::Brace)) || self.check(&token::BinOp(token::Star)) || self.is_import_coupler() @@ -893,10 +916,14 @@ impl<'a> Parser<'a> { /// extern "C" {} /// extern {} /// ``` - fn parse_item_foreign_mod(&mut self, attrs: &mut Vec<Attribute>) -> PResult<'a, ItemInfo> { + fn parse_item_foreign_mod( + &mut self, + attrs: &mut Vec<Attribute>, + unsafety: Unsafe, + ) -> PResult<'a, ItemInfo> { let abi = self.parse_abi(); // ABI? let items = self.parse_item_list(attrs, |p| p.parse_foreign_item())?; - let module = ast::ForeignMod { abi, items }; + let module = ast::ForeignMod { unsafety, abi, items }; Ok((Ident::invalid(), ItemKind::ForeignMod(module))) } @@ -938,6 +965,15 @@ impl<'a> Parser<'a> { .emit(); } + fn is_unsafe_foreign_mod(&self) -> bool { + self.token.is_keyword(kw::Unsafe) + && self.is_keyword_ahead(1, &[kw::Extern]) + && self.look_ahead( + 2 + self.look_ahead(2, |t| t.can_begin_literal_maybe_minus() as usize), + |t| t.kind == token::OpenDelim(token::Brace), + ) + } + fn is_static_global(&mut self) -> bool { if self.check_keyword(kw::Static) { // Check if this could be a closure. @@ -1015,7 +1051,7 @@ impl<'a> Parser<'a> { // The user intended that the type be inferred, // so treat this as if the user wrote e.g. `const A: _ = expr;`. - P(Ty { kind: TyKind::Infer, span: id.span, id: ast::DUMMY_NODE_ID }) + P(Ty { kind: TyKind::Infer, span: id.span, id: ast::DUMMY_NODE_ID, tokens: None }) } /// Parses an enum declaration. @@ -1382,7 +1418,7 @@ impl<'a> Parser<'a> { /// Item macro invocations or `macro_rules!` definitions need inherited visibility. /// If that's not the case, emit an error. fn complain_if_pub_macro(&self, vis: &Visibility, macro_rules: bool) { - if let VisibilityKind::Inherited = vis.node { + if let VisibilityKind::Inherited = vis.kind { return; } @@ -1552,10 +1588,14 @@ impl<'a> Parser<'a> { // `$qual fn` or `$qual $qual`: || QUALS.iter().any(|&kw| self.check_keyword(kw)) && self.look_ahead(1, |t| { - // ...qualified and then `fn`, e.g. `const fn`. + // `$qual fn`, e.g. `const fn` or `async fn`. t.is_keyword(kw::Fn) - // Two qualifiers. This is enough. Due `async` we need to check that it's reserved. - || t.is_non_raw_ident_where(|i| QUALS.contains(&i.name) && i.is_reserved()) + // Two qualifiers `$qual $qual` is enough, e.g. `async unsafe`. + || t.is_non_raw_ident_where(|i| QUALS.contains(&i.name) + // Rule out 2015 `const async: T = val`. + && i.is_reserved() + // Rule out unsafe extern block. + && !self.is_unsafe_foreign_mod()) }) // `extern ABI fn` || self.check_keyword(kw::Extern) @@ -1567,9 +1607,9 @@ impl<'a> Parser<'a> { /// up to and including the `fn` keyword. The formal grammar is: /// /// ``` - /// Extern = "extern" StringLit ; + /// Extern = "extern" StringLit? ; /// FnQual = "const"? "async"? "unsafe"? Extern? ; - /// FnFrontMatter = FnQual? "fn" ; + /// FnFrontMatter = FnQual "fn" ; /// ``` pub(super) fn parse_fn_front_matter(&mut self) -> PResult<'a, FnHeader> { let constness = self.parse_constness(); diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 84edfecad19..5eefae3af60 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -15,14 +15,14 @@ pub use path::PathStyle; use rustc_ast::ptr::P; use rustc_ast::token::{self, DelimToken, Token, TokenKind}; -use rustc_ast::tokenstream::{self, DelimSpan, TokenStream, TokenTree, TreeAndJoint}; +use rustc_ast::tokenstream::{self, DelimSpan, TokenStream, TokenTree, TreeAndSpacing}; use rustc_ast::DUMMY_NODE_ID; use rustc_ast::{self as ast, AttrStyle, AttrVec, Const, CrateSugar, Extern, Unsafe}; use rustc_ast::{Async, MacArgs, MacDelimiter, Mutability, StrLit, Visibility, VisibilityKind}; use rustc_ast_pretty::pprust; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, FatalError, PResult}; use rustc_session::parse::ParseSess; -use rustc_span::source_map::{respan, Span, DUMMY_SP}; +use rustc_span::source_map::{Span, DUMMY_SP}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use tracing::debug; @@ -118,7 +118,7 @@ impl<'a> Drop for Parser<'a> { struct TokenCursor { frame: TokenCursorFrame, stack: Vec<TokenCursorFrame>, - cur_token: Option<TreeAndJoint>, + cur_token: Option<TreeAndSpacing>, collecting: Option<Collecting>, } @@ -136,7 +136,7 @@ struct TokenCursorFrame { struct Collecting { /// Holds the current tokens captured during the most /// recent call to `collect_tokens` - buf: Vec<TreeAndJoint>, + buf: Vec<TreeAndSpacing>, /// The depth of the `TokenCursor` stack at the time /// collection was started. When we encounter a `TokenTree::Delimited`, /// we want to record the `TokenTree::Delimited` itself, @@ -167,7 +167,7 @@ impl TokenCursor { let tree = if !self.frame.open_delim { self.frame.open_delim = true; TokenTree::open_tt(self.frame.span, self.frame.delim).into() - } else if let Some(tree) = self.frame.tree_cursor.next_with_joint() { + } else if let Some(tree) = self.frame.tree_cursor.next_with_spacing() { tree } else if !self.frame.close_delim { self.frame.close_delim = true; @@ -1022,14 +1022,22 @@ impl<'a> Parser<'a> { if self.is_crate_vis() { self.bump(); // `crate` self.sess.gated_spans.gate(sym::crate_visibility_modifier, self.prev_token.span); - return Ok(respan(self.prev_token.span, VisibilityKind::Crate(CrateSugar::JustCrate))); + return Ok(Visibility { + span: self.prev_token.span, + kind: VisibilityKind::Crate(CrateSugar::JustCrate), + tokens: None, + }); } if !self.eat_keyword(kw::Pub) { // We need a span for our `Spanned<VisibilityKind>`, but there's inherently no // keyword to grab a span from for inherited visibility; an empty span at the // beginning of the current token would seem to be the "Schelling span". - return Ok(respan(self.token.span.shrink_to_lo(), VisibilityKind::Inherited)); + return Ok(Visibility { + span: self.token.span.shrink_to_lo(), + kind: VisibilityKind::Inherited, + tokens: None, + }); } let lo = self.prev_token.span; @@ -1046,7 +1054,11 @@ impl<'a> Parser<'a> { self.bump(); // `crate` self.expect(&token::CloseDelim(token::Paren))?; // `)` let vis = VisibilityKind::Crate(CrateSugar::PubCrate); - return Ok(respan(lo.to(self.prev_token.span), vis)); + return Ok(Visibility { + span: lo.to(self.prev_token.span), + kind: vis, + tokens: None, + }); } else if self.is_keyword_ahead(1, &[kw::In]) { // Parse `pub(in path)`. self.bump(); // `(` @@ -1054,7 +1066,11 @@ impl<'a> Parser<'a> { let path = self.parse_path(PathStyle::Mod)?; // `path` self.expect(&token::CloseDelim(token::Paren))?; // `)` let vis = VisibilityKind::Restricted { path: P(path), id: ast::DUMMY_NODE_ID }; - return Ok(respan(lo.to(self.prev_token.span), vis)); + return Ok(Visibility { + span: lo.to(self.prev_token.span), + kind: vis, + tokens: None, + }); } else if self.look_ahead(2, |t| t == &token::CloseDelim(token::Paren)) && self.is_keyword_ahead(1, &[kw::Super, kw::SelfLower]) { @@ -1063,7 +1079,11 @@ impl<'a> Parser<'a> { let path = self.parse_path(PathStyle::Mod)?; // `super`/`self` self.expect(&token::CloseDelim(token::Paren))?; // `)` let vis = VisibilityKind::Restricted { path: P(path), id: ast::DUMMY_NODE_ID }; - return Ok(respan(lo.to(self.prev_token.span), vis)); + return Ok(Visibility { + span: lo.to(self.prev_token.span), + kind: vis, + tokens: None, + }); } else if let FollowedByType::No = fbt { // Provide this diagnostic if a type cannot follow; // in particular, if this is not a tuple struct. @@ -1072,7 +1092,7 @@ impl<'a> Parser<'a> { } } - Ok(respan(lo, VisibilityKind::Public)) + Ok(Visibility { span: lo, kind: VisibilityKind::Public, tokens: None }) } /// Recovery for e.g. `pub(something) fn ...` or `struct X { pub(something) y: Z }` @@ -1154,7 +1174,7 @@ impl<'a> Parser<'a> { f: impl FnOnce(&mut Self) -> PResult<'a, R>, ) -> PResult<'a, (R, TokenStream)> { // Record all tokens we parse when parsing this item. - let tokens: Vec<TreeAndJoint> = self.token_cursor.cur_token.clone().into_iter().collect(); + let tokens: Vec<TreeAndSpacing> = self.token_cursor.cur_token.clone().into_iter().collect(); debug!("collect_tokens: starting with {:?}", tokens); // We need special handling for the case where `collect_tokens` is called diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index f40cd1131d2..15660fd574c 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -111,11 +111,28 @@ impl<'a> Parser<'a> { return Err(self.struct_span_err(self.token.span, "expected an item keyword")); } }, - NonterminalKind::Block => token::NtBlock(self.parse_block()?), - NonterminalKind::Stmt => match self.parse_stmt()? { - Some(s) => token::NtStmt(s), - None => return Err(self.struct_span_err(self.token.span, "expected a statement")), - }, + NonterminalKind::Block => { + let (mut block, tokens) = self.collect_tokens(|this| this.parse_block())?; + // We have have eaten an NtBlock, which could already have tokens + if block.tokens.is_none() { + block.tokens = Some(tokens); + } + token::NtBlock(block) + } + NonterminalKind::Stmt => { + let (stmt, tokens) = self.collect_tokens(|this| this.parse_stmt())?; + match stmt { + Some(mut s) => { + if s.tokens.is_none() { + s.tokens = Some(tokens); + } + token::NtStmt(s) + } + None => { + return Err(self.struct_span_err(self.token.span, "expected a statement")); + } + } + } NonterminalKind::Pat => { let (mut pat, tokens) = self.collect_tokens(|this| this.parse_pat(None))?; // We have have eaten an NtPat, which could already have tokens @@ -133,8 +150,23 @@ impl<'a> Parser<'a> { } token::NtExpr(expr) } - NonterminalKind::Literal => token::NtLiteral(self.parse_literal_maybe_minus()?), - NonterminalKind::Ty => token::NtTy(self.parse_ty()?), + NonterminalKind::Literal => { + let (mut lit, tokens) = + self.collect_tokens(|this| this.parse_literal_maybe_minus())?; + // We have have eaten a nonterminal, which could already have tokens + if lit.tokens.is_none() { + lit.tokens = Some(tokens); + } + token::NtLiteral(lit) + } + NonterminalKind::Ty => { + let (mut ty, tokens) = self.collect_tokens(|this| this.parse_ty())?; + // We have an eaten an NtTy, which could already have tokens + if ty.tokens.is_none() { + ty.tokens = Some(tokens); + } + token::NtTy(ty) + } // this could be handled like a token, since it is one NonterminalKind::Ident => { if let Some((ident, is_raw)) = get_macro_ident(&self.token) { @@ -146,10 +178,33 @@ impl<'a> Parser<'a> { return Err(self.struct_span_err(self.token.span, msg)); } } - NonterminalKind::Path => token::NtPath(self.parse_path(PathStyle::Type)?), - NonterminalKind::Meta => token::NtMeta(P(self.parse_attr_item()?)), + NonterminalKind::Path => { + let (mut path, tokens) = + self.collect_tokens(|this| this.parse_path(PathStyle::Type))?; + // We have have eaten an NtPath, which could already have tokens + if path.tokens.is_none() { + path.tokens = Some(tokens); + } + token::NtPath(path) + } + NonterminalKind::Meta => { + let (mut attr, tokens) = self.collect_tokens(|this| this.parse_attr_item())?; + // We may have eaten a nonterminal, which could already have tokens + if attr.tokens.is_none() { + attr.tokens = Some(tokens); + } + token::NtMeta(P(attr)) + } NonterminalKind::TT => token::NtTT(self.parse_token_tree()), - NonterminalKind::Vis => token::NtVis(self.parse_visibility(FollowedByType::Yes)?), + NonterminalKind::Vis => { + let (mut vis, tokens) = + self.collect_tokens(|this| this.parse_visibility(FollowedByType::Yes))?; + // We may have etan an `NtVis`, which could already have tokens + if vis.tokens.is_none() { + vis.tokens = Some(tokens); + } + token::NtVis(vis) + } NonterminalKind::Lifetime => { if self.check_lifetime() { token::NtLifetime(self.expect_lifetime().ident) diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 54b4df8613f..66ce015d02e 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -64,7 +64,7 @@ impl<'a> Parser<'a> { path_span = path_lo.to(self.prev_token.span); } else { path_span = self.token.span.to(self.token.span); - path = ast::Path { segments: Vec::new(), span: path_span }; + path = ast::Path { segments: Vec::new(), span: path_span, tokens: None }; } // See doc comment for `unmatched_angle_bracket_count`. @@ -81,7 +81,10 @@ impl<'a> Parser<'a> { let qself = QSelf { ty, path_span, position: path.segments.len() }; self.parse_path_segments(&mut path.segments, style)?; - Ok((qself, Path { segments: path.segments, span: lo.to(self.prev_token.span) })) + Ok(( + qself, + Path { segments: path.segments, span: lo.to(self.prev_token.span), tokens: None }, + )) } /// Recover from an invalid single colon, when the user likely meant a qualified path. @@ -144,7 +147,7 @@ impl<'a> Parser<'a> { } self.parse_path_segments(&mut segments, style)?; - Ok(Path { segments, span: lo.to(self.prev_token.span) }) + Ok(Path { segments, span: lo.to(self.prev_token.span), tokens: None }) } pub(super) fn parse_path_segments( diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 947ca6b5bd5..64b959e8325 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -411,11 +411,11 @@ impl<'a> Parser<'a> { } pub(super) fn mk_block(&self, stmts: Vec<Stmt>, rules: BlockCheckMode, span: Span) -> P<Block> { - P(Block { stmts, id: DUMMY_NODE_ID, rules, span }) + P(Block { stmts, id: DUMMY_NODE_ID, rules, span, tokens: None }) } pub(super) fn mk_stmt(&self, span: Span, kind: StmtKind) -> Stmt { - Stmt { id: DUMMY_NODE_ID, kind, span } + Stmt { id: DUMMY_NODE_ID, kind, span, tokens: None } } fn mk_stmt_err(&self, span: Span) -> Stmt { diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 4356850818e..259764a317d 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -626,6 +626,6 @@ impl<'a> Parser<'a> { } pub(super) fn mk_ty(&self, span: Span, kind: TyKind) -> P<Ty> { - P(Ty { kind, span, id: ast::DUMMY_NODE_ID }) + P(Ty { kind, span, id: ast::DUMMY_NODE_ID, tokens: None }) } } diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 761724be57d..565313902a4 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -218,7 +218,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { speculative: bool, ) -> Result<ty::Visibility, VisResolutionError<'ast>> { let parent_scope = &self.parent_scope; - match vis.node { + match vis.kind { ast::VisibilityKind::Public => Ok(ty::Visibility::Public), ast::VisibilityKind::Crate(..) => { Ok(ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX))) @@ -796,23 +796,26 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { vis }; + let mut ret_fields = Vec::with_capacity(vdata.fields().len()); + for field in vdata.fields() { // NOTE: The field may be an expansion placeholder, but expansion sets // correct visibilities for unnamed field placeholders specifically, so the // constructor visibility should still be determined correctly. - if let Ok(field_vis) = self.resolve_visibility_speculative(&field.vis, true) - { - if ctor_vis.is_at_least(field_vis, &*self.r) { - ctor_vis = field_vis; - } + let field_vis = self + .resolve_visibility_speculative(&field.vis, true) + .unwrap_or(ty::Visibility::Public); + if ctor_vis.is_at_least(field_vis, &*self.r) { + ctor_vis = field_vis; } + ret_fields.push(field_vis); } let ctor_res = Res::Def( DefKind::Ctor(CtorOf::Struct, CtorKind::from_ast(vdata)), self.r.local_def_id(ctor_node_id).to_def_id(), ); self.r.define(parent, ident, ValueNS, (ctor_res, ctor_vis, sp, expansion)); - self.r.struct_constructors.insert(def_id, (ctor_res, ctor_vis)); + self.r.struct_constructors.insert(def_id, (ctor_res, ctor_vis, ret_fields)); } } @@ -964,7 +967,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { Res::Def(DefKind::Ctor(CtorOf::Struct, ..), def_id) => { let parent = cstore.def_key(def_id).parent; if let Some(struct_def_id) = parent.map(|index| DefId { index, ..def_id }) { - self.r.struct_constructors.insert(struct_def_id, (res, vis)); + self.r.struct_constructors.insert(struct_def_id, (res, vis, vec![])); } } _ => {} diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 5624a6b6acc..89ce89b2e9a 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -105,7 +105,7 @@ impl<'a, 'b> Visitor<'a> for UnusedImportCheckVisitor<'a, 'b> { // because this means that they were generated in some fashion by the // compiler and we don't need to consider them. if let ast::ItemKind::Use(..) = item.kind { - if item.vis.node.is_pub() || item.span.is_dummy() { + if item.vis.kind.is_pub() || item.span.is_dummy() { return; } } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 48e1068b8da..7a0503d68f3 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -794,7 +794,7 @@ impl<'a> Resolver<'a> { } segms.push(ast::PathSegment::from_ident(ident)); - let path = Path { span: name_binding.span, segments: segms }; + let path = Path { span: name_binding.span, segments: segms, tokens: None }; let did = match res { Res::Def(DefKind::Ctor(..), did) => this.parent(did), _ => res.opt_def_id(), diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 2b2123e295d..07f36c7b7ad 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -188,7 +188,7 @@ crate enum PathSource<'a> { // Paths in struct expressions and patterns `Path { .. }`. Struct, // Paths in tuple struct patterns `Path(..)`. - TupleStruct(Span), + TupleStruct(Span, &'a [Span]), // `m::A::B` in `<T as m::A>::B::C`. TraitItem(Namespace), } @@ -197,7 +197,7 @@ impl<'a> PathSource<'a> { fn namespace(self) -> Namespace { match self { PathSource::Type | PathSource::Trait(_) | PathSource::Struct => TypeNS, - PathSource::Expr(..) | PathSource::Pat | PathSource::TupleStruct(_) => ValueNS, + PathSource::Expr(..) | PathSource::Pat | PathSource::TupleStruct(..) => ValueNS, PathSource::TraitItem(ns) => ns, } } @@ -208,7 +208,7 @@ impl<'a> PathSource<'a> { | PathSource::Expr(..) | PathSource::Pat | PathSource::Struct - | PathSource::TupleStruct(_) => true, + | PathSource::TupleStruct(..) => true, PathSource::Trait(_) | PathSource::TraitItem(..) => false, } } @@ -219,7 +219,7 @@ impl<'a> PathSource<'a> { PathSource::Trait(_) => "trait", PathSource::Pat => "unit struct, unit variant or constant", PathSource::Struct => "struct, variant or union type", - PathSource::TupleStruct(_) => "tuple struct or tuple variant", + PathSource::TupleStruct(..) => "tuple struct or tuple variant", PathSource::TraitItem(ns) => match ns { TypeNS => "associated type", ValueNS => "method or associated constant", @@ -305,7 +305,7 @@ impl<'a> PathSource<'a> { | Res::SelfCtor(..) => true, _ => false, }, - PathSource::TupleStruct(_) => match res { + PathSource::TupleStruct(..) => match res { Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) | Res::SelfCtor(..) => true, _ => false, }, @@ -340,8 +340,8 @@ impl<'a> PathSource<'a> { (PathSource::Struct, false) => error_code!(E0422), (PathSource::Expr(..), true) => error_code!(E0423), (PathSource::Expr(..), false) => error_code!(E0425), - (PathSource::Pat | PathSource::TupleStruct(_), true) => error_code!(E0532), - (PathSource::Pat | PathSource::TupleStruct(_), false) => error_code!(E0531), + (PathSource::Pat | PathSource::TupleStruct(..), true) => error_code!(E0532), + (PathSource::Pat | PathSource::TupleStruct(..), false) => error_code!(E0531), (PathSource::TraitItem(..), true) => error_code!(E0575), (PathSource::TraitItem(..), false) => error_code!(E0576), } @@ -411,7 +411,7 @@ struct LateResolutionVisitor<'a, 'b, 'ast> { } /// Walks the whole crate in DFS order, visiting each item, resolving names as it goes. -impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { +impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { fn visit_item(&mut self, item: &'ast Item) { let prev = replace(&mut self.diagnostic_metadata.current_item, Some(item)); // Always report errors in items we just entered. @@ -659,7 +659,7 @@ impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { } } -impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { +impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { fn new(resolver: &'b mut Resolver<'a>) -> LateResolutionVisitor<'a, 'b, 'ast> { // During late resolution we only track the module component of the parent scope, // although it may be useful to track other components as well for diagnostics. @@ -1539,8 +1539,16 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { .unwrap_or_else(|| self.fresh_binding(ident, pat.id, pat_src, bindings)); self.r.record_partial_res(pat.id, PartialRes::new(res)); } - PatKind::TupleStruct(ref path, ..) => { - self.smart_resolve_path(pat.id, None, path, PathSource::TupleStruct(pat.span)); + PatKind::TupleStruct(ref path, ref sub_patterns) => { + self.smart_resolve_path( + pat.id, + None, + path, + PathSource::TupleStruct( + pat.span, + self.r.arenas.alloc_pattern_spans(sub_patterns.iter().map(|p| p.span)), + ), + ); } PatKind::Path(ref qself, ref path) => { self.smart_resolve_path(pat.id, qself.as_ref(), path, PathSource::Pat); @@ -1967,7 +1975,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { if qself.is_none() { let path_seg = |seg: &Segment| PathSegment::from_ident(seg.ident); - let path = Path { segments: path.iter().map(path_seg).collect(), span }; + let path = Path { segments: path.iter().map(path_seg).collect(), span, tokens: None }; if let Ok((_, res)) = self.r.resolve_macro_path(&path, None, &self.parent_scope, false, false) { diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 8cb6b6553ff..9b5650c260c 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -83,13 +83,14 @@ fn import_candidate_to_enum_paths(suggestion: &ImportSuggestion) -> (String, Str let enum_path = ast::Path { span: suggestion.path.span, segments: suggestion.path.segments[0..path_len - 1].to_vec(), + tokens: None, }; let enum_path_string = path_names_to_string(&enum_path); (variant_path_string, enum_path_string) } -impl<'a> LateResolutionVisitor<'a, '_, '_> { +impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { fn def_span(&self, def_id: DefId) -> Option<Span> { match def_id.krate { LOCAL_CRATE => self.r.opt_span(def_id), @@ -622,12 +623,12 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> { ); } } - PathSource::Expr(_) | PathSource::TupleStruct(_) | PathSource::Pat => { + PathSource::Expr(_) | PathSource::TupleStruct(..) | PathSource::Pat => { let span = match &source { PathSource::Expr(Some(Expr { span, kind: ExprKind::Call(_, _), .. })) - | PathSource::TupleStruct(span) => { + | PathSource::TupleStruct(span, _) => { // We want the main underline to cover the suggested code as well for // cleaner output. err.set_span(*span); @@ -639,7 +640,7 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> { err.span_label(span, &format!("`{}` defined here", path_str)); } let (tail, descr, applicability) = match source { - PathSource::Pat | PathSource::TupleStruct(_) => { + PathSource::Pat | PathSource::TupleStruct(..) => { ("", "pattern", Applicability::MachineApplicable) } _ => (": val", "literal", Applicability::HasPlaceholders), @@ -704,7 +705,7 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> { } ( Res::Def(DefKind::Enum, def_id), - PathSource::TupleStruct(_) | PathSource::Expr(..), + PathSource::TupleStruct(..) | PathSource::Expr(..), ) => { if self .diagnostic_metadata @@ -744,15 +745,50 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> { } } (Res::Def(DefKind::Struct, def_id), _) if ns == ValueNS => { - if let Some((ctor_def, ctor_vis)) = self.r.struct_constructors.get(&def_id).cloned() + if let Some((ctor_def, ctor_vis, fields)) = + self.r.struct_constructors.get(&def_id).cloned() { let accessible_ctor = self.r.is_accessible_from(ctor_vis, self.parent_scope.module); if is_expected(ctor_def) && !accessible_ctor { - err.span_label( - span, - "constructor is not visible here due to private fields".to_string(), - ); + let mut better_diag = false; + if let PathSource::TupleStruct(_, pattern_spans) = source { + if pattern_spans.len() > 0 && fields.len() == pattern_spans.len() { + let non_visible_spans: Vec<Span> = fields + .iter() + .zip(pattern_spans.iter()) + .filter_map(|(vis, span)| { + match self + .r + .is_accessible_from(*vis, self.parent_scope.module) + { + true => None, + false => Some(*span), + } + }) + .collect(); + // Extra check to be sure + if non_visible_spans.len() > 0 { + let mut m: rustc_span::MultiSpan = + non_visible_spans.clone().into(); + non_visible_spans.into_iter().for_each(|s| { + m.push_span_label(s, "private field".to_string()) + }); + err.span_note( + m, + "constructor is not visible here due to private fields", + ); + better_diag = true; + } + } + } + + if !better_diag { + err.span_label( + span, + "constructor is not visible here due to private fields".to_string(), + ); + } } } else { bad_struct_syntax_suggestion(def_id); @@ -1065,7 +1101,8 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> { path_segments.push(ast::PathSegment::from_ident(ident)); let module_def_id = module.def_id().unwrap(); if module_def_id == def_id { - let path = Path { span: name_binding.span, segments: path_segments }; + let path = + Path { span: name_binding.span, segments: path_segments, tokens: None }; result = Some(( module, ImportSuggestion { @@ -1095,7 +1132,7 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> { if let Res::Def(DefKind::Variant, _) = name_binding.res() { let mut segms = enum_import_suggestion.path.segments.clone(); segms.push(ast::PathSegment::from_ident(ident)); - variants.push(Path { span: name_binding.span, segments: segms }); + variants.push(Path { span: name_binding.span, segments: segms, tokens: None }); } }); variants @@ -1387,7 +1424,7 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { } } } - introduce_suggestion.push((*for_span, for_sugg.to_string())); + introduce_suggestion.push((*for_span, for_sugg)); introduce_suggestion.push((span, formatter(<_name))); err.multipart_suggestion(&msg, introduce_suggestion, Applicability::MaybeIncorrect); } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 50729086ec6..0f5b9b51816 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1005,7 +1005,8 @@ pub struct Resolver<'a> { /// Table for mapping struct IDs into struct constructor IDs, /// it's not used during normal resolution, only for better error reporting. - struct_constructors: DefIdMap<(Res, ty::Visibility)>, + /// Also includes of list of each fields visibility + struct_constructors: DefIdMap<(Res, ty::Visibility, Vec<ty::Visibility>)>, /// Features enabled for this crate. active_features: FxHashSet<Symbol>, @@ -1042,6 +1043,7 @@ pub struct ResolverArenas<'a> { name_resolutions: TypedArena<RefCell<NameResolution<'a>>>, macro_rules_bindings: TypedArena<MacroRulesBinding<'a>>, ast_paths: TypedArena<ast::Path>, + pattern_spans: TypedArena<Span>, } impl<'a> ResolverArenas<'a> { @@ -1073,6 +1075,9 @@ impl<'a> ResolverArenas<'a> { fn alloc_ast_paths(&'a self, paths: &[ast::Path]) -> &'a [ast::Path] { self.ast_paths.alloc_from_iter(paths.iter().cloned()) } + fn alloc_pattern_spans(&'a self, spans: impl Iterator<Item = Span>) -> &'a [Span] { + self.pattern_spans.alloc_from_iter(spans) + } } impl<'a> AsMut<Resolver<'a>> for Resolver<'a> { @@ -2413,7 +2418,14 @@ impl<'a> Resolver<'a> { (format!("maybe a missing crate `{}`?", ident), None) } } else if i == 0 { - (format!("use of undeclared type or module `{}`", ident), None) + if ident + .name + .with(|n| n.chars().next().map_or(false, |c| c.is_ascii_uppercase())) + { + (format!("use of undeclared type `{}`", ident), None) + } else { + (format!("use of undeclared crate or module `{}`", ident), None) + } } else { let mut msg = format!("could not find `{}` in `{}`", ident, path[i - 1].ident); @@ -3182,6 +3194,7 @@ impl<'a> Resolver<'a> { .chain(path_str.split("::").skip(1).map(Ident::from_str)) .map(|i| self.new_ast_path_segment(i)) .collect(), + tokens: None, } } else { ast::Path { @@ -3191,6 +3204,7 @@ impl<'a> Resolver<'a> { .map(Ident::from_str) .map(|i| self.new_ast_path_segment(i)) .collect(), + tokens: None, } }; let module = self.get_module(module_id); diff --git a/compiler/rustc_save_analysis/src/lib.rs b/compiler/rustc_save_analysis/src/lib.rs index c95e7e193be..032d7cb3ed6 100644 --- a/compiler/rustc_save_analysis/src/lib.rs +++ b/compiler/rustc_save_analysis/src/lib.rs @@ -438,7 +438,7 @@ impl<'tcx> SaveContext<'tcx> { .next() .map(|item| item.def_id); } - qualname.push_str(">"); + qualname.push('>'); (qualname, trait_id, decl_id, docs, attrs) } diff --git a/compiler/rustc_save_analysis/src/sig.rs b/compiler/rustc_save_analysis/src/sig.rs index 6dd7f89d594..747e198cd93 100644 --- a/compiler/rustc_save_analysis/src/sig.rs +++ b/compiler/rustc_save_analysis/src/sig.rs @@ -497,7 +497,7 @@ impl<'hir> Sig for hir::Item<'hir> { sig.text.push_str(&bounds_to_string(bounds)); } // FIXME where clause - sig.text.push_str(";"); + sig.text.push(';'); Ok(sig) } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 19cd2385992..4aecb35294a 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -581,9 +581,9 @@ impl OutputFilenames { if !ext.is_empty() { if !extension.is_empty() { - extension.push_str("."); + extension.push('.'); extension.push_str(RUST_CGU_EXT); - extension.push_str("."); + extension.push('.'); } extension.push_str(ext); diff --git a/compiler/rustc_session/src/lint/builtin.rs b/compiler/rustc_session/src/lint/builtin.rs index 2db4d2a7f51..a9deaaae0da 100644 --- a/compiler/rustc_session/src/lint/builtin.rs +++ b/compiler/rustc_session/src/lint/builtin.rs @@ -233,6 +233,12 @@ declare_lint! { } declare_lint! { + pub CONST_ITEM_MUTATION, + Warn, + "detects attempts to mutate a `const` item", +} + +declare_lint! { pub SAFE_PACKED_BORROWS, Warn, "safe borrows of fields of packed structs were erroneously allowed", @@ -539,6 +545,16 @@ declare_lint! { }; } +declare_lint! { + pub CONST_EVALUATABLE_UNCHECKED, + Warn, + "detects a generic constant is used in a type without a emitting a warning", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #76200 <https://github.com/rust-lang/rust/issues/76200>", + edition: None, + }; +} + declare_lint_pass! { /// Does nothing as a lint pass, but registers some `Lint`s /// that are used by other parts of the compiler. @@ -572,6 +588,7 @@ declare_lint_pass! { CONST_ERR, RENAMED_AND_REMOVED_LINTS, UNALIGNED_REFERENCES, + CONST_ITEM_MUTATION, SAFE_PACKED_BORROWS, PATTERNS_IN_FNS_WITHOUT_BODY, LATE_BOUND_LIFETIME_ARGUMENTS, @@ -612,6 +629,7 @@ declare_lint_pass! { UNSAFE_OP_IN_UNSAFE_FN, INCOMPLETE_INCLUDE, CENUM_IMPL_DROP_CAST, + CONST_EVALUATABLE_UNCHECKED, ] } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index ad36fa76986..c5050dbea73 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -850,6 +850,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "enable the experimental Chalk-based trait solving engine"), codegen_backend: Option<String> = (None, parse_opt_string, [TRACKED], "the backend to use"), + combine_cgu: bool = (false, parse_bool, [TRACKED], + "combine CGUs into a single one"), crate_attr: Vec<String> = (Vec::new(), parse_string_push, [TRACKED], "inject the given attribute in the crate"), debug_macros: bool = (false, parse_bool, [TRACKED], @@ -907,6 +909,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "force all crates to be `rustc_private` unstable (default: no)"), fuel: Option<(String, u64)> = (None, parse_optimization_fuel, [TRACKED], "set the optimization fuel quota for a crate"), + graphviz_dark_mode: bool = (false, parse_bool, [UNTRACKED], + "use dark-themed colors in graphviz output (default: no)"), hir_stats: bool = (false, parse_bool, [UNTRACKED], "print some statistics about AST and HIR (default: no)"), human_readable_cgu_names: bool = (false, parse_bool, [TRACKED], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index ff22b4ce4ad..974f4c31bb6 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -237,6 +237,14 @@ enum DiagnosticBuilderMethod { // Add more variants as needed to support one-time diagnostics. } +/// Trait implemented by error types. This should not be implemented manually. Instead, use +/// `#[derive(SessionDiagnostic)]` -- see [rustc_macros::SessionDiagnostic]. +pub trait SessionDiagnostic<'a> { + /// Write out as a diagnostic out of `sess`. + #[must_use] + fn into_diagnostic(self, sess: &'a Session) -> DiagnosticBuilder<'a>; +} + /// Diagnostic message ID, used by `Session.one_time_diagnostics` to avoid /// emitting the same message more than once. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] @@ -392,6 +400,9 @@ impl Session { pub fn err(&self, msg: &str) { self.diagnostic().err(msg) } + pub fn emit_err<'a>(&'a self, err: impl SessionDiagnostic<'a>) { + err.into_diagnostic(self).emit() + } pub fn err_count(&self) -> usize { self.diagnostic().err_count() } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 5092b945f72..407663e5757 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -348,6 +348,7 @@ symbols! { const_compare_raw_pointers, const_constructor, const_eval_limit, + const_evaluatable_checked, const_extern_fn, const_fn, const_fn_transmute, diff --git a/compiler/rustc_target/src/spec/avr_gnu_base.rs b/compiler/rustc_target/src/spec/avr_gnu_base.rs index ff559c2bfd6..527a322d56a 100644 --- a/compiler/rustc_target/src/spec/avr_gnu_base.rs +++ b/compiler/rustc_target/src/spec/avr_gnu_base.rs @@ -45,6 +45,8 @@ pub fn target(target_cpu: String) -> TargetResult { late_link_args: vec![(LinkerFlavor::Gcc, vec!["-lgcc".to_owned()])] .into_iter() .collect(), + max_atomic_width: Some(0), + atomic_cas: false, ..TargetOptions::default() }, }) diff --git a/compiler/rustc_target/src/spec/wasm32_base.rs b/compiler/rustc_target/src/spec/wasm32_base.rs index 62fc8f06183..a7957d84cbe 100644 --- a/compiler/rustc_target/src/spec/wasm32_base.rs +++ b/compiler/rustc_target/src/spec/wasm32_base.rs @@ -83,6 +83,7 @@ pub fn options() -> TargetOptions { dll_prefix: String::new(), dll_suffix: ".wasm".to_string(), linker_is_gnu: false, + eh_frame_header: false, max_atomic_width: Some(64), diff --git a/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs b/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs index fd55a0fc6a1..fcb2af0005f 100644 --- a/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs +++ b/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs @@ -22,7 +22,7 @@ pub fn opts() -> TargetOptions { "-lmingw32".to_string(), ]; late_link_args.insert(LinkerFlavor::Gcc, mingw_libs.clone()); - late_link_args.insert(LinkerFlavor::Lld(LldFlavor::Ld), mingw_libs.clone()); + late_link_args.insert(LinkerFlavor::Lld(LldFlavor::Ld), mingw_libs); TargetOptions { executables: false, diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs new file mode 100644 index 00000000000..fdb87c085b5 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -0,0 +1,78 @@ +use rustc_hir::def::DefKind; +use rustc_infer::infer::InferCtxt; +use rustc_middle::mir::interpret::ErrorHandled; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::{self, TypeFoldable}; +use rustc_session::lint; +use rustc_span::def_id::DefId; +use rustc_span::Span; + +pub fn is_const_evaluatable<'cx, 'tcx>( + infcx: &InferCtxt<'cx, 'tcx>, + def: ty::WithOptConstParam<DefId>, + substs: SubstsRef<'tcx>, + param_env: ty::ParamEnv<'tcx>, + span: Span, +) -> Result<(), ErrorHandled> { + debug!("is_const_evaluatable({:?}, {:?})", def, substs); + if infcx.tcx.features().const_evaluatable_checked { + // FIXME(const_evaluatable_checked): Actually look into generic constants to + // implement const equality. + for pred in param_env.caller_bounds() { + match pred.skip_binders() { + ty::PredicateAtom::ConstEvaluatable(b_def, b_substs) => { + debug!("is_const_evaluatable: caller_bound={:?}, {:?}", b_def, b_substs); + if b_def == def && b_substs == substs { + debug!("is_const_evaluatable: caller_bound ~~> ok"); + return Ok(()); + } + } + _ => {} // don't care + } + } + } + + let future_compat_lint = || { + if let Some(local_def_id) = def.did.as_local() { + infcx.tcx.struct_span_lint_hir( + lint::builtin::CONST_EVALUATABLE_UNCHECKED, + infcx.tcx.hir().local_def_id_to_hir_id(local_def_id), + span, + |err| { + err.build("cannot use constants which depend on generic parameters in types") + .emit(); + }, + ); + } + }; + + // FIXME: We should only try to evaluate a given constant here if it is fully concrete + // as we don't want to allow things like `[u8; std::mem::size_of::<*mut T>()]`. + // + // We previously did not check this, so we only emit a future compat warning if + // const evaluation succeeds and the given constant is still polymorphic for now + // and hopefully soon change this to an error. + // + // See #74595 for more details about this. + let concrete = infcx.const_eval_resolve(param_env, def, substs, None, Some(span)); + + if concrete.is_ok() && substs.has_param_types_or_consts() { + match infcx.tcx.def_kind(def.did) { + DefKind::AnonConst => { + let mir_body = if let Some(def) = def.as_const_arg() { + infcx.tcx.optimized_mir_of_const_arg(def) + } else { + infcx.tcx.optimized_mir(def.did) + }; + + if mir_body.is_polymorphic { + future_compat_lint(); + } + } + _ => future_compat_lint(), + } + } + + debug!(?concrete, "is_const_evaluatable"); + concrete.map(drop) +} diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index a5c6dc042ab..4818022bf62 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -10,6 +10,7 @@ use rustc_middle::ty::ToPredicate; use rustc_middle::ty::{self, Binder, Const, Ty, TypeFoldable}; use std::marker::PhantomData; +use super::const_evaluatable; use super::project; use super::select::SelectionContext; use super::wf; @@ -458,15 +459,15 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { } ty::PredicateAtom::ConstEvaluatable(def_id, substs) => { - match self.selcx.infcx().const_eval_resolve( - obligation.param_env, + match const_evaluatable::is_const_evaluatable( + self.selcx.infcx(), def_id, substs, - None, - Some(obligation.cause.span), + obligation.param_env, + obligation.cause.span, ) { - Ok(_) => ProcessResult::Changed(vec![]), - Err(err) => ProcessResult::Error(CodeSelectionError(ConstEvalFailure(err))), + Ok(()) => ProcessResult::Changed(vec![]), + Err(e) => ProcessResult::Error(CodeSelectionError(ConstEvalFailure(e))), } } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index fe406e88c52..49dac873cde 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -7,6 +7,7 @@ pub mod auto_trait; mod chalk_fulfill; pub mod codegen; mod coherence; +mod const_evaluatable; mod engine; pub mod error_reporting; mod fulfill; diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 4258d8e3010..7e8e2baa8a1 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -6,6 +6,7 @@ use self::EvaluationResult::*; use self::SelectionCandidate::*; use super::coherence::{self, Conflict}; +use super::const_evaluatable; use super::project; use super::project::normalize_with_depth_to; use super::util; @@ -542,14 +543,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ty::PredicateAtom::ConstEvaluatable(def_id, substs) => { - match self.tcx().const_eval_resolve( - obligation.param_env, + match const_evaluatable::is_const_evaluatable( + self.infcx, def_id, substs, - None, - None, + obligation.param_env, + obligation.cause.span, ) { - Ok(_) => Ok(EvaluatedToOk), + Ok(()) => Ok(EvaluatedToOk), Err(ErrorHandled::TooGeneric) => Ok(EvaluatedToAmbig), Err(_) => Ok(EvaluatedToErr), } diff --git a/compiler/rustc_typeck/Cargo.toml b/compiler/rustc_typeck/Cargo.toml index 0a6bfaef431..e3ba0bea7e8 100644 --- a/compiler/rustc_typeck/Cargo.toml +++ b/compiler/rustc_typeck/Cargo.toml @@ -11,6 +11,7 @@ doctest = false [dependencies] rustc_arena = { path = "../rustc_arena" } tracing = "0.1" +rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_attr = { path = "../rustc_attr" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs index 84dab6de958..b54de1d0916 100644 --- a/compiler/rustc_typeck/src/astconv/generics.rs +++ b/compiler/rustc_typeck/src/astconv/generics.rs @@ -1,8 +1,9 @@ use crate::astconv::{ AstConv, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, GenericArgPosition, }; +use crate::errors::AssocTypeBindingNotAllowed; use rustc_ast::ast::ParamKindOrd; -use rustc_errors::{pluralize, struct_span_err, DiagnosticId, ErrorReported}; +use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorReported}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::{GenericArg, GenericArgs}; @@ -367,7 +368,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } if position != GenericArgPosition::Type && !args.bindings.is_empty() { - Self::prohibit_assoc_ty_binding(tcx, args.bindings[0].span); + AstConv::prohibit_assoc_ty_binding(tcx, args.bindings[0].span); } let explicit_late_bound = @@ -392,7 +393,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } if silent { - return Err(true); + return Err((0i32, None)); } // Unfortunately lifetime and type parameter mismatches are typically styled @@ -441,16 +442,16 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { for span in spans { err.span_label(span, label.as_str()); } - err.emit(); - Err(true) + assert_ne!(bound, provided); + Err((bound as i32 - provided as i32, Some(err))) }; - let mut arg_count_correct = Ok(()); let mut unexpected_spans = vec![]; + let mut lifetime_count_correct = Ok(()); if !infer_lifetimes || arg_counts.lifetimes > param_counts.lifetimes { - arg_count_correct = check_kind_count( + lifetime_count_correct = check_kind_count( "lifetime", param_counts.lifetimes, param_counts.lifetimes, @@ -458,12 +459,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { 0, &mut unexpected_spans, explicit_late_bound == ExplicitLateBound::Yes, - ) - .and(arg_count_correct); + ); } + // FIXME(const_generics:defaults) + let mut const_count_correct = Ok(()); if !infer_args || arg_counts.consts > param_counts.consts { - arg_count_correct = check_kind_count( + const_count_correct = check_kind_count( "const", param_counts.consts, param_counts.consts, @@ -471,13 +473,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { arg_counts.lifetimes + arg_counts.types, &mut unexpected_spans, false, - ) - .and(arg_count_correct); + ); } + // Note that type errors are currently be emitted *after* const errors. + let mut type_count_correct = Ok(()); if !infer_args || arg_counts.types > param_counts.types - defaults.types - has_self as usize { - arg_count_correct = check_kind_count( + type_count_correct = check_kind_count( "type", param_counts.types - defaults.types - has_self as usize, param_counts.types - has_self as usize, @@ -485,14 +488,54 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { arg_counts.lifetimes, &mut unexpected_spans, false, - ) - .and(arg_count_correct); + ); + } + + // Emit a help message if it's possible that a type could be surrounded in braces + if let Err((c_mismatch, Some(ref mut _const_err))) = const_count_correct { + if let Err((_, Some(ref mut type_err))) = type_count_correct { + let possible_matches = args.args[arg_counts.lifetimes..] + .iter() + .filter(|arg| { + matches!( + arg, + GenericArg::Type(hir::Ty { kind: hir::TyKind::Path { .. }, .. }) + ) + }) + .take(c_mismatch.max(0) as usize); + for arg in possible_matches { + let suggestions = vec![ + (arg.span().shrink_to_lo(), String::from("{ ")), + (arg.span().shrink_to_hi(), String::from(" }")), + ]; + type_err.multipart_suggestion( + "If this generic argument was intended as a const parameter, \ + try surrounding it with braces:", + suggestions, + Applicability::MaybeIncorrect, + ); + } + } } + let emit_correct = + |correct: Result<(), (_, Option<rustc_errors::DiagnosticBuilder<'_>>)>| match correct { + Ok(()) => Ok(()), + Err((_, None)) => Err(()), + Err((_, Some(mut err))) => { + err.emit(); + Err(()) + } + }; + + let arg_count_correct = emit_correct(lifetime_count_correct) + .and(emit_correct(const_count_correct)) + .and(emit_correct(type_count_correct)); + GenericArgCountResult { explicit_late_bound, - correct: arg_count_correct.map_err(|reported_err| GenericArgCountMismatch { - reported: if reported_err { Some(ErrorReported) } else { None }, + correct: arg_count_correct.map_err(|()| GenericArgCountMismatch { + reported: Some(ErrorReported), invalid_args: unexpected_spans, }), } @@ -544,13 +587,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// Emits an error regarding forbidden type binding associations pub fn prohibit_assoc_ty_binding(tcx: TyCtxt<'_>, span: Span) { - let mut err = struct_span_err!( - tcx.sess, - span, - E0229, - "associated type bindings are not allowed here" - ); - err.span_label(span, "associated type not allowed here").emit(); + tcx.sess.emit_err(AssocTypeBindingNotAllowed { span }); } /// Prohibits explicit lifetime arguments if late-bound lifetime parameters diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index bb79a1258fb..9e339b1082c 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -7,6 +7,10 @@ mod generics; use crate::bounds::Bounds; use crate::collect::PlaceholderHirTyCollector; +use crate::errors::{ + AmbiguousLifetimeBound, MultipleRelaxedDefaultBounds, TraitObjectDeclaredWithNoTraits, + TypeofReservedKeywordUsed, ValueOfAssociatedStructAlreadySpecified, +}; use crate::middle::resolve_lifetime as rl; use crate::require_c_abi_if_c_variadic; use rustc_ast::util::lev_distance::find_best_match_for_name; @@ -684,14 +688,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { if unbound.is_none() { unbound = Some(&ptr.trait_ref); } else { - struct_span_err!( - tcx.sess, - span, - E0203, - "type parameter has more than one relaxed default \ - bound, only one is supported" - ) - .emit(); + tcx.sess.emit_err(MultipleRelaxedDefaultBounds { span }); } } } @@ -927,18 +924,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { dup_bindings .entry(assoc_ty.def_id) .and_modify(|prev_span| { - struct_span_err!( - self.tcx().sess, - binding.span, - E0719, - "the value of the associated type `{}` (from trait `{}`) \ - is already specified", - binding.item_name, - tcx.def_path_str(assoc_ty.container.id()) - ) - .span_label(binding.span, "re-bound here") - .span_label(*prev_span, format!("`{}` bound here first", binding.item_name)) - .emit(); + self.tcx().sess.emit_err(ValueOfAssociatedStructAlreadySpecified { + span: binding.span, + prev_span: *prev_span, + item_name: binding.item_name, + def_path: tcx.def_path_str(assoc_ty.container.id()), + }); }) .or_insert(binding.span); } @@ -1051,13 +1042,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } if regular_traits.is_empty() && auto_traits.is_empty() { - struct_span_err!( - tcx.sess, - span, - E0224, - "at least one trait is required for an object type" - ) - .emit(); + tcx.sess.emit_err(TraitObjectDeclaredWithNoTraits { span }); return tcx.ty_error(); } @@ -2059,15 +2044,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self.normalize_ty(ast_ty.span, array_ty) } hir::TyKind::Typeof(ref _e) => { - struct_span_err!( - tcx.sess, - ast_ty.span, - E0516, - "`typeof` is a reserved keyword but unimplemented" - ) - .span_label(ast_ty.span, "reserved keyword") - .emit(); - + tcx.sess.emit_err(TypeofReservedKeywordUsed { span: ast_ty.span }); tcx.ty_error() } hir::TyKind::Infer => { @@ -2283,13 +2260,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // error. let r = derived_region_bounds[0]; if derived_region_bounds[1..].iter().any(|r1| r != *r1) { - struct_span_err!( - tcx.sess, - span, - E0227, - "ambiguous lifetime bound, explicit lifetime bound required" - ) - .emit(); + tcx.sess.emit_err(AmbiguousLifetimeBound { span }); } Some(r) } diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs index 7adcd7b472e..bbf5153d35d 100644 --- a/compiler/rustc_typeck/src/check/compare_method.rs +++ b/compiler/rustc_typeck/src/check/compare_method.rs @@ -1,3 +1,4 @@ +use crate::errors::LifetimesOrBoundsMismatchOnTrait; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorReported}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; @@ -366,24 +367,18 @@ fn check_region_bounds_on_impl_item<'tcx>( let item_kind = assoc_item_kind_str(impl_m); let def_span = tcx.sess.source_map().guess_head_span(span); let span = tcx.hir().get_generics(impl_m.def_id).map(|g| g.span).unwrap_or(def_span); - let mut err = struct_span_err!( - tcx.sess, + let generics_span = if let Some(sp) = tcx.hir().span_if_local(trait_m.def_id) { + let def_sp = tcx.sess.source_map().guess_head_span(sp); + Some(tcx.hir().get_generics(trait_m.def_id).map(|g| g.span).unwrap_or(def_sp)) + } else { + None + }; + tcx.sess.emit_err(LifetimesOrBoundsMismatchOnTrait { span, - E0195, - "lifetime parameters or bounds on {} `{}` do not match the trait declaration", item_kind, - impl_m.ident, - ); - err.span_label(span, &format!("lifetimes do not match {} in trait", item_kind)); - if let Some(sp) = tcx.hir().span_if_local(trait_m.def_id) { - let def_sp = tcx.sess.source_map().guess_head_span(sp); - let sp = tcx.hir().get_generics(trait_m.def_id).map(|g| g.span).unwrap_or(def_sp); - err.span_label( - sp, - &format!("lifetimes in impl do not match this {} in trait", item_kind), - ); - } - err.emit(); + ident: impl_m.ident, + generics_span, + }); return Err(ErrorReported); } diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 05fd957e1f4..dba46f35dca 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -14,8 +14,13 @@ use crate::check::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExp use crate::check::FnCtxt; use crate::check::Needs; use crate::check::TupleArgumentsFlag::DontTupleArguments; +use crate::errors::{ + FieldMultiplySpecifiedInInitializer, FunctionalRecordUpdateOnNonStruct, + YieldExprOutsideOfGenerator, +}; use crate::type_error_struct; +use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive}; use rustc_ast as ast; use rustc_ast::util::lev_distance::find_best_match_for_name; use rustc_data_structures::fx::FxHashMap; @@ -439,14 +444,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) }); if !is_named { - struct_span_err!( - self.tcx.sess, - oprnd.span, - E0745, - "cannot take address of a temporary" - ) - .span_label(oprnd.span, "temporary value") - .emit(); + self.tcx.sess.emit_err(AddressOfTemporaryTaken { span: oprnd.span }) } } @@ -665,13 +663,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &'tcx hir::Expr<'tcx>, ) -> Ty<'tcx> { if self.ret_coercion.is_none() { - struct_span_err!( - self.tcx.sess, - expr.span, - E0572, - "return statement outside of function body", - ) - .emit(); + self.tcx.sess.emit_err(ReturnStmtOutsideOfFnBody { span: expr.span }); } else if let Some(ref e) = expr_opt { if self.ret_coercion_span.borrow().is_none() { *self.ret_coercion_span.borrow_mut() = Some(e.span); @@ -740,6 +732,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr_span: &Span, ) { if !lhs.is_syntactic_place_expr() { + // FIXME: Make this use SessionDiagnostic once error codes can be dynamically set. let mut err = self.tcx.sess.struct_span_err_with_code( *expr_span, "invalid left-hand side of assignment", @@ -1120,14 +1113,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Prohibit struct expressions when non-exhaustive flag is set. let adt = adt_ty.ty_adt_def().expect("`check_struct_path` returned non-ADT type"); if !adt.did.is_local() && variant.is_field_list_non_exhaustive() { - struct_span_err!( - self.tcx.sess, - expr.span, - E0639, - "cannot create non-exhaustive {} using struct expression", - adt.variant_descr() - ) - .emit(); + self.tcx + .sess + .emit_err(StructExprNonExhaustive { span: expr.span, what: adt.variant_descr() }); } let error_happened = self.check_expr_struct_fields( @@ -1165,13 +1153,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .insert(expr.hir_id, fru_field_types); } _ => { - struct_span_err!( - self.tcx.sess, - base_expr.span, - E0436, - "functional record update syntax requires a struct" - ) - .emit(); + self.tcx + .sess + .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span }); } } } @@ -1234,18 +1218,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { error_happened = true; if let Some(prev_span) = seen_fields.get(&ident) { - let mut err = struct_span_err!( - self.tcx.sess, - field.ident.span, - E0062, - "field `{}` specified more than once", - ident - ); - - err.span_label(field.ident.span, "used more than once"); - err.span_label(*prev_span, format!("first use of `{}`", ident)); - - err.emit(); + tcx.sess.emit_err(FieldMultiplySpecifiedInInitializer { + span: field.ident.span, + prev_span: *prev_span, + ident, + }); } else { self.report_unknown_field(adt_ty, variant, field, ast_fields, kind_name, span); } @@ -1264,42 +1241,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tcx.sess.span_err(span, "union expressions should have exactly one field"); } } else if check_completeness && !error_happened && !remaining_fields.is_empty() { - let len = remaining_fields.len(); - - let mut displayable_field_names = - remaining_fields.keys().map(|ident| ident.as_str()).collect::<Vec<_>>(); - - displayable_field_names.sort(); + let no_accessible_remaining_fields = remaining_fields + .iter() + .filter(|(_, (_, field))| { + field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx) + }) + .next() + .is_none(); - let truncated_fields_error = if len <= 3 { - String::new() + if no_accessible_remaining_fields { + self.report_no_accessible_fields(adt_ty, span); } else { - format!(" and {} other field{}", (len - 3), if len - 3 == 1 { "" } else { "s" }) - }; - - let remaining_fields_names = displayable_field_names - .iter() - .take(3) - .map(|n| format!("`{}`", n)) - .collect::<Vec<_>>() - .join(", "); - - struct_span_err!( - tcx.sess, - span, - E0063, - "missing field{} {}{} in initializer of `{}`", - pluralize!(remaining_fields.len()), - remaining_fields_names, - truncated_fields_error, - adt_ty - ) - .span_label( - span, - format!("missing {}{}", remaining_fields_names, truncated_fields_error), - ) - .emit(); + self.report_missing_field(adt_ty, span, remaining_fields); + } } + error_happened } @@ -1316,6 +1272,79 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// Report an error for a struct field expression when there are fields which aren't provided. + /// + /// ```ignore (diagnostic) + /// error: missing field `you_can_use_this_field` in initializer of `foo::Foo` + /// --> src/main.rs:8:5 + /// | + /// 8 | foo::Foo {}; + /// | ^^^^^^^^ missing `you_can_use_this_field` + /// + /// error: aborting due to previous error + /// ``` + fn report_missing_field( + &self, + adt_ty: Ty<'tcx>, + span: Span, + remaining_fields: FxHashMap<Ident, (usize, &ty::FieldDef)>, + ) { + let tcx = self.tcx; + let len = remaining_fields.len(); + + let mut displayable_field_names = + remaining_fields.keys().map(|ident| ident.as_str()).collect::<Vec<_>>(); + + displayable_field_names.sort(); + + let truncated_fields_error = if len <= 3 { + String::new() + } else { + format!(" and {} other field{}", (len - 3), if len - 3 == 1 { "" } else { "s" }) + }; + + let remaining_fields_names = displayable_field_names + .iter() + .take(3) + .map(|n| format!("`{}`", n)) + .collect::<Vec<_>>() + .join(", "); + + struct_span_err!( + tcx.sess, + span, + E0063, + "missing field{} {}{} in initializer of `{}`", + pluralize!(remaining_fields.len()), + remaining_fields_names, + truncated_fields_error, + adt_ty + ) + .span_label(span, format!("missing {}{}", remaining_fields_names, truncated_fields_error)) + .emit(); + } + + /// Report an error for a struct field expression when there are no visible fields. + /// + /// ```ignore (diagnostic) + /// error: cannot construct `Foo` with struct literal syntax due to inaccessible fields + /// --> src/main.rs:8:5 + /// | + /// 8 | foo::Foo {}; + /// | ^^^^^^^^ + /// + /// error: aborting due to previous error + /// ``` + fn report_no_accessible_fields(&self, adt_ty: Ty<'tcx>, span: Span) { + self.tcx.sess.span_err( + span, + &format!( + "cannot construct `{}` with struct literal syntax due to inaccessible fields", + adt_ty, + ), + ); + } + fn report_unknown_field( &self, ty: Ty<'tcx>, @@ -1876,13 +1905,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.mk_unit() } _ => { - struct_span_err!( - self.tcx.sess, - expr.span, - E0627, - "yield expression outside of generator literal" - ) - .emit(); + self.tcx.sess.emit_err(YieldExprOutsideOfGenerator { span: expr.span }); self.tcx.mk_unit() } } diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs index 47cea8649ef..b8230f52444 100644 --- a/compiler/rustc_typeck/src/check/intrinsic.rs +++ b/compiler/rustc_typeck/src/check/intrinsic.rs @@ -1,6 +1,10 @@ //! Type-checking for the rust-intrinsic and platform-intrinsic //! intrinsics that the compiler exposes. +use crate::errors::{ + SimdShuffleMissingLength, UnrecognizedAtomicOperation, UnrecognizedIntrinsicFunction, + WrongNumberOfTypeArgumentsToInstrinsic, +}; use crate::require_same_types; use rustc_errors::struct_span_err; @@ -41,17 +45,11 @@ fn equate_intrinsic_type<'tcx>( _ => bug!(), }; - struct_span_err!( - tcx.sess, + tcx.sess.emit_err(WrongNumberOfTypeArgumentsToInstrinsic { span, - E0094, - "intrinsic has wrong number of type \ - parameters: found {}, expected {}", - i_n_tps, - n_tps - ) - .span_label(span, format!("expected {} type parameter", n_tps)) - .emit(); + found: i_n_tps, + expected: n_tps, + }); return; } @@ -146,15 +144,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { | "umin" => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], param(0)), "fence" | "singlethreadfence" => (0, Vec::new(), tcx.mk_unit()), op => { - struct_span_err!( - tcx.sess, - it.span, - E0092, - "unrecognized atomic operation function: `{}`", - op - ) - .span_label(it.span, "unrecognized atomic operation") - .emit(); + tcx.sess.emit_err(UnrecognizedAtomicOperation { span: it.span, op }); return; } }; @@ -380,15 +370,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { sym::nontemporal_store => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()), other => { - struct_span_err!( - tcx.sess, - it.span, - E0093, - "unrecognized intrinsic function: `{}`", - other, - ) - .span_label(it.span, "unrecognized intrinsic") - .emit(); + tcx.sess.emit_err(UnrecognizedIntrinsicFunction { span: it.span, name: other }); return; } }; @@ -468,14 +450,7 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) (2, params, param(1)) } Err(_) => { - struct_span_err!( - tcx.sess, - it.span, - E0439, - "invalid `simd_shuffle`, needs length: `{}`", - name - ) - .emit(); + tcx.sess.emit_err(SimdShuffleMissingLength { span: it.span, name }); return; } } diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs index 7645ea4ff8c..14d80fded71 100644 --- a/compiler/rustc_typeck/src/check/method/probe.rs +++ b/compiler/rustc_typeck/src/check/method/probe.rs @@ -4,6 +4,7 @@ use super::NoMatchData; use super::{CandidateSource, ImplSource, TraitSource}; use crate::check::FnCtxt; +use crate::errors::MethodCallOnUnknownType; use crate::hir::def::DefKind; use crate::hir::def_id::DefId; @@ -11,7 +12,6 @@ use rustc_ast as ast; use rustc_ast::util::lev_distance::{find_best_match_for_name, lev_distance}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::Lrc; -use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::Namespace; use rustc_infer::infer::canonical::OriginalQueryValues; @@ -376,14 +376,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // so we do a future-compat lint here for the 2015 edition // (see https://github.com/rust-lang/rust/issues/46906) if self.tcx.sess.rust_2018() { - struct_span_err!( - self.tcx.sess, - span, - E0699, - "the type of this value must be known to call a method on a raw pointer on \ - it" - ) - .emit(); + self.tcx.sess.emit_err(MethodCallOnUnknownType { span }); } else { self.tcx.struct_span_lint_hir( lint::builtin::TYVAR_BEHIND_RAW_POINTER, diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index f06929aa98f..1896155e327 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -1078,8 +1078,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut unmentioned_fields = variant .fields .iter() - .map(|field| field.ident.normalize_to_macros_2_0()) - .filter(|ident| !used_fields.contains_key(&ident)) + .map(|field| (field, field.ident.normalize_to_macros_2_0())) + .filter(|(_, ident)| !used_fields.contains_key(&ident)) .collect::<Vec<_>>(); let inexistent_fields_err = if !(inexistent_fields.is_empty() || variant.is_recovered()) { @@ -1110,7 +1110,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tcx.sess.struct_span_err(pat.span, "`..` cannot be used in union patterns").emit(); } } else if !etc && !unmentioned_fields.is_empty() { - unmentioned_err = Some(self.error_unmentioned_fields(pat, &unmentioned_fields)); + let no_accessible_unmentioned_fields = unmentioned_fields + .iter() + .filter(|(field, _)| { + field.vis.is_accessible_from(tcx.parent_module(pat.hir_id).to_def_id(), tcx) + }) + .next() + .is_none(); + + if no_accessible_unmentioned_fields { + unmentioned_err = Some(self.error_no_accessible_fields(pat, &fields)); + } else { + unmentioned_err = Some(self.error_unmentioned_fields(pat, &unmentioned_fields)); + } } match (inexistent_fields_err, unmentioned_err) { (Some(mut i), Some(mut u)) => { @@ -1173,7 +1185,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, kind_name: &str, inexistent_fields: &[Ident], - unmentioned_fields: &mut Vec<Ident>, + unmentioned_fields: &mut Vec<(&ty::FieldDef, Ident)>, variant: &ty::VariantDef, ) -> DiagnosticBuilder<'tcx> { let tcx = self.tcx; @@ -1215,7 +1227,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), ); if plural == "" { - let input = unmentioned_fields.iter().map(|field| &field.name); + let input = unmentioned_fields.iter().map(|(_, field)| &field.name); let suggested_name = find_best_match_for_name(input, ident.name, None); if let Some(suggested_name) = suggested_name { err.span_suggestion( @@ -1232,7 +1244,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // `smart_resolve_context_dependent_help`. if suggested_name.to_ident_string().parse::<usize>().is_err() { // We don't want to throw `E0027` in case we have thrown `E0026` for them. - unmentioned_fields.retain(|&x| x.name != suggested_name); + unmentioned_fields.retain(|&(_, x)| x.name != suggested_name); } } } @@ -1300,17 +1312,77 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None } + /// Returns a diagnostic reporting a struct pattern which is missing an `..` due to + /// inaccessible fields. + /// + /// ```ignore (diagnostic) + /// error: pattern requires `..` due to inaccessible fields + /// --> src/main.rs:10:9 + /// | + /// LL | let foo::Foo {} = foo::Foo::default(); + /// | ^^^^^^^^^^^ + /// | + /// help: add a `..` + /// | + /// LL | let foo::Foo { .. } = foo::Foo::default(); + /// | ^^^^^^ + /// ``` + fn error_no_accessible_fields( + &self, + pat: &Pat<'_>, + fields: &'tcx [hir::FieldPat<'tcx>], + ) -> DiagnosticBuilder<'tcx> { + let mut err = self + .tcx + .sess + .struct_span_err(pat.span, "pattern requires `..` due to inaccessible fields"); + + if let Some(field) = fields.last() { + err.span_suggestion_verbose( + field.span.shrink_to_hi(), + "ignore the inaccessible and unused fields", + ", ..".to_string(), + Applicability::MachineApplicable, + ); + } else { + let qpath_span = if let PatKind::Struct(qpath, ..) = &pat.kind { + qpath.span() + } else { + bug!("`error_no_accessible_fields` called on non-struct pattern"); + }; + + // Shrink the span to exclude the `foo:Foo` in `foo::Foo { }`. + let span = pat.span.with_lo(qpath_span.shrink_to_hi().hi()); + err.span_suggestion_verbose( + span, + "ignore the inaccessible and unused fields", + " { .. }".to_string(), + Applicability::MachineApplicable, + ); + } + err + } + + /// Returns a diagnostic reporting a struct pattern which does not mention some fields. + /// + /// ```ignore (diagnostic) + /// error[E0027]: pattern does not mention field `you_cant_use_this_field` + /// --> src/main.rs:15:9 + /// | + /// LL | let foo::Foo {} = foo::Foo::new(); + /// | ^^^^^^^^^^^ missing field `you_cant_use_this_field` + /// ``` fn error_unmentioned_fields( &self, pat: &Pat<'_>, - unmentioned_fields: &[Ident], + unmentioned_fields: &[(&ty::FieldDef, Ident)], ) -> DiagnosticBuilder<'tcx> { let field_names = if unmentioned_fields.len() == 1 { - format!("field `{}`", unmentioned_fields[0]) + format!("field `{}`", unmentioned_fields[0].1) } else { let fields = unmentioned_fields .iter() - .map(|name| format!("`{}`", name)) + .map(|(_, name)| format!("`{}`", name)) .collect::<Vec<String>>() .join(", "); format!("fields {}", fields) diff --git a/compiler/rustc_typeck/src/check/place_op.rs b/compiler/rustc_typeck/src/check/place_op.rs index cdbed28f754..aed2af20e52 100644 --- a/compiler/rustc_typeck/src/check/place_op.rs +++ b/compiler/rustc_typeck/src/check/place_op.rs @@ -193,7 +193,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index` /// into `DerefMut` and `IndexMut` respectively. /// - /// This is a second pass of typechecking derefs/indices. We need this we do not + /// This is a second pass of typechecking derefs/indices. We need this because we do not /// always know whether a place needs to be mutable or not in the first pass. /// This happens whether there is an implicit mutable reborrow, e.g. when the type /// is used as the receiver of a method call. @@ -211,13 +211,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs); // Fix up autoderefs and derefs. + let mut inside_union = false; for (i, &expr) in exprs.iter().rev().enumerate() { debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr); + let mut source = self.node_ty(expr.hir_id); + if matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::UnDeref, _)) { + // Clear previous flag; after a pointer indirection it does not apply any more. + inside_union = false; + } + if source.ty_adt_def().map_or(false, |adt| adt.is_union()) { + inside_union = true; + } // Fix up the autoderefs. Autorefs can only occur immediately preceding // overloaded place ops, and will be fixed by them in order to get // the correct region. - let mut source = self.node_ty(expr.hir_id); // Do not mutate adjustments in place, but rather take them, // and replace them after mutating them, to avoid having the // typeck results borrowed during (`deref_mut`) method resolution. @@ -236,6 +244,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let ty::Ref(region, _, mutbl) = *method.sig.output().kind() { *deref = OverloadedDeref { region, mutbl }; } + // If this is a union field, also throw an error for `DerefMut` of `ManuallyDrop` (see RFC 2514). + // This helps avoid accidental drops. + if inside_union + && source.ty_adt_def().map_or(false, |adt| adt.is_manually_drop()) + { + let mut err = self.tcx.sess.struct_span_err( + expr.span, + "not automatically applying `DerefMut` on `ManuallyDrop` union field", + ); + err.help( + "writing to this reference calls the destructor for the old value", + ); + err.help("add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor"); + err.emit(); + } } } source = adjustment.target; diff --git a/compiler/rustc_typeck/src/coherence/builtin.rs b/compiler/rustc_typeck/src/coherence/builtin.rs index bc316a45be6..89270fb6c77 100644 --- a/compiler/rustc_typeck/src/coherence/builtin.rs +++ b/compiler/rustc_typeck/src/coherence/builtin.rs @@ -1,6 +1,7 @@ //! Check properties that are required by built-in traits and set //! up data structures required by type-checking/codegen. +use crate::errors::{CopyImplOnNonAdt, CopyImplOnTypeWithDtor, DropImplOnWrongItem}; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -58,14 +59,7 @@ fn visit_implementation_of_drop(tcx: TyCtxt<'_>, impl_did: LocalDefId) { _ => bug!("expected Drop impl item"), }; - struct_span_err!( - tcx.sess, - sp, - E0120, - "the `Drop` trait may only be implemented for structs, enums, and unions", - ) - .span_label(sp, "must be a struct, enum, or union") - .emit(); + tcx.sess.emit_err(DropImplOnWrongItem { span: sp }); } fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) { @@ -108,25 +102,10 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) { let span = if let ItemKind::Impl { self_ty, .. } = item.kind { self_ty.span } else { span }; - struct_span_err!( - tcx.sess, - span, - E0206, - "the trait `Copy` may not be implemented for this type" - ) - .span_label(span, "type is not a structure or enumeration") - .emit(); + tcx.sess.emit_err(CopyImplOnNonAdt { span }); } Err(CopyImplementationError::HasDestructor) => { - struct_span_err!( - tcx.sess, - span, - E0184, - "the trait `Copy` may not be implemented for this type; the \ - type has a destructor" - ) - .span_label(span, "Copy not allowed on types with destructors") - .emit(); + tcx.sess.emit_err(CopyImplOnTypeWithDtor { span }); } } } diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 94555e588bd..7d6b3df03b0 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -18,6 +18,7 @@ use crate::astconv::{AstConv, SizedByDefault}; use crate::bounds::Bounds; use crate::check::intrinsic::intrinsic_operation_unsafety; use crate::constrained_generic_params as cgp; +use crate::errors; use crate::middle::resolve_lifetime as rl; use rustc_ast as ast; use rustc_ast::MetaItemKind; @@ -36,11 +37,12 @@ use rustc_middle::hir::map::Map; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::mir::mono::Linkage; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::subst::InternalSubsts; +use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; use rustc_middle::ty::util::Discr; use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::{self, AdtKind, Const, ToPolyTraitRef, Ty, TyCtxt}; use rustc_middle::ty::{ReprOptions, ToPredicate, WithConstness}; +use rustc_middle::ty::{TypeFoldable, TypeVisitor}; use rustc_session::config::SanitizerSet; use rustc_session::lint; use rustc_session::parse::feature_err; @@ -49,6 +51,8 @@ use rustc_span::{Span, DUMMY_SP}; use rustc_target::spec::abi; use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName; +use smallvec::SmallVec; + mod type_of; struct OnlySelfBounds(bool); @@ -834,16 +838,11 @@ fn convert_variant( let fid = tcx.hir().local_def_id(f.hir_id); let dup_span = seen_fields.get(&f.ident.normalize_to_macros_2_0()).cloned(); if let Some(prev_span) = dup_span { - struct_span_err!( - tcx.sess, - f.span, - E0124, - "field `{}` is already declared", - f.ident - ) - .span_label(f.span, "field already declared") - .span_label(prev_span, format!("`{}` first declared here", f.ident)) - .emit(); + tcx.sess.emit_err(errors::FieldAlreadyDeclared { + field_name: f.ident, + span: f.span, + prev_span, + }); } else { seen_fields.insert(f.ident.normalize_to_macros_2_0(), f.span); } @@ -1676,10 +1675,46 @@ fn predicates_defined_on(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicate .alloc_from_iter(result.predicates.iter().chain(inferred_outlives).copied()); } } + + if tcx.features().const_evaluatable_checked { + let const_evaluatable = const_evaluatable_predicates_of(tcx, def_id, &result); + result.predicates = + tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(const_evaluatable)); + } + debug!("predicates_defined_on({:?}) = {:?}", def_id, result); result } +pub fn const_evaluatable_predicates_of<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + predicates: &ty::GenericPredicates<'tcx>, +) -> impl Iterator<Item = (ty::Predicate<'tcx>, Span)> { + #[derive(Default)] + struct ConstCollector<'tcx> { + ct: SmallVec<[(ty::WithOptConstParam<DefId>, SubstsRef<'tcx>); 4]>, + } + + impl<'tcx> TypeVisitor<'tcx> for ConstCollector<'tcx> { + fn visit_const(&mut self, ct: &'tcx Const<'tcx>) -> bool { + if let ty::ConstKind::Unevaluated(def, substs, None) = ct.val { + self.ct.push((def, substs)); + } + false + } + } + + let mut collector = ConstCollector::default(); + for (pred, _span) in predicates.predicates.iter() { + pred.visit_with(&mut collector); + } + warn!("const_evaluatable_predicates_of({:?}) = {:?}", def_id, collector.ct); + collector.ct.into_iter().map(move |(def_id, subst)| { + (ty::PredicateAtom::ConstEvaluatable(def_id, subst).to_predicate(tcx), DUMMY_SP) + }) +} + /// Returns a list of all type predicates (explicit and implicit) for the definition with /// ID `def_id`. This includes all predicates returned by `predicates_defined_on`, plus /// `Self: Trait` predicates for traits. diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs index f9668f1a0da..4b3250a1d44 100644 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ b/compiler/rustc_typeck/src/collect/type_of.rs @@ -1,5 +1,6 @@ +use crate::errors::AssocTypeOnInherentImpl; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{struct_span_err, Applicability, ErrorReported, StashKey}; +use rustc_errors::{Applicability, ErrorReported, StashKey}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -627,11 +628,5 @@ fn infer_placeholder_type( } fn report_assoc_ty_on_inherent_impl(tcx: TyCtxt<'_>, span: Span) { - struct_span_err!( - tcx.sess, - span, - E0202, - "associated types are not yet supported in inherent impls (see #8995)" - ) - .emit(); + tcx.sess.emit_err(AssocTypeOnInherentImpl { span }); } diff --git a/compiler/rustc_typeck/src/errors.rs b/compiler/rustc_typeck/src/errors.rs new file mode 100644 index 00000000000..a769e48d2ca --- /dev/null +++ b/compiler/rustc_typeck/src/errors.rs @@ -0,0 +1,199 @@ +//! Errors emitted by typeck. +use rustc_macros::SessionDiagnostic; +use rustc_span::{symbol::Ident, Span, Symbol}; + +#[derive(SessionDiagnostic)] +#[error = "E0062"] +pub struct FieldMultiplySpecifiedInInitializer { + #[message = "field `{ident}` specified more than once"] + #[label = "used more than once"] + pub span: Span, + #[label = "first use of `{ident}`"] + pub prev_span: Span, + pub ident: Ident, +} + +#[derive(SessionDiagnostic)] +#[error = "E0092"] +pub struct UnrecognizedAtomicOperation<'a> { + #[message = "unrecognized atomic operation function: `{op}`"] + #[label = "unrecognized atomic operation"] + pub span: Span, + pub op: &'a str, +} + +#[derive(SessionDiagnostic)] +#[error = "E0094"] +pub struct WrongNumberOfTypeArgumentsToInstrinsic { + #[message = "intrinsic has wrong number of type \ + parameters: found {found}, expected {expected}"] + #[label = "expected {expected} type parameter"] + pub span: Span, + pub found: usize, + pub expected: usize, +} + +#[derive(SessionDiagnostic)] +#[error = "E0093"] +pub struct UnrecognizedIntrinsicFunction { + #[message = "unrecognized intrinsic function: `{name}`"] + #[label = "unrecognized intrinsic"] + pub span: Span, + pub name: Symbol, +} + +#[derive(SessionDiagnostic)] +#[error = "E0195"] +pub struct LifetimesOrBoundsMismatchOnTrait { + #[message = "lifetime parameters or bounds on {item_kind} `{ident}` do not match the trait declaration"] + #[label = "lifetimes do not match {item_kind} in trait"] + pub span: Span, + #[label = "lifetimes in impl do not match this {item_kind} in trait"] + pub generics_span: Option<Span>, + pub item_kind: &'static str, + pub ident: Ident, +} + +#[derive(SessionDiagnostic)] +#[error = "E0120"] +pub struct DropImplOnWrongItem { + #[message = "the `Drop` trait may only be implemented for structs, enums, and unions"] + #[label = "must be a struct, enum, or union"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0124"] +pub struct FieldAlreadyDeclared { + pub field_name: Ident, + #[message = "field `{field_name}` is already declared"] + #[label = "field already declared"] + pub span: Span, + #[label = "`{field_name}` first declared here"] + pub prev_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0184"] +pub struct CopyImplOnTypeWithDtor { + #[message = "the trait `Copy` may not be implemented for this type; the \ + type has a destructor"] + #[label = "Copy not allowed on types with destructors"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0202"] +pub struct AssocTypeOnInherentImpl { + #[message = "associated types are not yet supported in inherent impls (see #8995)"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0203"] +pub struct MultipleRelaxedDefaultBounds { + #[message = "type parameter has more than one relaxed default bound, only one is supported"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0206"] +pub struct CopyImplOnNonAdt { + #[message = "the trait `Copy` may not be implemented for this type"] + #[label = "type is not a structure or enumeration"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0224"] +pub struct TraitObjectDeclaredWithNoTraits { + #[message = "at least one trait is required for an object type"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0227"] +pub struct AmbiguousLifetimeBound { + #[message = "ambiguous lifetime bound, explicit lifetime bound required"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0229"] +pub struct AssocTypeBindingNotAllowed { + #[message = "associated type bindings are not allowed here"] + #[label = "associated type not allowed here"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0439"] +pub struct SimdShuffleMissingLength { + #[message = "invalid `simd_shuffle`, needs length: `{name}`"] + pub span: Span, + pub name: Symbol, +} + +#[derive(SessionDiagnostic)] +#[error = "E0436"] +pub struct FunctionalRecordUpdateOnNonStruct { + #[message = "functional record update syntax requires a struct"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0516"] +pub struct TypeofReservedKeywordUsed { + #[message = "`typeof` is a reserved keyword but unimplemented"] + #[label = "reserved keyword"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0572"] +pub struct ReturnStmtOutsideOfFnBody { + #[message = "return statement outside of function body"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0627"] +pub struct YieldExprOutsideOfGenerator { + #[message = "yield expression outside of generator literal"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0639"] +pub struct StructExprNonExhaustive { + #[message = "cannot create non-exhaustive {what} using struct expression"] + pub span: Span, + pub what: &'static str, +} + +#[derive(SessionDiagnostic)] +#[error = "E0699"] +pub struct MethodCallOnUnknownType { + #[message = "the type of this value must be known to call a method on a raw pointer on it"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0719"] +pub struct ValueOfAssociatedStructAlreadySpecified { + #[message = "the value of the associated type `{item_name}` (from trait `{def_path}`) is already specified"] + #[label = "re-bound here"] + pub span: Span, + #[label = "`{item_name}` bound here first"] + pub prev_span: Span, + pub item_name: Ident, + pub def_path: String, +} + +#[derive(SessionDiagnostic)] +#[error = "E0745"] +pub struct AddressOfTemporaryTaken { + #[message = "cannot take address of a temporary"] + #[label = "temporary value"] + pub span: Span, +} diff --git a/compiler/rustc_typeck/src/impl_wf_check.rs b/compiler/rustc_typeck/src/impl_wf_check.rs index 891e482b431..4901d6041d6 100644 --- a/compiler/rustc_typeck/src/impl_wf_check.rs +++ b/compiler/rustc_typeck/src/impl_wf_check.rs @@ -187,7 +187,7 @@ fn enforce_impl_params_are_constrained( } // (*) This is a horrible concession to reality. I think it'd be - // better to just ban unconstrianed lifetimes outright, but in + // better to just ban unconstrained lifetimes outright, but in // practice people do non-hygenic macros like: // // ``` @@ -207,7 +207,7 @@ fn enforce_impl_params_are_constrained( } fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: &str) { - struct_span_err!( + let mut err = struct_span_err!( tcx.sess, span, E0207, @@ -215,9 +215,17 @@ fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: &str) impl trait, self type, or predicates", kind, name - ) - .span_label(span, format!("unconstrained {} parameter", kind)) - .emit(); + ); + err.span_label(span, format!("unconstrained {} parameter", kind)); + if kind == "const" { + err.note( + "expressions using a const parameter must map each value to a distinct output value", + ); + err.note( + "proving the result of expressions other than the parameter are unique is not supported", + ); + } + err.emit(); } /// Enforce that we do not have two items in an impl with the same name. diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs index b056582faf8..0e9f4476c20 100644 --- a/compiler/rustc_typeck/src/lib.rs +++ b/compiler/rustc_typeck/src/lib.rs @@ -84,6 +84,7 @@ mod check_unused; mod coherence; mod collect; mod constrained_generic_params; +mod errors; mod impl_wf_check; mod mem_categorization; mod outlives; |
