diff --git a/.mailmap b/.mailmap index 0f7bc5e38bd1..fc8e83d6493c 100644 --- a/.mailmap +++ b/.mailmap @@ -427,6 +427,7 @@ Marcell Pardavi Marco Ieni <11428655+MarcoIeni@users.noreply.github.com> Marcus Klaas de Vries Margaret Meyerhofer +Marijn Schouten Mark Mansi Mark Mansi Mark Rousskov diff --git a/Cargo.lock b/Cargo.lock index bcb7dd1b25bc..2320e33bc4bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -75,7 +75,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "710e8eae58854cdc1790fcb56cca04d712a17be849eeb81da2a724bf4bae2bc4" dependencies = [ "anstyle", - "unicode-width 0.2.1", + "unicode-width 0.2.2", +] + +[[package]] +name = "annotate-snippets" +version = "0.12.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47224528f74de27d1d06aad6a5dda4f865b6ebe2e56c538943d746a7270cb67e" +dependencies = [ + "anstyle", + "unicode-width 0.2.2", ] [[package]] @@ -95,9 +105,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-lossy" @@ -136,7 +146,7 @@ dependencies = [ "anstyle-lossy", "anstyle-parse", "html-escape", - "unicode-width 0.2.1", + "unicode-width 0.2.2", ] [[package]] @@ -677,7 +687,7 @@ checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" dependencies = [ "serde", "termcolor", - "unicode-width 0.2.1", + "unicode-width 0.2.2", ] [[package]] @@ -808,7 +818,7 @@ dependencies = [ "encode_unicode", "libc", "once_cell", - "unicode-width 0.2.1", + "unicode-width 0.2.2", "windows-sys 0.59.0", ] @@ -1485,7 +1495,7 @@ version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df" dependencies = [ - "unicode-width 0.2.1", + "unicode-width 0.2.2", ] [[package]] @@ -1887,7 +1897,7 @@ dependencies = [ "console", "number_prefix", "portable-atomic", - "unicode-width 0.2.1", + "unicode-width 0.2.2", "web-time", ] @@ -3756,7 +3766,9 @@ dependencies = [ name = "rustc_errors" version = "0.0.0" dependencies = [ - "annotate-snippets 0.11.5", + "annotate-snippets 0.12.7", + "anstream", + "anstyle", "derive_setters", "rustc_abi", "rustc_ast", @@ -3773,7 +3785,6 @@ dependencies = [ "rustc_span", "serde", "serde_json", - "termcolor", "termize", "tracing", "windows 0.61.3", @@ -4327,11 +4338,10 @@ dependencies = [ "rustc_macros", "rustc_session", "rustc_span", - "termcolor", "thin-vec", "tracing", "unicode-normalization", - "unicode-width 0.2.1", + "unicode-width 0.2.2", ] [[package]] @@ -4590,7 +4600,7 @@ dependencies = [ "sha1", "sha2", "tracing", - "unicode-width 0.2.1", + "unicode-width 0.2.2", ] [[package]] @@ -5936,9 +5946,9 @@ checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-width" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "unicode-xid" @@ -6223,7 +6233,7 @@ dependencies = [ "bumpalo", "leb128fmt", "memchr", - "unicode-width 0.2.1", + "unicode-width 0.2.2", "wasm-encoder 0.240.0", ] diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 0adf4f898ab6..94c15094f525 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2579,6 +2579,9 @@ pub enum TyPatKind { /// A range pattern (e.g., `1...2`, `1..2`, `1..`, `..2`, `1..=2`, `..=2`). Range(Option>, Option>, Spanned), + /// A `!null` pattern for raw pointers. + NotNull, + Or(ThinVec), /// Placeholder for a pattern that wasn't syntactically well formed in some way. @@ -3633,49 +3636,26 @@ pub struct Trait { pub items: ThinVec>, } -/// The location of a where clause on a `TyAlias` (`Span`) and whether there was -/// a `where` keyword (`bool`). This is split out from `WhereClause`, since there -/// are two locations for where clause on type aliases, but their predicates -/// are concatenated together. -/// -/// Take this example: -/// ```ignore (only-for-syntax-highlight) -/// trait Foo { -/// type Assoc<'a, 'b> where Self: 'a, Self: 'b; -/// } -/// impl Foo for () { -/// type Assoc<'a, 'b> where Self: 'a = () where Self: 'b; -/// // ^^^^^^^^^^^^^^ first where clause -/// // ^^^^^^^^^^^^^^ second where clause -/// } -/// ``` -/// -/// If there is no where clause, then this is `false` with `DUMMY_SP`. -#[derive(Copy, Clone, Encodable, Decodable, Debug, Default, Walkable)] -pub struct TyAliasWhereClause { - pub has_where_token: bool, - pub span: Span, -} - -/// The span information for the two where clauses on a `TyAlias`. -#[derive(Copy, Clone, Encodable, Decodable, Debug, Default, Walkable)] -pub struct TyAliasWhereClauses { - /// Before the equals sign. - pub before: TyAliasWhereClause, - /// After the equals sign. - pub after: TyAliasWhereClause, - /// The index in `TyAlias.generics.where_clause.predicates` that would split - /// into predicates from the where clause before the equals sign and the ones - /// from the where clause after the equals sign. - pub split: usize, -} - #[derive(Clone, Encodable, Decodable, Debug, Walkable)] pub struct TyAlias { pub defaultness: Defaultness, pub ident: Ident, pub generics: Generics, - pub where_clauses: TyAliasWhereClauses, + /// There are two locations for where clause on type aliases. This represents the second + /// where clause, before the semicolon. The first where clause is stored inside `generics`. + /// + /// Take this example: + /// ```ignore (only-for-syntax-highlight) + /// trait Foo { + /// type Assoc<'a, 'b> where Self: 'a, Self: 'b; + /// } + /// impl Foo for () { + /// type Assoc<'a, 'b> where Self: 'a = () where Self: 'b; + /// // ^^^^^^^^^^^^^^ before where clause + /// // ^^^^^^^^^^^^^^ after where clause + /// } + /// ``` + pub after_where_clause: WhereClause, #[visitable(extra = BoundKind::Bound)] pub bounds: GenericBounds, pub ty: Option>, diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index ebd5aa6e93d8..03e5a6edeece 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -471,8 +471,6 @@ macro_rules! common_visitor_and_walkers { TraitBoundModifiers, TraitObjectSyntax, TyAlias, - TyAliasWhereClause, - TyAliasWhereClauses, TyKind, TyPatKind, UnOp, diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 4a9b9f544b53..c443b1e3a03c 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -7,6 +7,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir as hir; use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{DefKind, Res}; +use rustc_hir::definitions::DefPathData; use rustc_hir::{HirId, Target, find_attr}; use rustc_middle::span_bug; use rustc_middle::ty::TyCtxt; @@ -461,7 +462,13 @@ impl<'hir> LoweringContext<'_, 'hir> { for (idx, arg) in args.iter().cloned().enumerate() { if legacy_args_idx.contains(&idx) { let node_id = self.next_node_id(); - self.create_def(node_id, None, DefKind::AnonConst, f.span); + self.create_def( + node_id, + None, + DefKind::AnonConst, + DefPathData::LateAnonConst, + f.span, + ); let mut visitor = WillCreateDefIdsVisitor {}; let const_value = if let ControlFlow::Break(span) = visitor.visit_expr(&arg) { Box::new(Expr { diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index e4b23cba8aa1..8527108f7041 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -36,20 +36,18 @@ pub(super) struct ItemLowerer<'a, 'hir> { /// clause if it exists. fn add_ty_alias_where_clause( generics: &mut ast::Generics, - mut where_clauses: TyAliasWhereClauses, + after_where_clause: &ast::WhereClause, prefer_first: bool, ) { + generics.where_clause.predicates.extend_from_slice(&after_where_clause.predicates); + + let mut before = (generics.where_clause.has_where_token, generics.where_clause.span); + let mut after = (after_where_clause.has_where_token, after_where_clause.span); if !prefer_first { - (where_clauses.before, where_clauses.after) = (where_clauses.after, where_clauses.before); + (before, after) = (after, before); } - let where_clause = - if where_clauses.before.has_where_token || !where_clauses.after.has_where_token { - where_clauses.before - } else { - where_clauses.after - }; - generics.where_clause.has_where_token = where_clause.has_where_token; - generics.where_clause.span = where_clause.span; + (generics.where_clause.has_where_token, generics.where_clause.span) = + if before.0 || !after.0 { before } else { after }; } impl<'a, 'hir> ItemLowerer<'a, 'hir> { @@ -271,7 +269,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.lower_body(|this| (&[], this.expr(span, hir::ExprKind::InlineAsm(asm)))); hir::ItemKind::GlobalAsm { asm, fake_body } } - ItemKind::TyAlias(box TyAlias { ident, generics, where_clauses, ty, .. }) => { + ItemKind::TyAlias(box TyAlias { ident, generics, after_where_clause, ty, .. }) => { // We lower // // type Foo = impl Trait @@ -282,7 +280,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // opaque type Foo1: Trait let ident = self.lower_ident(*ident); let mut generics = generics.clone(); - add_ty_alias_where_clause(&mut generics, *where_clauses, true); + add_ty_alias_where_clause(&mut generics, after_where_clause, true); let (generics, ty) = self.lower_generics( &generics, id, @@ -901,10 +899,15 @@ impl<'hir> LoweringContext<'_, 'hir> { ) } AssocItemKind::Type(box TyAlias { - ident, generics, where_clauses, bounds, ty, .. + ident, + generics, + after_where_clause, + bounds, + ty, + .. }) => { let mut generics = generics.clone(); - add_ty_alias_where_clause(&mut generics, *where_clauses, false); + add_ty_alias_where_clause(&mut generics, after_where_clause, false); let (generics, kind) = self.lower_generics( &generics, i.id, @@ -1070,9 +1073,11 @@ impl<'hir> LoweringContext<'_, 'hir> { (*ident, (generics, hir::ImplItemKind::Fn(sig, body_id))) } - AssocItemKind::Type(box TyAlias { ident, generics, where_clauses, ty, .. }) => { + AssocItemKind::Type(box TyAlias { + ident, generics, after_where_clause, ty, .. + }) => { let mut generics = generics.clone(); - add_ty_alias_where_clause(&mut generics, *where_clauses, false); + add_ty_alias_where_clause(&mut generics, after_where_clause, false); ( *ident, self.lower_generics( diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index d959657e7fe5..be9db9257356 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -51,6 +51,7 @@ use rustc_data_structures::tagged_ptr::TaggedRef; use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle}; use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId}; +use rustc_hir::definitions::{DefPathData, DisambiguatorState}; use rustc_hir::lints::DelayedLint; use rustc_hir::{ self as hir, AngleBrackets, ConstArg, GenericArg, HirId, ItemLocalMap, LifetimeSource, @@ -93,6 +94,7 @@ rustc_fluent_macro::fluent_messages! { "../messages.ftl" } struct LoweringContext<'a, 'hir> { tcx: TyCtxt<'hir>, resolver: &'a mut ResolverAstLowering, + disambiguator: DisambiguatorState, /// Used to allocate HIR nodes. arena: &'hir hir::Arena<'hir>, @@ -155,6 +157,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // Pseudo-globals. tcx, resolver, + disambiguator: DisambiguatorState::new(), arena: tcx.hir_arena, // HirId handling. @@ -546,6 +549,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { node_id: ast::NodeId, name: Option, def_kind: DefKind, + def_path_data: DefPathData, span: Span, ) -> LocalDefId { let parent = self.current_hir_id_owner.def_id; @@ -561,7 +565,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let def_id = self .tcx .at(span) - .create_def(parent, name, def_kind, None, &mut self.resolver.disambiguator) + .create_def(parent, name, def_kind, Some(def_path_data), &mut self.disambiguator) .def_id(); debug!("create_def: def_id_to_node_id[{:?}] <-> {:?}", def_id, node_id); @@ -846,6 +850,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { param, Some(kw::UnderscoreLifetime), DefKind::LifetimeParam, + DefPathData::DesugaredAnonymousLifetime, ident.span, ); debug!(?_def_id); @@ -2290,7 +2295,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // We're lowering a const argument that was originally thought to be a type argument, // so the def collector didn't create the def ahead of time. That's why we have to do // it here. - let def_id = self.create_def(node_id, None, DefKind::AnonConst, span); + let def_id = self.create_def( + node_id, + None, + DefKind::AnonConst, + DefPathData::LateAnonConst, + span, + ); let hir_id = self.lower_node_id(node_id); let path_expr = Expr { diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index 03aeb064d7f1..815338c84fa6 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use rustc_ast::*; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::def::{DefKind, Res}; +use rustc_hir::definitions::DefPathData; use rustc_hir::{self as hir, LangItem, Target}; use rustc_middle::span_bug; use rustc_span::source_map::{Spanned, respan}; @@ -143,7 +144,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } // return inner to be processed in next loop PatKind::Paren(inner) => pattern = inner, - PatKind::MacCall(_) => panic!("{:?} shouldn't exist here", pattern.span), + PatKind::MacCall(_) => { + panic!("{pattern:#?} shouldn't exist here") + } PatKind::Err(guar) => break hir::PatKind::Err(*guar), } }; @@ -460,6 +463,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) }), ), + TyPatKind::NotNull => hir::TyPatKind::NotNull, TyPatKind::Or(variants) => { hir::TyPatKind::Or(self.arena.alloc_from_iter( variants.iter().map(|pat| self.lower_ty_pat_mut(pat, base_type)), @@ -524,7 +528,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // We're generating a range end that didn't exist in the AST, // so the def collector didn't create the def ahead of time. That's why we have to do // it here. - let def_id = self.create_def(node_id, None, DefKind::AnonConst, span); + let def_id = + self.create_def(node_id, None, DefKind::AnonConst, DefPathData::LateAnonConst, span); let hir_id = self.lower_node_id(node_id); let unstable_span = self.mark_span_with_reason( diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index f773b02058ef..93f8b74ad561 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -145,25 +145,24 @@ impl<'a> AstValidator<'a> { &mut self, ty_alias: &TyAlias, ) -> Result<(), errors::WhereClauseBeforeTypeAlias> { - if ty_alias.ty.is_none() || !ty_alias.where_clauses.before.has_where_token { + if ty_alias.ty.is_none() || !ty_alias.generics.where_clause.has_where_token { return Ok(()); } - let (before_predicates, after_predicates) = - ty_alias.generics.where_clause.predicates.split_at(ty_alias.where_clauses.split); - let span = ty_alias.where_clauses.before.span; + let span = ty_alias.generics.where_clause.span; - let sugg = if !before_predicates.is_empty() || !ty_alias.where_clauses.after.has_where_token + let sugg = if !ty_alias.generics.where_clause.predicates.is_empty() + || !ty_alias.after_where_clause.has_where_token { let mut state = State::new(); - if !ty_alias.where_clauses.after.has_where_token { + if !ty_alias.after_where_clause.has_where_token { state.space(); state.word_space("where"); } - let mut first = after_predicates.is_empty(); - for p in before_predicates { + let mut first = ty_alias.after_where_clause.predicates.is_empty(); + for p in &ty_alias.generics.where_clause.predicates { if !first { state.word_space(","); } @@ -174,7 +173,7 @@ impl<'a> AstValidator<'a> { errors::WhereClauseBeforeTypeAliasSugg::Move { left: span, snippet: state.s.eof(), - right: ty_alias.where_clauses.after.span.shrink_to_hi(), + right: ty_alias.after_where_clause.span.shrink_to_hi(), } } else { errors::WhereClauseBeforeTypeAliasSugg::Remove { span } @@ -566,11 +565,7 @@ impl<'a> AstValidator<'a> { self.dcx().emit_err(errors::BoundInContext { span, ctx }); } - fn check_foreign_ty_genericless( - &self, - generics: &Generics, - where_clauses: &TyAliasWhereClauses, - ) { + fn check_foreign_ty_genericless(&self, generics: &Generics, after_where_clause: &WhereClause) { let cannot_have = |span, descr, remove_descr| { self.dcx().emit_err(errors::ExternTypesCannotHave { span, @@ -584,14 +579,14 @@ impl<'a> AstValidator<'a> { cannot_have(generics.span, "generic parameters", "generic parameters"); } - let check_where_clause = |where_clause: TyAliasWhereClause| { + let check_where_clause = |where_clause: &WhereClause| { if where_clause.has_where_token { cannot_have(where_clause.span, "`where` clauses", "`where` clause"); } }; - check_where_clause(where_clauses.before); - check_where_clause(where_clauses.after); + check_where_clause(&generics.where_clause); + check_where_clause(&after_where_clause); } fn check_foreign_kind_bodyless(&self, ident: Ident, kind: &str, body_span: Option) { @@ -1261,7 +1256,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { visit::walk_item(self, item); } ItemKind::TyAlias( - ty_alias @ box TyAlias { defaultness, bounds, where_clauses, ty, .. }, + ty_alias @ box TyAlias { defaultness, bounds, after_where_clause, ty, .. }, ) => { self.check_defaultness(item.span, *defaultness); if ty.is_none() { @@ -1276,9 +1271,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> { if let Err(err) = self.check_type_alias_where_clause_location(ty_alias) { self.dcx().emit_err(err); } - } else if where_clauses.after.has_where_token { + } else if after_where_clause.has_where_token { self.dcx().emit_err(errors::WhereClauseAfterTypeAlias { - span: where_clauses.after.span, + span: after_where_clause.span, help: self.sess.is_nightly_build(), }); } @@ -1308,7 +1303,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { defaultness, ident, generics, - where_clauses, + after_where_clause, bounds, ty, .. @@ -1316,7 +1311,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.check_defaultness(fi.span, *defaultness); self.check_foreign_kind_bodyless(*ident, "type", ty.as_ref().map(|b| b.span)); self.check_type_no_bounds(bounds, "`extern` blocks"); - self.check_foreign_ty_genericless(generics, where_clauses); + self.check_foreign_ty_genericless(generics, after_where_clause); self.check_foreign_item_ascii_only(*ident); } ForeignItemKind::Static(box StaticItem { ident, safety, expr, .. }) => { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 41b520b04c99..a5e2bcaa3bd0 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1232,6 +1232,7 @@ impl<'a> State<'a> { self.print_expr_anon_const(end, &[]); } } + rustc_ast::TyPatKind::NotNull => self.word("!null"), rustc_ast::TyPatKind::Or(variants) => { let mut first = true; for pat in variants { diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index ab402cbb8dc1..341266086340 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -59,14 +59,14 @@ impl<'a> State<'a> { defaultness, ident, generics, - where_clauses, + after_where_clause, bounds, ty, }) => { self.print_associated_type( *ident, generics, - *where_clauses, + after_where_clause, bounds, ty.as_deref(), vis, @@ -127,14 +127,12 @@ impl<'a> State<'a> { &mut self, ident: Ident, generics: &ast::Generics, - where_clauses: ast::TyAliasWhereClauses, + after_where_clause: &ast::WhereClause, bounds: &ast::GenericBounds, ty: Option<&ast::Ty>, vis: &ast::Visibility, defaultness: ast::Defaultness, ) { - let (before_predicates, after_predicates) = - generics.where_clause.predicates.split_at(where_clauses.split); let (cb, ib) = self.head(""); self.print_visibility(vis); self.print_defaultness(defaultness); @@ -145,13 +143,13 @@ impl<'a> State<'a> { self.word_nbsp(":"); self.print_type_bounds(bounds); } - self.print_where_clause_parts(where_clauses.before.has_where_token, before_predicates); + self.print_where_clause(&generics.where_clause); if let Some(ty) = ty { self.space(); self.word_space("="); self.print_type(ty); } - self.print_where_clause_parts(where_clauses.after.has_where_token, after_predicates); + self.print_where_clause(&after_where_clause); self.word(";"); self.end(ib); self.end(cb); @@ -283,14 +281,14 @@ impl<'a> State<'a> { defaultness, ident, generics, - where_clauses, + after_where_clause, bounds, ty, }) => { self.print_associated_type( *ident, generics, - *where_clauses, + after_where_clause, bounds, ty.as_deref(), &item.vis, @@ -585,14 +583,14 @@ impl<'a> State<'a> { defaultness, ident, generics, - where_clauses, + after_where_clause, bounds, ty, }) => { self.print_associated_type( *ident, generics, - *where_clauses, + after_where_clause, bounds, ty.as_deref(), vis, @@ -759,14 +757,7 @@ impl<'a> State<'a> { } fn print_where_clause(&mut self, where_clause: &ast::WhereClause) { - self.print_where_clause_parts(where_clause.has_where_token, &where_clause.predicates); - } - - fn print_where_clause_parts( - &mut self, - has_where_token: bool, - predicates: &[ast::WherePredicate], - ) { + let ast::WhereClause { has_where_token, ref predicates, span: _ } = *where_clause; if predicates.is_empty() && !has_where_token { return; } diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index af94e8acaf68..dab9e7666c23 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -326,7 +326,8 @@ pub fn parse_cfg_attr( }) { Ok(r) => return Some(r), Err(e) => { - let suggestions = CFG_ATTR_TEMPLATE.suggestions(cfg_attr.style, sym::cfg_attr); + let suggestions = + CFG_ATTR_TEMPLATE.suggestions(Some(cfg_attr.style), sym::cfg_attr); e.with_span_suggestions( cfg_attr.span, "must be of the form", @@ -356,7 +357,7 @@ pub fn parse_cfg_attr( template: CFG_ATTR_TEMPLATE, attribute: AttrPath::from_ast(&cfg_attr.get_normal_item().path), reason, - attr_style: cfg_attr.style, + suggestions: CFG_ATTR_TEMPLATE.suggestions(Some(cfg_attr.style), sym::cfg_attr), }); } } @@ -388,6 +389,7 @@ fn parse_cfg_attr_internal<'a>( let cfg_predicate = AttributeParser::parse_single_args( sess, attribute.span, + attribute.get_normal_item().span(), attribute.style, AttrPath { segments: attribute diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index 20ec0fd0c7b6..1d3ab76e1499 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -20,12 +20,7 @@ impl SingleAttributeParser for CrateNameParser { return None; }; - Some(AttributeKind::CrateName { - name, - name_span: n.value_span, - attr_span: cx.attr_span, - style: cx.attr_style, - }) + Some(AttributeKind::CrateName { name, name_span: n.value_span, attr_span: cx.attr_span }) } } diff --git a/compiler/rustc_attr_parsing/src/attributes/inline.rs b/compiler/rustc_attr_parsing/src/attributes/inline.rs index a10ad27fcc68..eda272fb7f2b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/inline.rs +++ b/compiler/rustc_attr_parsing/src/attributes/inline.rs @@ -56,8 +56,7 @@ impl SingleAttributeParser for InlineParser { } } ArgParser::NameValue(_) => { - let suggestions = >::TEMPLATE - .suggestions(cx.attr_style, "inline"); + let suggestions = cx.suggestions(); let span = cx.attr_span; cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span); return None; diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 40ecd91e5cfc..797d04b914dd 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -71,8 +71,7 @@ impl CombineAttributeParser for LinkParser { // Specifically `#[link = "dl"]` is accepted with a FCW // For more information, see https://github.com/rust-lang/rust/pull/143193 ArgParser::NameValue(nv) if nv.value_as_str().is_some_and(|v| v == sym::dl) => { - let suggestions = >::TEMPLATE - .suggestions(cx.attr_style, "link"); + let suggestions = cx.suggestions(); let span = cx.attr_span; cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span); return None; diff --git a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs index 849141c34f7d..787003519e78 100644 --- a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs @@ -1,4 +1,3 @@ -use rustc_ast::AttrStyle; use rustc_errors::DiagArgValue; use rustc_hir::attrs::MacroUseArgs; @@ -102,7 +101,7 @@ impl AttributeParser for MacroUseParser { } } ArgParser::NameValue(_) => { - let suggestions = MACRO_USE_TEMPLATE.suggestions(cx.attr_style, sym::macro_use); + let suggestions = cx.suggestions(); cx.emit_err(IllFormedAttributeInputLint { num_suggestions: suggestions.len(), suggestions: DiagArgValue::StrListSepByAnd( @@ -149,19 +148,14 @@ impl SingleAttributeParser for MacroExportParser { ]); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { - let suggestions = || { - >::TEMPLATE - .suggestions(AttrStyle::Inner, "macro_export") - }; let local_inner_macros = match args { ArgParser::NoArgs => false, ArgParser::List(list) => { let Some(l) = list.single() else { let span = cx.attr_span; + let suggestions = cx.suggestions(); cx.emit_lint( - AttributeLintKind::InvalidMacroExportArguments { - suggestions: suggestions(), - }, + AttributeLintKind::InvalidMacroExportArguments { suggestions }, span, ); return None; @@ -170,10 +164,9 @@ impl SingleAttributeParser for MacroExportParser { Some(sym::local_inner_macros) => true, _ => { let span = cx.attr_span; + let suggestions = cx.suggestions(); cx.emit_lint( - AttributeLintKind::InvalidMacroExportArguments { - suggestions: suggestions(), - }, + AttributeLintKind::InvalidMacroExportArguments { suggestions }, span, ); return None; @@ -182,7 +175,7 @@ impl SingleAttributeParser for MacroExportParser { } ArgParser::NameValue(_) => { let span = cx.attr_span; - let suggestions = suggestions(); + let suggestions = cx.suggestions(); cx.emit_err(IllFormedAttributeInputLint { num_suggestions: suggestions.len(), suggestions: DiagArgValue::StrListSepByAnd( diff --git a/compiler/rustc_attr_parsing/src/attributes/must_use.rs b/compiler/rustc_attr_parsing/src/attributes/must_use.rs index e6a5141d7830..51b43e96adf9 100644 --- a/compiler/rustc_attr_parsing/src/attributes/must_use.rs +++ b/compiler/rustc_attr_parsing/src/attributes/must_use.rs @@ -45,8 +45,7 @@ impl SingleAttributeParser for MustUseParser { Some(value_str) } ArgParser::List(_) => { - let suggestions = >::TEMPLATE - .suggestions(cx.attr_style, "must_use"); + let suggestions = cx.suggestions(); cx.emit_err(IllFormedAttributeInputLint { num_suggestions: suggestions.len(), suggestions: DiagArgValue::StrListSepByAnd( diff --git a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs index 510ff1ded490..23ecc0bf7d29 100644 --- a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs @@ -20,8 +20,7 @@ impl SingleAttributeParser for IgnoreParser { ArgParser::NoArgs => None, ArgParser::NameValue(name_value) => { let Some(str_value) = name_value.value_as_str() else { - let suggestions = >::TEMPLATE - .suggestions(cx.attr_style, "ignore"); + let suggestions = cx.suggestions(); let span = cx.attr_span; cx.emit_lint( AttributeLintKind::IllFormedAttributeInput { suggestions }, @@ -32,8 +31,7 @@ impl SingleAttributeParser for IgnoreParser { Some(str_value) } ArgParser::List(_) => { - let suggestions = >::TEMPLATE - .suggestions(cx.attr_style, "ignore"); + let suggestions = cx.suggestions(); let span = cx.attr_span; cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span); return None; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 6749bdfb1a67..6694dac8bb25 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -337,8 +337,16 @@ pub struct Late; /// Gives [`AttributeParser`]s enough information to create errors, for example. pub struct AcceptContext<'f, 'sess, S: Stage> { pub(crate) shared: SharedContext<'f, 'sess, S>, - /// The span of the attribute currently being parsed + + /// The outer span of the attribute currently being parsed + /// #[attribute(...)] + /// ^^^^^^^^^^^^^^^^^ outer span + /// For attributes in `cfg_attr`, the outer span and inner spans are equal. pub(crate) attr_span: Span, + /// The inner span of the attribute currently being parsed + /// #[attribute(...)] + /// ^^^^^^^^^^^^^^ inner span + pub(crate) inner_span: Span, /// Whether it is an inner or outer attribute pub(crate) attr_style: AttrStyle, @@ -427,7 +435,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { i.kind.is_bytestr().then(|| self.sess().source_map().start_point(i.span)) }), }, - attr_style: self.attr_style, + suggestions: self.suggestions(), }) } @@ -438,7 +446,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { template: self.template.clone(), attribute: self.attr_path.clone(), reason: AttributeParseErrorReason::ExpectedIntegerLiteral, - attr_style: self.attr_style, + suggestions: self.suggestions(), }) } @@ -449,7 +457,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { template: self.template.clone(), attribute: self.attr_path.clone(), reason: AttributeParseErrorReason::ExpectedList, - attr_style: self.attr_style, + suggestions: self.suggestions(), }) } @@ -460,7 +468,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { template: self.template.clone(), attribute: self.attr_path.clone(), reason: AttributeParseErrorReason::ExpectedNoArgs, - attr_style: self.attr_style, + suggestions: self.suggestions(), }) } @@ -472,7 +480,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { template: self.template.clone(), attribute: self.attr_path.clone(), reason: AttributeParseErrorReason::ExpectedIdentifier, - attr_style: self.attr_style, + suggestions: self.suggestions(), }) } @@ -485,7 +493,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { template: self.template.clone(), attribute: self.attr_path.clone(), reason: AttributeParseErrorReason::ExpectedNameValue(name), - attr_style: self.attr_style, + suggestions: self.suggestions(), }) } @@ -497,7 +505,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { template: self.template.clone(), attribute: self.attr_path.clone(), reason: AttributeParseErrorReason::DuplicateKey(key), - attr_style: self.attr_style, + suggestions: self.suggestions(), }) } @@ -510,7 +518,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { template: self.template.clone(), attribute: self.attr_path.clone(), reason: AttributeParseErrorReason::UnexpectedLiteral, - attr_style: self.attr_style, + suggestions: self.suggestions(), }) } @@ -521,7 +529,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { template: self.template.clone(), attribute: self.attr_path.clone(), reason: AttributeParseErrorReason::ExpectedSingleArgument, - attr_style: self.attr_style, + suggestions: self.suggestions(), }) } @@ -532,7 +540,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { template: self.template.clone(), attribute: self.attr_path.clone(), reason: AttributeParseErrorReason::ExpectedAtLeastOneArgument, - attr_style: self.attr_style, + suggestions: self.suggestions(), }) } @@ -552,7 +560,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { strings: false, list: false, }, - attr_style: self.attr_style, + suggestions: self.suggestions(), }) } @@ -573,7 +581,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { strings: false, list: true, }, - attr_style: self.attr_style, + suggestions: self.suggestions(), }) } @@ -593,7 +601,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { strings: true, list: false, }, - attr_style: self.attr_style, + suggestions: self.suggestions(), }) } @@ -605,6 +613,13 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { span, ); } + + pub(crate) fn suggestions(&self) -> Vec { + // If the outer and inner spans are equal, we are parsing an attribute from `cfg_attr`, + // So don't display an attribute style in the suggestions + let style = (self.attr_span != self.inner_span).then_some(self.attr_style); + self.template.suggestions(style, &self.attr_path) + } } impl<'f, 'sess, S: Stage> Deref for AcceptContext<'f, 'sess, S> { diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index b8ef11c26d80..953b0ebfaf04 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -142,6 +142,7 @@ impl<'sess> AttributeParser<'sess, Early> { Self::parse_single_args( sess, attr.span, + normal_attr.item.span(), attr.style, path.get_attribute_path(), target_span, @@ -159,6 +160,7 @@ impl<'sess> AttributeParser<'sess, Early> { pub fn parse_single_args( sess: &'sess Session, attr_span: Span, + inner_span: Span, attr_style: AttrStyle, attr_path: AttrPath, target_span: Span, @@ -186,6 +188,7 @@ impl<'sess> AttributeParser<'sess, Early> { }, }, attr_span, + inner_span, attr_style, template, attr_path, @@ -305,6 +308,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { emit_lint: &mut emit_lint, }, attr_span: lower_span(attr.span), + inner_span: lower_span(attr.get_normal_item().span()), attr_style: attr.style, template: &accept.template, attr_path: path.get_attribute_path(), diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 7b82f3baec09..82bd29218313 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -1,6 +1,6 @@ use std::num::IntErrorKind; -use rustc_ast::{self as ast, AttrStyle, Path}; +use rustc_ast::{self as ast, Path}; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, @@ -613,10 +613,10 @@ pub(crate) enum AttributeParseErrorReason<'a> { pub(crate) struct AttributeParseError<'a> { pub(crate) span: Span, pub(crate) attr_span: Span, - pub(crate) attr_style: AttrStyle, pub(crate) template: AttributeTemplate, pub(crate) attribute: AttrPath, pub(crate) reason: AttributeParseErrorReason<'a>, + pub(crate) suggestions: Vec, } impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> { @@ -752,16 +752,15 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> { if let Some(link) = self.template.docs { diag.note(format!("for more information, visit <{link}>")); } - let suggestions = self.template.suggestions(self.attr_style, &name); diag.span_suggestions( self.attr_span, - if suggestions.len() == 1 { + if self.suggestions.len() == 1 { "must be of the form" } else { "try changing it to one of the following valid forms of the attribute" }, - suggestions, + self.suggestions, Applicability::HasPlaceholders, ); diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 984a154853a9..d1d0bff8fe06 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1046,16 +1046,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } - &Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, ty) => { - let trait_ref = - ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::Sized, span), [ty]); - - self.prove_trait_ref( - trait_ref, - location.to_locations(), - ConstraintCategory::SizedBound, - ); - } &Rvalue::NullaryOp(NullOp::ContractChecks, _) => {} &Rvalue::NullaryOp(NullOp::UbChecks, _) => {} diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index cbdd8199d7ba..5e498bf98fb5 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -283,7 +283,7 @@ builtin_macros_requires_cfg_pattern = macro requires a cfg-pattern as an argument .label = cfg-pattern required -builtin_macros_source_uitls_expected_item = expected item, found `{$token}` +builtin_macros_source_utils_expected_item = expected item, found `{$token}` builtin_macros_takes_no_arguments = {$name} takes no arguments diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 36ce1b720446..24a71ae94389 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -610,7 +610,7 @@ impl<'a> TraitDef<'a> { defaultness: ast::Defaultness::Final, ident, generics: Generics::default(), - where_clauses: ast::TyAliasWhereClauses::default(), + after_where_clause: ast::WhereClause::default(), bounds: Vec::new(), ty: Some(type_def.to_ty(cx, self.span, type_ident, generics)), })), diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index 0993fdc5be45..d6ffbb5a4101 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -952,7 +952,7 @@ pub(crate) struct AttributeOnlyUsableWithCrateType<'a> { } #[derive(Diagnostic)] -#[diag(builtin_macros_source_uitls_expected_item)] +#[diag(builtin_macros_source_utils_expected_item)] pub(crate) struct ExpectedItem<'a> { #[primary_span] pub span: Span, diff --git a/compiler/rustc_builtin_macros/src/pattern_type.rs b/compiler/rustc_builtin_macros/src/pattern_type.rs index 8b64bdf02d3b..87a5a440140e 100644 --- a/compiler/rustc_builtin_macros/src/pattern_type.rs +++ b/compiler/rustc_builtin_macros/src/pattern_type.rs @@ -30,15 +30,21 @@ fn parse_pat_ty<'a>( let ty = parser.parse_ty()?; parser.expect_keyword(exp!(Is))?; - let pat = pat_to_ty_pat( - cx, - parser.parse_pat_no_top_guard( - None, - RecoverComma::No, - RecoverColon::No, - CommaRecoveryMode::EitherTupleOrPipe, - )?, - ); + let start = parser.token.span; + let pat = if parser.eat(exp!(Bang)) { + parser.expect_keyword(exp!(Null))?; + ty_pat(TyPatKind::NotNull, start.to(parser.token.span)) + } else { + pat_to_ty_pat( + cx, + parser.parse_pat_no_top_guard( + None, + RecoverComma::No, + RecoverColon::No, + CommaRecoveryMode::EitherTupleOrPipe, + )?, + ) + }; if parser.token != token::Eof { parser.unexpected()?; diff --git a/compiler/rustc_codegen_cranelift/example/example.rs b/compiler/rustc_codegen_cranelift/example/example.rs index aeb38331edb0..769d262b9ebb 100644 --- a/compiler/rustc_codegen_cranelift/example/example.rs +++ b/compiler/rustc_codegen_cranelift/example/example.rs @@ -72,10 +72,6 @@ pub fn debug_tuple() -> DebugTuple { DebugTuple(()) } -pub fn size_of() -> usize { - intrinsics::size_of::() -} - pub fn use_size_of() -> usize { size_of::() } diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs index 2f53bbf8b793..304d0d648561 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs @@ -6,6 +6,7 @@ extern_types, decl_macro, rustc_attrs, + rustc_private, transparent_unions, auto_traits, freeze_impls, @@ -594,7 +595,7 @@ impl, U: ?Sized> CoerceUnsized> for Box {} impl Box { pub fn new(val: T) -> Box { unsafe { - let size = intrinsics::size_of::(); + let size = size_of::(); let ptr = libc::malloc(size); intrinsics::copy(&val as *const T as *const u8, ptr, size); Box(Unique { pointer: NonNull(ptr as *const T), _marker: PhantomData }, Global) @@ -646,11 +647,11 @@ pub mod intrinsics { #[rustc_intrinsic] pub fn abort() -> !; #[rustc_intrinsic] - pub fn size_of() -> usize; + pub const fn size_of() -> usize; #[rustc_intrinsic] pub unsafe fn size_of_val(val: *const T) -> usize; #[rustc_intrinsic] - pub fn align_of() -> usize; + pub const fn align_of() -> usize; #[rustc_intrinsic] pub unsafe fn align_of_val(val: *const T) -> usize; #[rustc_intrinsic] @@ -715,6 +716,23 @@ impl Index for [T] { } } +pub const fn size_of() -> usize { + ::SIZE +} + +pub const fn align_of() -> usize { + ::ALIGN +} + +trait SizedTypeProperties: Sized { + #[lang = "mem_size_const"] + const SIZE: usize = intrinsics::size_of::(); + + #[lang = "mem_align_const"] + const ALIGN: usize = intrinsics::align_of::(); +} +impl SizedTypeProperties for T {} + extern "C" { type VaListImpl; } diff --git a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs index 86602c6b2a3f..a9388814a7f5 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs @@ -109,10 +109,10 @@ fn start( puts(*argv as *const i8); } unsafe { - puts(*((argv as usize + intrinsics::size_of::<*const u8>()) as *const *const i8)); + puts(*((argv as usize + size_of::<*const u8>()) as *const *const i8)); } unsafe { - puts(*((argv as usize + 2 * intrinsics::size_of::<*const u8>()) as *const *const i8)); + puts(*((argv as usize + 2 * size_of::<*const u8>()) as *const *const i8)); } } @@ -213,8 +213,8 @@ fn main() { assert_eq!(intrinsics::size_of_val(a) as u8, 16); assert_eq!(intrinsics::size_of_val(&0u32) as u8, 4); - assert_eq!(intrinsics::align_of::() as u8, 2); - assert_eq!(intrinsics::align_of_val(&a) as u8, intrinsics::align_of::<&str>() as u8); + assert_eq!(align_of::() as u8, 2); + assert_eq!(intrinsics::align_of_val(&a) as u8, align_of::<&str>() as u8); let u8_needs_drop = const { intrinsics::needs_drop::() }; assert!(!u8_needs_drop); diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 7e99eb09b91a..7d50548b4026 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -829,19 +829,10 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt: fx.bcx.ins().nop(); } } - Rvalue::ShallowInitBox(ref operand, content_ty) => { - let content_ty = fx.monomorphize(content_ty); - let box_layout = fx.layout_of(Ty::new_box(fx.tcx, content_ty)); - let operand = codegen_operand(fx, operand); - let operand = operand.load_scalar(fx); - lval.write_cvalue(fx, CValue::by_val(operand, box_layout)); - } Rvalue::NullaryOp(ref null_op, ty) => { assert!(lval.layout().ty.is_sized(fx.tcx, fx.typing_env())); let layout = fx.layout_of(fx.monomorphize(ty)); let val = match null_op { - NullOp::SizeOf => layout.size.bytes(), - NullOp::AlignOf => layout.align.bytes(), NullOp::OffsetOf(fields) => fx .tcx .offset_of_subfield( @@ -924,6 +915,7 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt: lval.write_cvalue_transmute(fx, operand); } Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in codegen"), + Rvalue::ShallowInitBox(..) => bug!("`ShallowInitBox` in codegen"), } } StatementKind::StorageLive(_) diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index d994f3e32ec3..c97eb3874b02 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -131,6 +131,11 @@ pub(crate) fn coerce_unsized_into<'tcx>( dst.write_cvalue(fx, CValue::by_val_pair(base, info, dst.layout())); }; match (&src_ty.kind(), &dst_ty.kind()) { + (ty::Pat(a, _), ty::Pat(b, _)) => { + let src = src.cast_pat_ty_to_base(fx.layout_of(*a)); + let dst = dst.place_transmute_type(fx, *b); + return coerce_unsized_into(fx, src, dst); + } (&ty::Ref(..), &ty::Ref(..)) | (&ty::Ref(..), &ty::RawPtr(..)) | (&ty::RawPtr(..), &ty::RawPtr(..)) => coerce_ptr(), diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index db9b80c0f6a0..9dcd4a33d44f 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -342,6 +342,14 @@ impl<'tcx> CValue<'tcx> { assert_eq!(self.layout().backend_repr, layout.backend_repr); CValue(self.0, layout) } + + pub(crate) fn cast_pat_ty_to_base(self, layout: TyAndLayout<'tcx>) -> Self { + let ty::Pat(base, _) = *self.layout().ty.kind() else { + panic!("not a pattern type: {:#?}", self.layout()) + }; + assert_eq!(layout.ty, base); + CValue(self.0, layout) + } } /// A place where you can write a value to or read a value from diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 1f342061ec59..71500ded0203 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -1,7 +1,5 @@ /* * TODO(antoyo): implement equality in libgccjit based on https://zpz.github.io/blog/overloading-equality-operator-in-cpp-class-hierarchy/ (for type equality?) - * TODO(antoyo): support #[inline] attributes. - * TODO(antoyo): support LTO (gcc's equivalent to Full LTO is -flto -flto-partition=one — https://documentation.suse.com/sbp/all/html/SBP-GCC-10/index.html). * For Thin LTO, this might be helpful: // cspell:disable-next-line * In gcc 4.6 -fwhopr was removed and became default with -flto. The non-whopr path can still be executed via -flto-partition=none. diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 680ad98593e7..e03c2868b0f1 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -39,13 +39,11 @@ trait ArgAttributesExt { const ABI_AFFECTING_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 1] = [(ArgAttribute::InReg, llvm::AttributeKind::InReg)]; -const OPTIMIZATION_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 6] = [ +const OPTIMIZATION_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 4] = [ (ArgAttribute::NoAlias, llvm::AttributeKind::NoAlias), - (ArgAttribute::CapturesAddress, llvm::AttributeKind::CapturesAddress), (ArgAttribute::NonNull, llvm::AttributeKind::NonNull), (ArgAttribute::ReadOnly, llvm::AttributeKind::ReadOnly), (ArgAttribute::NoUndef, llvm::AttributeKind::NoUndef), - (ArgAttribute::CapturesReadOnly, llvm::AttributeKind::CapturesReadOnly), ]; fn get_attrs<'ll>(this: &ArgAttributes, cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'ll Attribute; 8]> { @@ -81,15 +79,23 @@ fn get_attrs<'ll>(this: &ArgAttributes, cx: &CodegenCx<'ll, '_>) -> SmallVec<[&' } for (attr, llattr) in OPTIMIZATION_ATTRIBUTES { if regular.contains(attr) { - // captures(...) is only available since LLVM 21. - if (attr == ArgAttribute::CapturesReadOnly || attr == ArgAttribute::CapturesAddress) - && llvm_util::get_version() < (21, 0, 0) - { - continue; - } attrs.push(llattr.create_attr(cx.llcx)); } } + // captures(...) is only available since LLVM 21. + if (21, 0, 0) <= llvm_util::get_version() { + const CAPTURES_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 3] = [ + (ArgAttribute::CapturesNone, llvm::AttributeKind::CapturesNone), + (ArgAttribute::CapturesAddress, llvm::AttributeKind::CapturesAddress), + (ArgAttribute::CapturesReadOnly, llvm::AttributeKind::CapturesReadOnly), + ]; + for (attr, llattr) in CAPTURES_ATTRIBUTES { + if regular.contains(attr) { + attrs.push(llattr.create_attr(cx.llcx)); + break; + } + } + } } else if cx.tcx.sess.opts.unstable_opts.sanitizer.contains(SanitizerSet::MEMORY) { // If we're not optimising, *but* memory sanitizer is on, emit noundef, since it affects // memory sanitizer's behavior. diff --git a/compiler/rustc_codegen_llvm/src/back/command_line_args.rs b/compiler/rustc_codegen_llvm/src/back/command_line_args.rs deleted file mode 100644 index b14713969b34..000000000000 --- a/compiler/rustc_codegen_llvm/src/back/command_line_args.rs +++ /dev/null @@ -1,37 +0,0 @@ -#[cfg(test)] -mod tests; - -/// Joins command-line arguments into a single space-separated string, quoting -/// and escaping individual arguments as necessary. -/// -/// The result is intended to be informational, for embedding in debug metadata, -/// and might not be properly quoted/escaped for actual command-line use. -pub(crate) fn quote_command_line_args(args: &[String]) -> String { - // Start with a decent-sized buffer, since rustc invocations tend to be long. - let mut buf = String::with_capacity(128); - - for arg in args { - if !buf.is_empty() { - buf.push(' '); - } - - print_arg_quoted(&mut buf, arg); - } - - buf -} - -/// Equivalent to LLVM's `sys::printArg` with quoting always enabled -/// (see llvm/lib/Support/Program.cpp). -fn print_arg_quoted(buf: &mut String, arg: &str) { - buf.reserve(arg.len() + 2); - - buf.push('"'); - for ch in arg.chars() { - if matches!(ch, '"' | '\\' | '$') { - buf.push('\\'); - } - buf.push(ch); - } - buf.push('"'); -} diff --git a/compiler/rustc_codegen_llvm/src/back/command_line_args/tests.rs b/compiler/rustc_codegen_llvm/src/back/command_line_args/tests.rs deleted file mode 100644 index 69641fed3bc9..000000000000 --- a/compiler/rustc_codegen_llvm/src/back/command_line_args/tests.rs +++ /dev/null @@ -1,25 +0,0 @@ -#[test] -fn quote_command_line_args() { - use super::quote_command_line_args; - - struct Case<'a> { - args: &'a [&'a str], - expected: &'a str, - } - - let cases = &[ - Case { args: &[], expected: "" }, - Case { args: &["--hello", "world"], expected: r#""--hello" "world""# }, - Case { args: &["--hello world"], expected: r#""--hello world""# }, - Case { - args: &["plain", "$dollar", "spa ce", r"back\slash", r#""quote""#, "plain"], - expected: r#""plain" "\$dollar" "spa ce" "back\\slash" "\"quote\"" "plain""#, - }, - ]; - - for &Case { args, expected } in cases { - let args = args.iter().copied().map(str::to_owned).collect::>(); - let actual = quote_command_line_args(&args); - assert_eq!(actual, expected, "args {args:?}"); - } -} diff --git a/compiler/rustc_codegen_llvm/src/back/mod.rs b/compiler/rustc_codegen_llvm/src/back/mod.rs index fe3883e8c73e..6cb89f80ab89 100644 --- a/compiler/rustc_codegen_llvm/src/back/mod.rs +++ b/compiler/rustc_codegen_llvm/src/back/mod.rs @@ -1,5 +1,4 @@ pub(crate) mod archive; -mod command_line_args; pub(crate) mod lto; pub(crate) mod owned_target_machine; mod profiling; diff --git a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs index 903a882916ee..2972f3d5201e 100644 --- a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs +++ b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs @@ -38,8 +38,6 @@ impl OwnedTargetMachine { output_obj_file: &CStr, debug_info_compression: &CStr, use_emulated_tls: bool, - argv0: &str, - command_line_args: &str, use_wasm_eh: bool, ) -> Result> { // SAFETY: llvm::LLVMRustCreateTargetMachine copies pointed to data @@ -66,10 +64,6 @@ impl OwnedTargetMachine { output_obj_file.as_ptr(), debug_info_compression.as_ptr(), use_emulated_tls, - argv0.as_ptr(), - argv0.len(), - command_line_args.as_ptr(), - command_line_args.len(), use_wasm_eh, ) }; diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 6ce1cbe072c0..d18030b1574c 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -31,7 +31,6 @@ use rustc_span::{BytePos, InnerSpan, Pos, SpanData, SyntaxContext, sym}; use rustc_target::spec::{CodeModel, FloatAbi, RelocModel, SanitizerSet, SplitDebuginfo, TlsModel}; use tracing::{debug, trace}; -use crate::back::command_line_args::quote_command_line_args; use crate::back::lto::ThinBuffer; use crate::back::owned_target_machine::OwnedTargetMachine; use crate::back::profiling::{ @@ -253,19 +252,6 @@ pub(crate) fn target_machine_factory( let use_emulated_tls = matches!(sess.tls_model(), TlsModel::Emulated); - // Command-line information to be included in the target machine. - // This seems to only be used for embedding in PDB debuginfo files. - // FIXME(Zalathar): Maybe skip this for non-PDB targets? - let argv0 = std::env::current_exe() - .unwrap_or_default() - .into_os_string() - .into_string() - .unwrap_or_default(); - let command_line_args = quote_command_line_args(&sess.expanded_args); - // Self-profile counter for the number of bytes produced by command-line quoting. - // Values are summed, so the summary result is cumulative across all TM factories. - sess.prof.artifact_size("quoted_command_line_args", "-", command_line_args.len() as u64); - let debuginfo_compression = sess.opts.debuginfo_compression.to_string(); match sess.opts.debuginfo_compression { rustc_session::config::DebugInfoCompression::Zlib => { @@ -326,8 +312,6 @@ pub(crate) fn target_machine_factory( &output_obj_file, &debuginfo_compression, use_emulated_tls, - &argv0, - &command_line_args, use_wasm_eh, ) }) @@ -358,7 +342,7 @@ fn write_bitcode_to_file(module: &ModuleCodegen, path: &Path) { } } -/// In what context is a dignostic handler being attached to a codegen unit? +/// In what context is a diagnostic handler being attached to a codegen unit? pub(crate) enum CodegenDiagnosticsStage { /// Prelink optimization stage. Opt, diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index c12383f19312..14b3f3626efe 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -1325,6 +1325,8 @@ fn generic_simd_intrinsic<'ll, 'tcx>( }; } + let llvm_version = crate::llvm_util::get_version(); + /// Converts a vector mask, where each element has a bit width equal to the data elements it is used with, /// down to an i1 based mask that can be used by llvm intrinsics. /// @@ -1808,7 +1810,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( ); // Alignment of T, must be a constant integer value: - let alignment = bx.const_i32(bx.align_of(in_elem).bytes() as i32); + let alignment = bx.align_of(in_elem).bytes(); // Truncate the mask vector to a vector of i1s: let mask = vector_mask_to_bitmask(bx, args[2].immediate(), mask_elem_bitwidth, in_len); @@ -1819,11 +1821,23 @@ fn generic_simd_intrinsic<'ll, 'tcx>( // Type of the vector of elements: let llvm_elem_vec_ty = llvm_vector_ty(bx, element_ty0, in_len); - return Ok(bx.call_intrinsic( - "llvm.masked.gather", - &[llvm_elem_vec_ty, llvm_pointer_vec_ty], - &[args[1].immediate(), alignment, mask, args[0].immediate()], - )); + let args: &[&'ll Value] = if llvm_version < (22, 0, 0) { + let alignment = bx.const_i32(alignment as i32); + &[args[1].immediate(), alignment, mask, args[0].immediate()] + } else { + &[args[1].immediate(), mask, args[0].immediate()] + }; + + let call = + bx.call_intrinsic("llvm.masked.gather", &[llvm_elem_vec_ty, llvm_pointer_vec_ty], args); + if llvm_version >= (22, 0, 0) { + crate::attributes::apply_to_callsite( + call, + crate::llvm::AttributePlace::Argument(0), + &[crate::llvm::CreateAlignmentAttr(bx.llcx, alignment)], + ) + } + return Ok(call); } if name == sym::simd_masked_load { @@ -1891,18 +1905,30 @@ fn generic_simd_intrinsic<'ll, 'tcx>( let mask = vector_mask_to_bitmask(bx, args[0].immediate(), m_elem_bitwidth, mask_len); // Alignment of T, must be a constant integer value: - let alignment = bx.const_i32(bx.align_of(values_elem).bytes() as i32); + let alignment = bx.align_of(values_elem).bytes(); let llvm_pointer = bx.type_ptr(); // Type of the vector of elements: let llvm_elem_vec_ty = llvm_vector_ty(bx, values_elem, values_len); - return Ok(bx.call_intrinsic( - "llvm.masked.load", - &[llvm_elem_vec_ty, llvm_pointer], - &[args[1].immediate(), alignment, mask, args[2].immediate()], - )); + let args: &[&'ll Value] = if llvm_version < (22, 0, 0) { + let alignment = bx.const_i32(alignment as i32); + + &[args[1].immediate(), alignment, mask, args[2].immediate()] + } else { + &[args[1].immediate(), mask, args[2].immediate()] + }; + + let call = bx.call_intrinsic("llvm.masked.load", &[llvm_elem_vec_ty, llvm_pointer], args); + if llvm_version >= (22, 0, 0) { + crate::attributes::apply_to_callsite( + call, + crate::llvm::AttributePlace::Argument(0), + &[crate::llvm::CreateAlignmentAttr(bx.llcx, alignment)], + ) + } + return Ok(call); } if name == sym::simd_masked_store { @@ -1964,18 +1990,29 @@ fn generic_simd_intrinsic<'ll, 'tcx>( let mask = vector_mask_to_bitmask(bx, args[0].immediate(), m_elem_bitwidth, mask_len); // Alignment of T, must be a constant integer value: - let alignment = bx.const_i32(bx.align_of(values_elem).bytes() as i32); + let alignment = bx.align_of(values_elem).bytes(); let llvm_pointer = bx.type_ptr(); // Type of the vector of elements: let llvm_elem_vec_ty = llvm_vector_ty(bx, values_elem, values_len); - return Ok(bx.call_intrinsic( - "llvm.masked.store", - &[llvm_elem_vec_ty, llvm_pointer], - &[args[2].immediate(), args[1].immediate(), alignment, mask], - )); + let args: &[&'ll Value] = if llvm_version < (22, 0, 0) { + let alignment = bx.const_i32(alignment as i32); + &[args[2].immediate(), args[1].immediate(), alignment, mask] + } else { + &[args[2].immediate(), args[1].immediate(), mask] + }; + + let call = bx.call_intrinsic("llvm.masked.store", &[llvm_elem_vec_ty, llvm_pointer], args); + if llvm_version >= (22, 0, 0) { + crate::attributes::apply_to_callsite( + call, + crate::llvm::AttributePlace::Argument(1), + &[crate::llvm::CreateAlignmentAttr(bx.llcx, alignment)], + ) + } + return Ok(call); } if name == sym::simd_scatter { @@ -2040,7 +2077,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( ); // Alignment of T, must be a constant integer value: - let alignment = bx.const_i32(bx.align_of(in_elem).bytes() as i32); + let alignment = bx.align_of(in_elem).bytes(); // Truncate the mask vector to a vector of i1s: let mask = vector_mask_to_bitmask(bx, args[2].immediate(), mask_elem_bitwidth, in_len); @@ -2050,12 +2087,25 @@ fn generic_simd_intrinsic<'ll, 'tcx>( // Type of the vector of elements: let llvm_elem_vec_ty = llvm_vector_ty(bx, element_ty0, in_len); - - return Ok(bx.call_intrinsic( + let args: &[&'ll Value] = if llvm_version < (22, 0, 0) { + let alignment = bx.const_i32(alignment as i32); + &[args[0].immediate(), args[1].immediate(), alignment, mask] + } else { + &[args[0].immediate(), args[1].immediate(), mask] + }; + let call = bx.call_intrinsic( "llvm.masked.scatter", &[llvm_elem_vec_ty, llvm_pointer_vec_ty], - &[args[0].immediate(), args[1].immediate(), alignment, mask], - )); + args, + ); + if llvm_version >= (22, 0, 0) { + crate::attributes::apply_to_callsite( + call, + crate::llvm::AttributePlace::Argument(1), + &[crate::llvm::CreateAlignmentAttr(bx.llcx, alignment)], + ) + } + return Ok(call); } macro_rules! arith_red { diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 284e2ec50f5e..53f0f9ff9d01 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -289,6 +289,7 @@ pub(crate) enum AttributeKind { DeadOnUnwind = 43, DeadOnReturn = 44, CapturesReadOnly = 45, + CapturesNone = 46, } /// LLVMIntPredicate @@ -2330,10 +2331,6 @@ unsafe extern "C" { OutputObjFile: *const c_char, DebugInfoCompression: *const c_char, UseEmulatedTls: bool, - Argv0: *const c_uchar, // See "PTR_LEN_STR". - Argv0Len: size_t, - CommandLineArgs: *const c_uchar, // See "PTR_LEN_STR". - CommandLineArgsLen: size_t, UseWasmEH: bool, ) -> *mut TargetMachine; diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index cbaf67d73454..7518bbaf16a1 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -15,8 +15,8 @@ use rustc_data_structures::profiling::{SelfProfilerRef, VerboseTimingGuard}; use rustc_errors::emitter::Emitter; use rustc_errors::translation::Translator; use rustc_errors::{ - Diag, DiagArgMap, DiagCtxt, DiagMessage, ErrCode, FatalErrorMarker, Level, MultiSpan, Style, - Suggestions, + Diag, DiagArgMap, DiagCtxt, DiagMessage, ErrCode, FatalError, FatalErrorMarker, Level, + MultiSpan, Style, Suggestions, }; use rustc_fs_util::link_or_copy; use rustc_incremental::{ @@ -346,12 +346,6 @@ pub struct CodegenContext { pub split_dwarf_kind: rustc_session::config::SplitDwarfKind, pub pointer_size: Size, - /// All commandline args used to invoke the compiler, with @file args fully expanded. - /// This will only be used within debug info, e.g. in the pdb file on windows - /// This is mainly useful for other tools that reads that debuginfo to figure out - /// how to call the compiler with the same arguments. - pub expanded_args: Vec, - /// Emitter to use for diagnostics produced during codegen. pub diag_emitter: SharedEmitter, /// LLVM optimizations for which we want to print remarks. @@ -380,7 +374,7 @@ fn generate_thin_lto_work( each_linked_rlib_for_lto: &[PathBuf], needs_thin_lto: Vec<(String, B::ThinBuffer)>, import_only_modules: Vec<(SerializedModule, WorkProduct)>, -) -> Vec<(WorkItem, u64)> { +) -> Vec<(ThinLtoWorkItem, u64)> { let _prof_timer = cgcx.prof.generic_activity("codegen_thin_generate_lto_work"); let (lto_modules, copy_jobs) = B::run_thin_lto( @@ -394,11 +388,11 @@ fn generate_thin_lto_work( .into_iter() .map(|module| { let cost = module.cost(); - (WorkItem::ThinLto(module), cost) + (ThinLtoWorkItem::ThinLto(module), cost) }) .chain(copy_jobs.into_iter().map(|wp| { ( - WorkItem::CopyPostLtoArtifacts(CachedModuleCodegen { + ThinLtoWorkItem::CopyPostLtoArtifacts(CachedModuleCodegen { name: wp.cgu_name.clone(), source: wp, }), @@ -703,64 +697,73 @@ pub(crate) enum WorkItem { /// Copy the post-LTO artifacts from the incremental cache to the output /// directory. CopyPostLtoArtifacts(CachedModuleCodegen), - /// Performs fat LTO on the given module. - FatLto { - exported_symbols_for_lto: Arc>, - each_linked_rlib_for_lto: Vec, - needs_fat_lto: Vec>, - import_only_modules: Vec<(SerializedModule, WorkProduct)>, - }, +} + +enum ThinLtoWorkItem { + /// Copy the post-LTO artifacts from the incremental cache to the output + /// directory. + CopyPostLtoArtifacts(CachedModuleCodegen), /// Performs thin-LTO on the given module. ThinLto(lto::ThinModule), } +// `pthread_setname()` on *nix ignores anything beyond the first 15 +// bytes. Use short descriptions to maximize the space available for +// the module name. +#[cfg(not(windows))] +fn desc(short: &str, _long: &str, name: &str) -> String { + // The short label is three bytes, and is followed by a space. That + // leaves 11 bytes for the CGU name. How we obtain those 11 bytes + // depends on the CGU name form. + // + // - Non-incremental, e.g. `regex.f10ba03eb5ec7975-cgu.0`: the part + // before the `-cgu.0` is the same for every CGU, so use the + // `cgu.0` part. The number suffix will be different for each + // CGU. + // + // - Incremental (normal), e.g. `2i52vvl2hco29us0`: use the whole + // name because each CGU will have a unique ASCII hash, and the + // first 11 bytes will be enough to identify it. + // + // - Incremental (with `-Zhuman-readable-cgu-names`), e.g. + // `regex.f10ba03eb5ec7975-re_builder.volatile`: use the whole + // name. The first 11 bytes won't be enough to uniquely identify + // it, but no obvious substring will, and this is a rarely used + // option so it doesn't matter much. + // + assert_eq!(short.len(), 3); + let name = if let Some(index) = name.find("-cgu.") { + &name[index + 1..] // +1 skips the leading '-'. + } else { + name + }; + format!("{short} {name}") +} + +// Windows has no thread name length limit, so use more descriptive names. +#[cfg(windows)] +fn desc(_short: &str, long: &str, name: &str) -> String { + format!("{long} {name}") +} + impl WorkItem { /// Generate a short description of this work item suitable for use as a thread name. fn short_description(&self) -> String { - // `pthread_setname()` on *nix ignores anything beyond the first 15 - // bytes. Use short descriptions to maximize the space available for - // the module name. - #[cfg(not(windows))] - fn desc(short: &str, _long: &str, name: &str) -> String { - // The short label is three bytes, and is followed by a space. That - // leaves 11 bytes for the CGU name. How we obtain those 11 bytes - // depends on the CGU name form. - // - // - Non-incremental, e.g. `regex.f10ba03eb5ec7975-cgu.0`: the part - // before the `-cgu.0` is the same for every CGU, so use the - // `cgu.0` part. The number suffix will be different for each - // CGU. - // - // - Incremental (normal), e.g. `2i52vvl2hco29us0`: use the whole - // name because each CGU will have a unique ASCII hash, and the - // first 11 bytes will be enough to identify it. - // - // - Incremental (with `-Zhuman-readable-cgu-names`), e.g. - // `regex.f10ba03eb5ec7975-re_builder.volatile`: use the whole - // name. The first 11 bytes won't be enough to uniquely identify - // it, but no obvious substring will, and this is a rarely used - // option so it doesn't matter much. - // - assert_eq!(short.len(), 3); - let name = if let Some(index) = name.find("-cgu.") { - &name[index + 1..] // +1 skips the leading '-'. - } else { - name - }; - format!("{short} {name}") - } - - // Windows has no thread name length limit, so use more descriptive names. - #[cfg(windows)] - fn desc(_short: &str, long: &str, name: &str) -> String { - format!("{long} {name}") - } - match self { WorkItem::Optimize(m) => desc("opt", "optimize module", &m.name), WorkItem::CopyPostLtoArtifacts(m) => desc("cpy", "copy LTO artifacts for", &m.name), - WorkItem::FatLto { .. } => desc("lto", "fat LTO module", "everything"), - WorkItem::ThinLto(m) => desc("lto", "thin-LTO module", m.name()), + } + } +} + +impl ThinLtoWorkItem { + /// Generate a short description of this work item suitable for use as a thread name. + fn short_description(&self) -> String { + match self { + ThinLtoWorkItem::CopyPostLtoArtifacts(m) => { + desc("cpy", "copy LTO artifacts for", &m.name) + } + ThinLtoWorkItem::ThinLto(m) => desc("lto", "thin-LTO module", m.name()), } } } @@ -891,7 +894,7 @@ fn execute_optimize_work_item( fn execute_copy_from_cache_work_item( cgcx: &CodegenContext, module: CachedModuleCodegen, -) -> WorkItemResult { +) -> CompiledModule { let _timer = cgcx .prof .generic_activity_with_arg("codegen_copy_artifacts_from_incr_cache", &*module.name); @@ -964,7 +967,7 @@ fn execute_copy_from_cache_work_item( cgcx.create_dcx().handle().emit_fatal(errors::NoSavedObjectFile { cgu_name: &module.name }) } - WorkItemResult::Finished(CompiledModule { + CompiledModule { links_from_incr_cache, kind: ModuleKind::Regular, name: module.name, @@ -973,17 +976,19 @@ fn execute_copy_from_cache_work_item( bytecode, assembly, llvm_ir, - }) + } } -fn execute_fat_lto_work_item( +fn do_fat_lto( cgcx: &CodegenContext, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], mut needs_fat_lto: Vec>, import_only_modules: Vec<(SerializedModule, WorkProduct)>, -) -> WorkItemResult { - let _timer = cgcx.prof.generic_activity_with_arg("codegen_module_perform_lto", "everything"); +) -> CompiledModule { + let _timer = cgcx.prof.verbose_generic_activity("LLVM_fatlto"); + + check_lto_allowed(&cgcx); for (module, wp) in import_only_modules { needs_fat_lto.push(FatLtoInput::Serialized { name: wp.cgu_name, buffer: module }) @@ -995,19 +1000,155 @@ fn execute_fat_lto_work_item( each_linked_rlib_for_lto, needs_fat_lto, ); - let module = B::codegen(cgcx, module, &cgcx.module_config); - WorkItemResult::Finished(module) + B::codegen(cgcx, module, &cgcx.module_config) +} + +fn do_thin_lto<'a, B: ExtraBackendMethods>( + cgcx: &'a CodegenContext, + exported_symbols_for_lto: Arc>, + each_linked_rlib_for_lto: Vec, + needs_thin_lto: Vec<(String, ::ThinBuffer)>, + lto_import_only_modules: Vec<( + SerializedModule<::ModuleBuffer>, + WorkProduct, + )>, +) -> Vec { + let _timer = cgcx.prof.verbose_generic_activity("LLVM_thinlto"); + + check_lto_allowed(&cgcx); + + let (coordinator_send, coordinator_receive) = channel(); + + // First up, convert our jobserver into a helper thread so we can use normal + // mpsc channels to manage our messages and such. + // After we've requested tokens then we'll, when we can, + // get tokens on `coordinator_receive` which will + // get managed in the main loop below. + let coordinator_send2 = coordinator_send.clone(); + let helper = jobserver::client() + .into_helper_thread(move |token| { + drop(coordinator_send2.send(ThinLtoMessage::Token(token))); + }) + .expect("failed to spawn helper thread"); + + let mut work_items = vec![]; + + // We have LTO work to do. Perform the serial work here of + // figuring out what we're going to LTO and then push a + // bunch of work items onto our queue to do LTO. This all + // happens on the coordinator thread but it's very quick so + // we don't worry about tokens. + for (work, cost) in generate_thin_lto_work( + cgcx, + &exported_symbols_for_lto, + &each_linked_rlib_for_lto, + needs_thin_lto, + lto_import_only_modules, + ) { + let insertion_index = + work_items.binary_search_by_key(&cost, |&(_, cost)| cost).unwrap_or_else(|e| e); + work_items.insert(insertion_index, (work, cost)); + if cgcx.parallel { + helper.request_token(); + } + } + + let mut codegen_aborted = None; + + // These are the Jobserver Tokens we currently hold. Does not include + // the implicit Token the compiler process owns no matter what. + let mut tokens = vec![]; + + // Amount of tokens that are used (including the implicit token). + let mut used_token_count = 0; + + let mut compiled_modules = vec![]; + + // Run the message loop while there's still anything that needs message + // processing. Note that as soon as codegen is aborted we simply want to + // wait for all existing work to finish, so many of the conditions here + // only apply if codegen hasn't been aborted as they represent pending + // work to be done. + loop { + if codegen_aborted.is_none() { + if used_token_count == 0 && work_items.is_empty() { + // All codegen work is done. + break; + } + + // Spin up what work we can, only doing this while we've got available + // parallelism slots and work left to spawn. + while used_token_count < tokens.len() + 1 + && let Some((item, _)) = work_items.pop() + { + spawn_thin_lto_work(&cgcx, coordinator_send.clone(), item); + used_token_count += 1; + } + } else { + // Don't queue up any more work if codegen was aborted, we're + // just waiting for our existing children to finish. + if used_token_count == 0 { + break; + } + } + + // Relinquish accidentally acquired extra tokens. Subtract 1 for the implicit token. + tokens.truncate(used_token_count.saturating_sub(1)); + + match coordinator_receive.recv().unwrap() { + // Save the token locally and the next turn of the loop will use + // this to spawn a new unit of work, or it may get dropped + // immediately if we have no more work to spawn. + ThinLtoMessage::Token(token) => match token { + Ok(token) => { + tokens.push(token); + } + Err(e) => { + let msg = &format!("failed to acquire jobserver token: {e}"); + cgcx.diag_emitter.fatal(msg); + codegen_aborted = Some(FatalError); + } + }, + + ThinLtoMessage::WorkItem { result } => { + // If a thread exits successfully then we drop a token associated + // with that worker and update our `used_token_count` count. + // We may later re-acquire a token to continue running more work. + // We may also not actually drop a token here if the worker was + // running with an "ephemeral token". + used_token_count -= 1; + + match result { + Ok(compiled_module) => compiled_modules.push(compiled_module), + Err(Some(WorkerFatalError)) => { + // Like `CodegenAborted`, wait for remaining work to finish. + codegen_aborted = Some(FatalError); + } + Err(None) => { + // If the thread failed that means it panicked, so + // we abort immediately. + bug!("worker thread panicked"); + } + } + } + } + } + + if let Some(codegen_aborted) = codegen_aborted { + codegen_aborted.raise(); + } + + compiled_modules } fn execute_thin_lto_work_item( cgcx: &CodegenContext, module: lto::ThinModule, -) -> WorkItemResult { +) -> CompiledModule { let _timer = cgcx.prof.generic_activity_with_arg("codegen_module_perform_lto", module.name()); let module = B::optimize_thin(cgcx, module); - let module = B::codegen(cgcx, module, &cgcx.module_config); - WorkItemResult::Finished(module) + B::codegen(cgcx, module, &cgcx.module_config) } /// Messages sent to the coordinator. @@ -1041,6 +1182,17 @@ pub(crate) enum Message { CodegenAborted, } +/// Messages sent to the coordinator. +pub(crate) enum ThinLtoMessage { + /// A jobserver token has become available. Sent from the jobserver helper + /// thread. + Token(io::Result), + + /// The backend has finished processing a work item for a codegen unit. + /// Sent from a backend worker thread. + WorkItem { result: Result> }, +} + /// A message sent from the coordinator thread to the main thread telling it to /// process another codegen unit. pub struct CguMessage; @@ -1092,9 +1244,8 @@ fn start_executing_work( regular_config: Arc, allocator_config: Arc, allocator_module: Option>, - tx_to_llvm_workers: Sender>, + coordinator_send: Sender>, ) -> thread::JoinHandle> { - let coordinator_send = tx_to_llvm_workers; let sess = tcx.sess; let mut each_linked_rlib_for_lto = Vec::new(); @@ -1153,7 +1304,6 @@ fn start_executing_work( remark: sess.opts.cg.remark.clone(), remark_dir, incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()), - expanded_args: tcx.sess.expanded_args.clone(), diag_emitter: shared_emitter.clone(), output_filenames: Arc::clone(tcx.output_filenames(())), module_config: regular_config, @@ -1314,7 +1464,6 @@ fn start_executing_work( let mut needs_fat_lto = Vec::new(); let mut needs_thin_lto = Vec::new(); let mut lto_import_only_modules = Vec::new(); - let mut started_lto = false; /// Possible state transitions: /// - Ongoing -> Completed @@ -1404,63 +1553,8 @@ fn start_executing_work( if running_with_any_token(main_thread_state, running_with_own_token) == 0 && work_items.is_empty() { - // All codegen work is done. Do we have LTO work to do? - if needs_fat_lto.is_empty() - && needs_thin_lto.is_empty() - && lto_import_only_modules.is_empty() - { - // Nothing more to do! - break; - } - - // We have LTO work to do. Perform the serial work here of - // figuring out what we're going to LTO and then push a - // bunch of work items onto our queue to do LTO. This all - // happens on the coordinator thread but it's very quick so - // we don't worry about tokens. - assert!(!started_lto); - started_lto = true; - - let needs_fat_lto = mem::take(&mut needs_fat_lto); - let needs_thin_lto = mem::take(&mut needs_thin_lto); - let import_only_modules = mem::take(&mut lto_import_only_modules); - let each_linked_rlib_file_for_lto = - mem::take(&mut each_linked_rlib_file_for_lto); - - check_lto_allowed(&cgcx); - - if !needs_fat_lto.is_empty() { - assert!(needs_thin_lto.is_empty()); - - work_items.push(( - WorkItem::FatLto { - exported_symbols_for_lto: Arc::clone(&exported_symbols_for_lto), - each_linked_rlib_for_lto: each_linked_rlib_file_for_lto, - needs_fat_lto, - import_only_modules, - }, - 0, - )); - if cgcx.parallel { - helper.request_token(); - } - } else { - for (work, cost) in generate_thin_lto_work( - &cgcx, - &exported_symbols_for_lto, - &each_linked_rlib_file_for_lto, - needs_thin_lto, - import_only_modules, - ) { - let insertion_index = work_items - .binary_search_by_key(&cost, |&(_, cost)| cost) - .unwrap_or_else(|e| e); - work_items.insert(insertion_index, (work, cost)); - if cgcx.parallel { - helper.request_token(); - } - } - } + // All codegen work is done. + break; } // In this branch, we know that everything has been codegened, @@ -1598,12 +1692,10 @@ fn start_executing_work( compiled_modules.push(compiled_module); } Ok(WorkItemResult::NeedsFatLto(fat_lto_input)) => { - assert!(!started_lto); assert!(needs_thin_lto.is_empty()); needs_fat_lto.push(fat_lto_input); } Ok(WorkItemResult::NeedsThinLto(name, thin_buffer)) => { - assert!(!started_lto); assert!(needs_fat_lto.is_empty()); needs_thin_lto.push((name, thin_buffer)); } @@ -1620,7 +1712,6 @@ fn start_executing_work( } Message::AddImportOnlyModule { module_data, work_product } => { - assert!(!started_lto); assert_eq!(codegen_state, Ongoing); assert_eq!(main_thread_state, MainThreadState::Codegenning); lto_import_only_modules.push((module_data, work_product)); @@ -1629,12 +1720,43 @@ fn start_executing_work( } } + // Drop to print timings + drop(llvm_start_time); + if codegen_state == Aborted { return Err(()); } - // Drop to print timings - drop(llvm_start_time); + drop(codegen_state); + drop(tokens); + drop(helper); + assert!(work_items.is_empty()); + + if !needs_fat_lto.is_empty() { + assert!(compiled_modules.is_empty()); + assert!(needs_thin_lto.is_empty()); + + // This uses the implicit token + let module = do_fat_lto( + &cgcx, + &exported_symbols_for_lto, + &each_linked_rlib_file_for_lto, + needs_fat_lto, + lto_import_only_modules, + ); + compiled_modules.push(module); + } else if !needs_thin_lto.is_empty() || !lto_import_only_modules.is_empty() { + assert!(compiled_modules.is_empty()); + assert!(needs_fat_lto.is_empty()); + + compiled_modules.extend(do_thin_lto( + &cgcx, + exported_symbols_for_lto, + each_linked_rlib_file_for_lto, + needs_thin_lto, + lto_import_only_modules, + )); + } // Regardless of what order these modules completed in, report them to // the backend in the same order every time to ensure that we're handing @@ -1725,20 +1847,9 @@ fn spawn_work<'a, B: ExtraBackendMethods>( B::spawn_named_thread(cgcx.time_trace, work.short_description(), move || { let result = std::panic::catch_unwind(AssertUnwindSafe(|| match work { WorkItem::Optimize(m) => execute_optimize_work_item(&cgcx, m), - WorkItem::CopyPostLtoArtifacts(m) => execute_copy_from_cache_work_item(&cgcx, m), - WorkItem::FatLto { - exported_symbols_for_lto, - each_linked_rlib_for_lto, - needs_fat_lto, - import_only_modules, - } => execute_fat_lto_work_item( - &cgcx, - &exported_symbols_for_lto, - &each_linked_rlib_for_lto, - needs_fat_lto, - import_only_modules, - ), - WorkItem::ThinLto(m) => execute_thin_lto_work_item(&cgcx, m), + WorkItem::CopyPostLtoArtifacts(m) => { + WorkItemResult::Finished(execute_copy_from_cache_work_item(&cgcx, m)) + } })); let msg = match result { @@ -1758,6 +1869,36 @@ fn spawn_work<'a, B: ExtraBackendMethods>( .expect("failed to spawn work thread"); } +fn spawn_thin_lto_work<'a, B: ExtraBackendMethods>( + cgcx: &'a CodegenContext, + coordinator_send: Sender, + work: ThinLtoWorkItem, +) { + let cgcx = cgcx.clone(); + + B::spawn_named_thread(cgcx.time_trace, work.short_description(), move || { + let result = std::panic::catch_unwind(AssertUnwindSafe(|| match work { + ThinLtoWorkItem::CopyPostLtoArtifacts(m) => execute_copy_from_cache_work_item(&cgcx, m), + ThinLtoWorkItem::ThinLto(m) => execute_thin_lto_work_item(&cgcx, m), + })); + + let msg = match result { + Ok(result) => ThinLtoMessage::WorkItem { result: Ok(result) }, + + // We ignore any `FatalError` coming out of `execute_work_item`, as a + // diagnostic was already sent off to the main thread - just surface + // that there was an error in this worker. + Err(err) if err.is::() => { + ThinLtoMessage::WorkItem { result: Err(Some(WorkerFatalError)) } + } + + Err(_) => ThinLtoMessage::WorkItem { result: Err(None) }, + }; + drop(coordinator_send.send(msg)); + }) + .expect("failed to spawn work thread"); +} + enum SharedEmitterMessage { Diagnostic(Diagnostic), InlineAsmError(SpanData, String, Level, Option<(String, Vec)>), diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index ecb1750ddfd3..c35b05f798ea 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -228,6 +228,7 @@ pub(crate) fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( ) -> (Bx::Value, Bx::Value) { debug!("unsize_ptr: {:?} => {:?}", src_ty, dst_ty); match (src_ty.kind(), dst_ty.kind()) { + (&ty::Pat(a, _), &ty::Pat(b, _)) => unsize_ptr(bx, src, a, b, old_info), (&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(b, _)) | (&ty::RawPtr(a, _), &ty::RawPtr(b, _)) => { assert_eq!(bx.cx().type_is_sized(a), old_info.is_none()); diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index befa00c6861e..cc3316c7f8cc 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -120,8 +120,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { | sym::atomic_singlethreadfence | sym::caller_location => {} _ => { - span_bug!(span, "nullary intrinsic {name} must either be in a const block or explicitly opted out because it is inherently a runtime intrinsic -"); + span_bug!( + span, + "Nullary intrinsic {name} must be called in a const block. \ + If you are seeing this message from code outside the standard library, the \ + unstable implementation details of the relevant intrinsic may have changed. \ + Consider using stable APIs instead. \ + If you are adding a new nullary intrinsic that is inherently a runtime \ + intrinsic, update this check." + ); } } } diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 7a6cb2a51e31..640f7211dc99 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -611,16 +611,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let ty = self.monomorphize(ty); let layout = bx.cx().layout_of(ty); let val = match null_op { - mir::NullOp::SizeOf => { - assert!(bx.cx().type_is_sized(ty)); - let val = layout.size.bytes(); - bx.cx().const_usize(val) - } - mir::NullOp::AlignOf => { - assert!(bx.cx().type_is_sized(ty)); - let val = layout.align.bytes(); - bx.cx().const_usize(val) - } mir::NullOp::OffsetOf(fields) => { let val = bx .tcx() @@ -724,15 +714,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } } - mir::Rvalue::ShallowInitBox(ref operand, content_ty) => { - let operand = self.codegen_operand(bx, operand); - let val = operand.immediate(); - - let content_ty = self.monomorphize(content_ty); - let box_layout = bx.cx().layout_of(Ty::new_box(bx.tcx(), content_ty)); - - OperandRef { val: OperandValue::Immediate(val), layout: box_layout } - } mir::Rvalue::WrapUnsafeBinder(ref operand, binder_ty) => { let operand = self.codegen_operand(bx, operand); let binder_ty = self.monomorphize(binder_ty); @@ -740,6 +721,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { OperandRef { val: operand.val, layout } } mir::Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in codegen"), + mir::Rvalue::ShallowInitBox(..) => bug!("`ShallowInitBox` in codegen"), } } diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 2c6dd5bd01f9..ca173fe26c2f 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -646,11 +646,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { Rvalue::Cast(_, _, _) => {} Rvalue::NullaryOp( - NullOp::SizeOf - | NullOp::AlignOf - | NullOp::OffsetOf(_) - | NullOp::UbChecks - | NullOp::ContractChecks, + NullOp::OffsetOf(_) | NullOp::UbChecks | NullOp::ContractChecks, _, ) => {} Rvalue::ShallowInitBox(_, _) => {} diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 4da2663319c3..bf5787c86bd5 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -414,8 +414,6 @@ fn report_eval_error<'tcx>( let (error, backtrace) = error.into_parts(); backtrace.print_backtrace(); - let instance = with_no_trimmed_paths!(cid.instance.to_string()); - super::report( ecx, error, @@ -430,7 +428,7 @@ fn report_eval_error<'tcx>( diag.subdiagnostic(frame); } // Add after the frame rendering above, as it adds its own `instance` args. - diag.arg("instance", instance); + diag.arg("instance", with_no_trimmed_paths!(cid.instance.to_string())); diag.arg("num_frames", num_frames); }, ) diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 23e4a2921ea6..312ebe7ddd09 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -127,7 +127,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } else if all_fields_1zst(def.variant(var1))? { def.variant(var0) } else { - // No varant is all-1-ZST, so no NPO. + // No variant is all-1-ZST, so no NPO. return interp_ok(layout); }; // The "relevant" variant must have exactly one field, and its type is the "inner" type. diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index b058d4b8ad44..cf5ee03bedae 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -466,6 +466,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx> { trace!("Unsizing {:?} of type {} into {}", *src, src.layout.ty, cast_ty.ty); match (src.layout.ty.kind(), cast_ty.ty.kind()) { + (&ty::Pat(_, s_pat), &ty::Pat(cast_ty, c_pat)) if s_pat == c_pat => { + let src = self.project_field(src, FieldIdx::ZERO)?; + let dest = self.project_field(dest, FieldIdx::ZERO)?; + let cast_ty = self.layout_of(cast_ty)?; + self.unsize_into(&src, cast_ty, &dest) + } (&ty::Ref(_, s, _), &ty::Ref(_, c, _) | &ty::RawPtr(c, _)) | (&ty::RawPtr(s, _), &ty::RawPtr(c, _)) => self.unsize_into_ptr(src, dest, s, c), (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index fc7f1166af99..f0f06d469c1e 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -156,6 +156,24 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let b_ty = self.read_type_id(&args[1])?; self.write_scalar(Scalar::from_bool(a_ty == b_ty), dest)?; } + sym::size_of => { + let tp_ty = instance.args.type_at(0); + let layout = self.layout_of(tp_ty)?; + if !layout.is_sized() { + span_bug!(self.cur_span(), "unsized type for `size_of`"); + } + let val = layout.size.bytes(); + self.write_scalar(Scalar::from_target_usize(val, self), dest)?; + } + sym::align_of => { + let tp_ty = instance.args.type_at(0); + let layout = self.layout_of(tp_ty)?; + if !layout.is_sized() { + span_bug!(self.cur_span(), "unsized type for `align_of`"); + } + let val = layout.align.bytes(); + self.write_scalar(Scalar::from_target_usize(val, self), dest)?; + } sym::variant_count => { let tp_ty = instance.args.type_at(0); let ty = match tp_ty.kind() { diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index f0819423aa0f..58b90abf0129 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -517,20 +517,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let usize_layout = || self.layout_of(self.tcx.types.usize).unwrap(); interp_ok(match null_op { - SizeOf => { - if !layout.is_sized() { - span_bug!(self.cur_span(), "unsized type for `NullaryOp::SizeOf`"); - } - let val = layout.size.bytes(); - ImmTy::from_uint(val, usize_layout()) - } - AlignOf => { - if !layout.is_sized() { - span_bug!(self.cur_span(), "unsized type for `NullaryOp::AlignOf`"); - } - let val = layout.align.bytes(); - ImmTy::from_uint(val, usize_layout()) - } OffsetOf(fields) => { let val = self.tcx.offset_of_subfield(self.typing_env, layout, fields.iter()).bytes(); diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index 2fd1657f6ba3..027e634ef7f7 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -2,7 +2,7 @@ //! //! OpTy and PlaceTy generally work by "let's see if we are actually an MPlaceTy, and do something custom if not". //! For PlaceTy, the custom thing is basically always to call `force_allocation` and then use the MPlaceTy logic anyway. -//! For OpTy, the custom thing on field pojections has to be pretty clever (since `Operand::Immediate` can have fields), +//! For OpTy, the custom thing on field projections has to be pretty clever (since `Operand::Immediate` can have fields), //! but for array/slice operations it only has to worry about `Operand::Uninit`. That makes the value part trivial, //! but we still need to do bounds checking and adjust the layout. To not duplicate that with MPlaceTy, we actually //! implement the logic on OpTy, and MPlaceTy calls that. diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index f667823723c0..b5cf6c4c3723 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -74,7 +74,7 @@ impl EnteredTraceSpan for tracing::span::EnteredSpan { } } -/// Shortand for calling [crate::interpret::Machine::enter_trace_span] on a [tracing::info_span!]. +/// Shorthand for calling [crate::interpret::Machine::enter_trace_span] on a [tracing::info_span!]. /// This is supposed to be compiled out when [crate::interpret::Machine::enter_trace_span] has the /// default implementation (i.e. when it does not actually enter the span but instead returns `()`). /// This macro takes a type implementing the [crate::interpret::Machine] trait as its first argument diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index c62a23eb0b34..89a3303eb390 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -1261,9 +1261,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, // When you extend this match, make sure to also add tests to // tests/ui/type/pattern_types/validity.rs(( match **pat { - // Range patterns are precisely reflected into `valid_range` and thus + // Range and non-null patterns are precisely reflected into `valid_range` and thus // handled fully by `visit_scalar` (called below). ty::PatternKind::Range { .. } => {}, + ty::PatternKind::NotNull => {}, // FIXME(pattern_types): check that the value is covered by one of the variants. // For now, we rely on layout computation setting the scalar's `valid_range` to diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 8dab520cf36b..9529ef2b99ad 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -269,7 +269,6 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) make_codegen_backend: None, registry: diagnostics_registry(), using_internal_features: &USING_INTERNAL_FEATURES, - expanded_args: args, }; callbacks.config(&mut config); @@ -521,11 +520,11 @@ fn show_md_content_with_pager(content: &str, color: ColorConfig) { }; // Try to prettify the raw markdown text. The result can be used by the pager or on stdout. - let pretty_data = { + let mut pretty_data = { let mdstream = markdown::MdStream::parse_str(content); let bufwtr = markdown::create_stdout_bufwtr(); - let mut mdbuf = bufwtr.buffer(); - if mdstream.write_termcolor_buf(&mut mdbuf).is_ok() { Some((bufwtr, mdbuf)) } else { None } + let mut mdbuf = Vec::new(); + if mdstream.write_anstream_buf(&mut mdbuf).is_ok() { Some((bufwtr, mdbuf)) } else { None } }; // Try to print via the pager, pretty output if possible. @@ -546,8 +545,8 @@ fn show_md_content_with_pager(content: &str, color: ColorConfig) { } // The pager failed. Try to print pretty output to stdout. - if let Some((bufwtr, mdbuf)) = &pretty_data - && bufwtr.print(mdbuf).is_ok() + if let Some((bufwtr, mdbuf)) = &mut pretty_data + && bufwtr.write_all(&mdbuf).is_ok() { return; } diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index 7912b8e6098b..6ade87ea3b25 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -5,7 +5,9 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -annotate-snippets = "0.11" +annotate-snippets = "0.12.7" +anstream = "0.6.20" +anstyle = "1.0.13" derive_setters = "0.1.6" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } @@ -22,7 +24,6 @@ rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } serde = { version = "1.0.125", features = ["derive"] } serde_json = "1.0.59" -termcolor = "1.2.0" termize = "0.2" tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index 2eb3c23259ff..854e3ddf15e4 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -5,32 +5,70 @@ //! //! [annotate_snippets]: https://docs.rs/crate/annotate-snippets/ +use std::borrow::Cow; +use std::error::Report; +use std::fmt::Debug; +use std::io; +use std::io::Write; use std::sync::Arc; -use annotate_snippets::{Renderer, Snippet}; -use rustc_error_messages::FluentArgs; -use rustc_span::SourceFile; +use annotate_snippets::renderer::DEFAULT_TERM_WIDTH; +use annotate_snippets::{AnnotationKind, Group, Origin, Padding, Patch, Renderer, Snippet}; +use anstream::ColorChoice; +use derive_setters::Setters; +use rustc_data_structures::sync::IntoDynSyncSend; +use rustc_error_messages::{FluentArgs, SpanLabel}; +use rustc_lint_defs::pluralize; use rustc_span::source_map::SourceMap; +use rustc_span::{BytePos, FileName, Pos, SourceFile, Span}; +use tracing::debug; -use crate::emitter::FileWithAnnotatedLines; +use crate::emitter::{ + ConfusionType, Destination, MAX_SUGGESTIONS, OutputTheme, detect_confusion_type, is_different, + normalize_whitespace, should_show_source_code, +}; use crate::registry::Registry; -use crate::snippet::Line; use crate::translation::{Translator, to_fluent_args}; use crate::{ CodeSuggestion, DiagInner, DiagMessage, Emitter, ErrCode, Level, MultiSpan, Style, Subdiag, + SuggestionStyle, TerminalUrl, }; /// Generates diagnostics using annotate-snippet +#[derive(Setters)] pub struct AnnotateSnippetEmitter { - source_map: Option>, + #[setters(skip)] + dst: IntoDynSyncSend, + sm: Option>, + #[setters(skip)] translator: Translator, - - /// If true, hides the longer explanation text short_message: bool, - /// If true, will normalize line numbers with `LL` to prevent noise in UI test diffs. ui_testing: bool, + ignored_directories_in_source_blocks: Vec, + diagnostic_width: Option, macro_backtrace: bool, + track_diagnostics: bool, + terminal_url: TerminalUrl, + theme: OutputTheme, +} + +impl Debug for AnnotateSnippetEmitter { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("AnnotateSnippetEmitter") + .field("short_message", &self.short_message) + .field("ui_testing", &self.ui_testing) + .field( + "ignored_directories_in_source_blocks", + &self.ignored_directories_in_source_blocks, + ) + .field("diagnostic_width", &self.diagnostic_width) + .field("macro_backtrace", &self.macro_backtrace) + .field("track_diagnostics", &self.track_diagnostics) + .field("terminal_url", &self.terminal_url) + .field("theme", &self.theme) + .finish() + } } impl Emitter for AnnotateSnippetEmitter { @@ -38,6 +76,10 @@ impl Emitter for AnnotateSnippetEmitter { fn emit_diagnostic(&mut self, mut diag: DiagInner, _registry: &Registry) { let fluent_args = to_fluent_args(diag.args.iter()); + if self.track_diagnostics && diag.span.has_primary_spans() && !diag.span.is_dummy() { + diag.children.insert(0, diag.emitted_at_sub_diag()); + } + let mut suggestions = diag.suggestions.unwrap_tag(); self.primary_span_formatted(&mut diag.span, &mut suggestions, &fluent_args); @@ -55,12 +97,12 @@ impl Emitter for AnnotateSnippetEmitter { &diag.code, &diag.span, &diag.children, - &suggestions, + suggestions, ); } fn source_map(&self) -> Option<&SourceMap> { - self.source_map.as_deref() + self.sm.as_deref() } fn should_show_explain(&self) -> bool { @@ -70,128 +112,648 @@ impl Emitter for AnnotateSnippetEmitter { fn translator(&self) -> &Translator { &self.translator } + + fn supports_color(&self) -> bool { + false + } } -/// Provides the source string for the given `line` of `file` -fn source_string(file: Arc, line: &Line) -> String { - file.get_line(line.line_index - 1).map(|a| a.to_string()).unwrap_or_default() -} - -/// Maps [`crate::Level`] to [`annotate_snippets::Level`] -fn annotation_level_for_level(level: Level) -> annotate_snippets::Level { +fn annotation_level_for_level(level: Level) -> annotate_snippets::level::Level<'static> { match level { - Level::Bug | Level::Fatal | Level::Error | Level::DelayedBug => { - annotate_snippets::Level::Error + Level::Bug | Level::DelayedBug => { + annotate_snippets::Level::ERROR.with_name("error: internal compiler error") } - Level::ForceWarning | Level::Warning => annotate_snippets::Level::Warning, - Level::Note | Level::OnceNote => annotate_snippets::Level::Note, - Level::Help | Level::OnceHelp => annotate_snippets::Level::Help, - // FIXME(#59346): Not sure how to map this level - Level::FailureNote => annotate_snippets::Level::Error, + Level::Fatal | Level::Error => annotate_snippets::level::ERROR, + Level::ForceWarning | Level::Warning => annotate_snippets::Level::WARNING, + Level::Note | Level::OnceNote => annotate_snippets::Level::NOTE, + Level::Help | Level::OnceHelp => annotate_snippets::Level::HELP, + Level::FailureNote => annotate_snippets::Level::NOTE.no_name(), Level::Allow => panic!("Should not call with Allow"), Level::Expect => panic!("Should not call with Expect"), } } impl AnnotateSnippetEmitter { - pub fn new( - source_map: Option>, - translator: Translator, - short_message: bool, - macro_backtrace: bool, - ) -> Self { - Self { source_map, translator, short_message, ui_testing: false, macro_backtrace } - } - - /// Allows to modify `Self` to enable or disable the `ui_testing` flag. - /// - /// If this is set to true, line numbers will be normalized as `LL` in the output. - pub fn ui_testing(mut self, ui_testing: bool) -> Self { - self.ui_testing = ui_testing; - self + pub fn new(dst: Destination, translator: Translator) -> Self { + Self { + dst: IntoDynSyncSend(dst), + sm: None, + translator, + short_message: false, + ui_testing: false, + ignored_directories_in_source_blocks: Vec::new(), + diagnostic_width: None, + macro_backtrace: false, + track_diagnostics: false, + terminal_url: TerminalUrl::No, + theme: OutputTheme::Ascii, + } } fn emit_messages_default( &mut self, level: &Level, - messages: &[(DiagMessage, Style)], + msgs: &[(DiagMessage, Style)], args: &FluentArgs<'_>, code: &Option, msp: &MultiSpan, - _children: &[Subdiag], - _suggestions: &[CodeSuggestion], + children: &[Subdiag], + suggestions: Vec, ) { - let message = self.translator.translate_messages(messages, args); - if let Some(source_map) = &self.source_map { - // Make sure our primary file comes first - let primary_lo = if let Some(primary_span) = msp.primary_span().as_ref() { - if primary_span.is_dummy() { - // FIXME(#59346): Not sure when this is the case and what - // should be done if it happens - return; - } else { - source_map.lookup_char_pos(primary_span.lo()) - } - } else { - // FIXME(#59346): Not sure when this is the case and what - // should be done if it happens - return; - }; - let mut annotated_files = FileWithAnnotatedLines::collect_annotations(self, args, msp); - if let Ok(pos) = - annotated_files.binary_search_by(|x| x.file.name.cmp(&primary_lo.file.name)) - { - annotated_files.swap(0, pos); - } - // owned: file name, line source, line index, annotations - type Owned = (String, String, usize, Vec); - let annotated_files: Vec = annotated_files - .into_iter() - .flat_map(|annotated_file| { - let file = annotated_file.file; - annotated_file - .lines - .into_iter() - .map(|line| { - // Ensure the source file is present before we try - // to load a string from it. - // FIXME(#115869): support -Z ignore-directory-in-diagnostics-source-blocks - source_map.ensure_source_file_source_present(&file); - ( - format!("{}", source_map.filename_for_diagnostics(&file.name)), - source_string(Arc::clone(&file), &line), - line.line_index, - line.annotations, - ) - }) - .collect::>() - }) - .collect(); - let code = code.map(|code| code.to_string()); + let renderer = self.renderer(); + let annotation_level = annotation_level_for_level(*level); - let snippets = - annotated_files.iter().map(|(file_name, source, line_index, annotations)| { - Snippet::source(source) - .line_start(*line_index) - .origin(file_name) - // FIXME(#59346): Not really sure when `fold` should be true or false - .fold(false) - .annotations(annotations.iter().map(|annotation| { - annotation_level_for_level(*level) - .span(annotation.start_col.display..annotation.end_col.display) - .label(annotation.label.as_deref().unwrap_or_default()) - })) - }); - let mut message = annotation_level_for_level(*level).title(&message).snippets(snippets); - if let Some(code) = code.as_deref() { - message = message.id(code) + // If at least one portion of the message is styled, we need to + // "pre-style" the message + let mut title = if msgs.iter().any(|(_, style)| style != &crate::Style::NoStyle) { + annotation_level + .clone() + .secondary_title(Cow::Owned(self.pre_style_msgs(msgs, *level, args))) + } else { + annotation_level.clone().primary_title(self.translator.translate_messages(msgs, args)) + }; + + if let Some(c) = code { + title = title.id(c.to_string()); + if let TerminalUrl::Yes = self.terminal_url { + title = title.id_url(format!("https://doc.rust-lang.org/error_codes/{c}.html")); } - // FIXME(#59346): Figure out if we can _always_ print to stderr or not. - // `emitter.rs` has the `Destination` enum that lists various possible output - // destinations. - let renderer = Renderer::plain().anonymized_line_numbers(self.ui_testing); - eprintln!("{}", renderer.render(message)) } - // FIXME(#59346): Is it ok to return None if there's no source_map? + + let mut report = vec![]; + let mut group = Group::with_title(title); + + // If we don't have span information, emit and exit + let Some(sm) = self.sm.as_ref() else { + group = group.elements(children.iter().map(|c| { + let msg = self.translator.translate_messages(&c.messages, args).to_string(); + let level = annotation_level_for_level(c.level); + level.message(msg) + })); + + report.push(group); + if let Err(e) = emit_to_destination( + renderer.render(&report), + level, + &mut self.dst, + self.short_message, + ) { + panic!("failed to emit error: {e}"); + } + return; + }; + + let mut file_ann = collect_annotations(args, msp, sm, &self.translator); + + // Make sure our primary file comes first + let primary_span = msp.primary_span().unwrap_or_default(); + if !primary_span.is_dummy() { + let primary_lo = sm.lookup_char_pos(primary_span.lo()); + if let Ok(pos) = file_ann.binary_search_by(|(f, _)| f.name.cmp(&primary_lo.file.name)) { + file_ann.swap(0, pos); + } + + for (file_idx, (file, annotations)) in file_ann.into_iter().enumerate() { + if should_show_source_code(&self.ignored_directories_in_source_blocks, sm, &file) { + if let Some(snippet) = self.annotated_snippet(annotations, &file.name, sm) { + group = group.element(snippet); + } + // we can't annotate anything if the source is unavailable. + } else if !self.short_message { + // We'll just print unannotated messages + group = self.unannotated_messages( + annotations, + &file.name, + sm, + file_idx, + &mut report, + group, + &annotation_level, + ); + // If this is the last annotation for a file, and + // this is the last file, and the first child is a + // "secondary" message, we need to add padding + // ╭▸ /rustc/FAKE_PREFIX/library/core/src/clone.rs:236:13 + // │ + // ├ note: the late bound lifetime parameter + // │ (<- It adds *this*) + // ╰ warning: this was previously accepted + if let Some(c) = children.first() + && (!c.span.has_primary_spans() && !c.span.has_span_labels()) + { + group = group.element(Padding); + } + } + } + } + + for c in children { + let level = annotation_level_for_level(c.level); + + // If at least one portion of the message is styled, we need to + // "pre-style" the message + let msg = if c.messages.iter().any(|(_, style)| style != &crate::Style::NoStyle) { + Cow::Owned(self.pre_style_msgs(&c.messages, c.level, args)) + } else { + self.translator.translate_messages(&c.messages, args) + }; + + // This is a secondary message with no span info + if !c.span.has_primary_spans() && !c.span.has_span_labels() { + group = group.element(level.clone().message(msg)); + continue; + } + + report.push(std::mem::replace( + &mut group, + Group::with_title(level.clone().secondary_title(msg)), + )); + + let mut file_ann = collect_annotations(args, &c.span, sm, &self.translator); + let primary_span = c.span.primary_span().unwrap_or_default(); + if !primary_span.is_dummy() { + let primary_lo = sm.lookup_char_pos(primary_span.lo()); + if let Ok(pos) = + file_ann.binary_search_by(|(f, _)| f.name.cmp(&primary_lo.file.name)) + { + file_ann.swap(0, pos); + } + } + + for (file_idx, (file, annotations)) in file_ann.into_iter().enumerate() { + if should_show_source_code(&self.ignored_directories_in_source_blocks, sm, &file) { + if let Some(snippet) = self.annotated_snippet(annotations, &file.name, sm) { + group = group.element(snippet); + } + // we can't annotate anything if the source is unavailable. + } else if !self.short_message { + // We'll just print unannotated messages + group = self.unannotated_messages( + annotations, + &file.name, + sm, + file_idx, + &mut report, + group, + &level, + ); + } + } + } + + let suggestions_expected = suggestions + .iter() + .filter(|s| { + matches!( + s.style, + SuggestionStyle::HideCodeInline + | SuggestionStyle::ShowCode + | SuggestionStyle::ShowAlways + ) + }) + .count(); + for suggestion in suggestions { + match suggestion.style { + SuggestionStyle::CompletelyHidden => { + // do not display this suggestion, it is meant only for tools + } + SuggestionStyle::HideCodeAlways => { + let msg = self + .translator + .translate_messages(&[(suggestion.msg.to_owned(), Style::HeaderMsg)], args); + group = group.element(annotate_snippets::Level::HELP.message(msg)); + } + SuggestionStyle::HideCodeInline + | SuggestionStyle::ShowCode + | SuggestionStyle::ShowAlways => { + let substitutions = suggestion + .substitutions + .into_iter() + .filter_map(|mut subst| { + // Suggestions coming from macros can have malformed spans. This is a heavy + // handed approach to avoid ICEs by ignoring the suggestion outright. + let invalid = + subst.parts.iter().any(|item| sm.is_valid_span(item.span).is_err()); + if invalid { + debug!("suggestion contains an invalid span: {:?}", subst); + } + + // Assumption: all spans are in the same file, and all spans + // are disjoint. Sort in ascending order. + subst.parts.sort_by_key(|part| part.span.lo()); + // Verify the assumption that all spans are disjoint + assert_eq!( + subst.parts.array_windows().find(|[a, b]| a.span.overlaps(b.span)), + None, + "all spans must be disjoint", + ); + + // Account for cases where we are suggesting the same code that's already + // there. This shouldn't happen often, but in some cases for multipart + // suggestions it's much easier to handle it here than in the origin. + subst.parts.retain(|p| is_different(sm, &p.snippet, p.span)); + + let item_span = subst.parts.first()?; + let file = sm.lookup_source_file(item_span.span.lo()); + if !invalid + && should_show_source_code( + &self.ignored_directories_in_source_blocks, + sm, + &file, + ) + { + Some(subst) + } else { + None + } + }) + .collect::>(); + + if substitutions.is_empty() { + continue; + } + let mut msg = self + .translator + .translate_message(&suggestion.msg, args) + .map_err(Report::new) + .unwrap() + .to_string(); + + let lo = substitutions + .iter() + .find_map(|sub| sub.parts.first().map(|p| p.span.lo())) + .unwrap(); + let file = sm.lookup_source_file(lo); + + let filename = + sm.filename_for_diagnostics(&file.name).to_string_lossy().to_string(); + + let other_suggestions = substitutions.len().saturating_sub(MAX_SUGGESTIONS); + + let subs = substitutions + .into_iter() + .take(MAX_SUGGESTIONS) + .filter_map(|sub| { + let mut confusion_type = ConfusionType::None; + for part in &sub.parts { + let part_confusion = + detect_confusion_type(sm, &part.snippet, part.span); + confusion_type = confusion_type.combine(part_confusion); + } + + if !matches!(confusion_type, ConfusionType::None) { + msg.push_str(confusion_type.label_text()); + } + + let mut parts = sub + .parts + .into_iter() + .filter_map(|p| { + if is_different(sm, &p.snippet, p.span) { + Some((p.span, p.snippet)) + } else { + None + } + }) + .collect::>(); + + if parts.is_empty() { + None + } else { + let spans = parts.iter().map(|(span, _)| *span).collect::>(); + // The suggestion adds an entire line of code, ending on a newline, so we'll also + // print the *following* line, to provide context of what we're advising people to + // do. Otherwise you would only see contextless code that can be confused for + // already existing code, despite the colors and UI elements. + // We special case `#[derive(_)]\n` and other attribute suggestions, because those + // are the ones where context is most useful. + let fold = if let [(p, snippet)] = &mut parts[..] + && snippet.trim().starts_with("#[") + // This allows for spaces to come between the attribute and the newline + && snippet.trim().ends_with("]") + && snippet.ends_with('\n') + && p.hi() == p.lo() + && let Ok(b) = sm.span_to_prev_source(*p) + && let b = b.rsplit_once('\n').unwrap_or_else(|| ("", &b)).1 + && b.trim().is_empty() + { + // FIXME: This is a hack: + // The span for attribute suggestions often times points to the + // beginning of an item, disregarding leading whitespace. This + // causes the attribute to be properly indented, but leaves original + // item without indentation when rendered. + // This fixes that problem by adjusting the span to point to the start + // of the whitespace, and adds the whitespace to the replacement. + // + // Source: " extern "custom" fn negate(a: i64) -> i64 {\n" + // Span: 4..4 + // Replacement: "#[unsafe(naked)]\n" + // + // Before: + // help: convert this to an `#[unsafe(naked)]` function + // | + // LL + #[unsafe(naked)] + // LL | extern "custom" fn negate(a: i64) -> i64 { + // | + // + // After + // help: convert this to an `#[unsafe(naked)]` function + // | + // LL + #[unsafe(naked)] + // LL | extern "custom" fn negate(a: i64) -> i64 { + // | + if !b.is_empty() && !snippet.ends_with(b) { + snippet.insert_str(0, b); + let offset = BytePos(b.len() as u32); + *p = p.with_lo(p.lo() - offset).shrink_to_lo(); + } + false + } else { + true + }; + + if let Some((bounding_span, source, line_offset)) = + shrink_file(spans.as_slice(), &file.name, sm) + { + let adj_lo = bounding_span.lo().to_usize(); + Some( + Snippet::source(source) + .line_start(line_offset) + .path(filename.clone()) + .fold(fold) + .patches(parts.into_iter().map( + |(span, replacement)| { + let lo = + span.lo().to_usize().saturating_sub(adj_lo); + let hi = + span.hi().to_usize().saturating_sub(adj_lo); + + Patch::new(lo..hi, replacement) + }, + )), + ) + } else { + None + } + } + }) + .collect::>(); + if !subs.is_empty() { + report.push(std::mem::replace( + &mut group, + Group::with_title(annotate_snippets::Level::HELP.secondary_title(msg)), + )); + + group = group.elements(subs); + if other_suggestions > 0 { + group = group.element( + annotate_snippets::Level::NOTE.no_name().message(format!( + "and {} other candidate{}", + other_suggestions, + pluralize!(other_suggestions) + )), + ); + } + } + } + } + } + + // FIXME: This hack should be removed once annotate_snippets is the + // default emitter. + if suggestions_expected > 0 && report.is_empty() { + group = group.element(Padding); + } + + if !group.is_empty() { + report.push(group); + } + if let Err(e) = + emit_to_destination(renderer.render(&report), level, &mut self.dst, self.short_message) + { + panic!("failed to emit error: {e}"); + } + } + + fn renderer(&self) -> Renderer { + let width = if let Some(width) = self.diagnostic_width { + width + } else if self.ui_testing || cfg!(miri) { + DEFAULT_TERM_WIDTH + } else { + termize::dimensions().map(|(w, _)| w).unwrap_or(DEFAULT_TERM_WIDTH) + }; + let decor_style = match self.theme { + OutputTheme::Ascii => annotate_snippets::renderer::DecorStyle::Ascii, + OutputTheme::Unicode => annotate_snippets::renderer::DecorStyle::Unicode, + }; + + match self.dst.current_choice() { + ColorChoice::AlwaysAnsi | ColorChoice::Always | ColorChoice::Auto => Renderer::styled(), + ColorChoice::Never => Renderer::plain(), + } + .term_width(width) + .anonymized_line_numbers(self.ui_testing) + .decor_style(decor_style) + .short_message(self.short_message) + } + + fn pre_style_msgs( + &self, + msgs: &[(DiagMessage, Style)], + level: Level, + args: &FluentArgs<'_>, + ) -> String { + msgs.iter() + .filter_map(|(m, style)| { + let text = self.translator.translate_message(m, args).map_err(Report::new).unwrap(); + let style = style.anstyle(level); + if text.is_empty() { None } else { Some(format!("{style}{text}{style:#}")) } + }) + .collect() + } + + fn annotated_snippet<'a>( + &self, + annotations: Vec, + file_name: &FileName, + sm: &Arc, + ) -> Option>> { + let spans = annotations.iter().map(|a| a.span).collect::>(); + if let Some((bounding_span, source, offset_line)) = shrink_file(&spans, file_name, sm) { + let adj_lo = bounding_span.lo().to_usize(); + let filename = sm.filename_for_diagnostics(file_name).to_string_lossy().to_string(); + Some(Snippet::source(source).line_start(offset_line).path(filename).annotations( + annotations.into_iter().map(move |a| { + let lo = a.span.lo().to_usize().saturating_sub(adj_lo); + let hi = a.span.hi().to_usize().saturating_sub(adj_lo); + let ann = a.kind.span(lo..hi); + if let Some(label) = a.label { ann.label(label) } else { ann } + }), + )) + } else { + None + } + } + + fn unannotated_messages<'a>( + &self, + annotations: Vec, + file_name: &FileName, + sm: &Arc, + file_idx: usize, + report: &mut Vec>, + mut group: Group<'a>, + level: &annotate_snippets::level::Level<'static>, + ) -> Group<'a> { + let filename = sm.filename_for_diagnostics(file_name).to_string_lossy().to_string(); + let mut line_tracker = vec![]; + for (i, a) in annotations.into_iter().enumerate() { + let lo = sm.lookup_char_pos(a.span.lo()); + let hi = sm.lookup_char_pos(a.span.hi()); + if i == 0 || (a.label.is_some()) { + // Render each new file after the first in its own Group + // ╭▸ $DIR/deriving-meta-unknown-trait.rs:1:10 + // │ + // LL │ #[derive(Eqr)] + // │ ━━━ + // ╰╴ (<- It makes it so *this* will get printed) + // ╭▸ $SRC_DIR/core/src/option.rs:594:0 + // ⸬ $SRC_DIR/core/src/option.rs:602:4 + // │ + // ╰ note: not covered + if i == 0 && file_idx != 0 { + report.push(std::mem::replace(&mut group, Group::with_level(level.clone()))); + } + + if !line_tracker.contains(&lo.line) { + line_tracker.push(lo.line); + // ╭▸ $SRC_DIR/core/src/option.rs:594:0 (<- It adds *this*) + // ⸬ $SRC_DIR/core/src/option.rs:602:4 + // │ + // ╰ note: not covered + group = group.element( + Origin::path(filename.clone()) + .line(sm.doctest_offset_line(file_name, lo.line)) + .char_column(lo.col_display), + ); + } + + if hi.line > lo.line + && a.label.as_ref().is_some_and(|l| !l.is_empty()) + && !line_tracker.contains(&hi.line) + { + line_tracker.push(hi.line); + // ╭▸ $SRC_DIR/core/src/option.rs:594:0 + // ⸬ $SRC_DIR/core/src/option.rs:602:4 (<- It adds *this*) + // │ + // ╰ note: not covered + group = group.element( + Origin::path(filename.clone()) + .line(sm.doctest_offset_line(file_name, hi.line)) + .char_column(hi.col_display), + ); + } + + if let Some(label) = a.label + && !label.is_empty() + { + // ╭▸ $SRC_DIR/core/src/option.rs:594:0 + // ⸬ $SRC_DIR/core/src/option.rs:602:4 + // │ (<- It adds *this*) + // ╰ note: not covered (<- and *this*) + group = group + .element(Padding) + .element(annotate_snippets::Level::NOTE.message(label)); + } + } + } + group } } + +fn emit_to_destination( + rendered: String, + lvl: &Level, + dst: &mut Destination, + short_message: bool, +) -> io::Result<()> { + use crate::lock; + let _buffer_lock = lock::acquire_global_lock("rustc_errors"); + writeln!(dst, "{rendered}")?; + if !short_message && !lvl.is_failure_note() { + writeln!(dst)?; + } + dst.flush()?; + Ok(()) +} + +#[derive(Debug)] +struct Annotation { + kind: AnnotationKind, + span: Span, + label: Option, +} + +fn collect_annotations( + args: &FluentArgs<'_>, + msp: &MultiSpan, + sm: &Arc, + translator: &Translator, +) -> Vec<(Arc, Vec)> { + let mut output: Vec<(Arc, Vec)> = vec![]; + + for SpanLabel { span, is_primary, label } in msp.span_labels() { + // If we don't have a useful span, pick the primary span if that exists. + // Worst case we'll just print an error at the top of the main file. + let span = match (span.is_dummy(), msp.primary_span()) { + (_, None) | (false, _) => span, + (true, Some(span)) => span, + }; + let file = sm.lookup_source_file(span.lo()); + + let kind = if is_primary { AnnotationKind::Primary } else { AnnotationKind::Context }; + + let label = label.as_ref().map(|m| { + normalize_whitespace( + &translator.translate_message(m, args).map_err(Report::new).unwrap(), + ) + }); + + let ann = Annotation { kind, span, label }; + if sm.is_valid_span(ann.span).is_ok() { + // Look through each of our files for the one we're adding to. We + // use each files `stable_id` to avoid issues with file name + // collisions when multiple versions of the same crate are present + // in the dependency graph + if let Some((_, annotations)) = + output.iter_mut().find(|(f, _)| f.stable_id == file.stable_id) + { + annotations.push(ann); + } else { + output.push((file, vec![ann])); + } + } + } + output +} + +fn shrink_file( + spans: &[Span], + file_name: &FileName, + sm: &Arc, +) -> Option<(Span, String, usize)> { + let lo_byte = spans.iter().map(|s| s.lo()).min()?; + let lo_loc = sm.lookup_char_pos(lo_byte); + let lo = lo_loc.file.line_bounds(lo_loc.line.saturating_sub(1)).start; + + let hi_byte = spans.iter().map(|s| s.hi()).max()?; + let hi_loc = sm.lookup_char_pos(hi_byte); + let hi = lo_loc.file.line_bounds(hi_loc.line.saturating_sub(1)).end; + + let bounding_span = Span::with_root_ctxt(lo, hi); + let source = sm.span_to_snippet(bounding_span).unwrap_or_default(); + let offset_line = sm.doctest_offset_line(file_name, lo_loc.line); + + Some((bounding_span, source, offset_line)) +} diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index ae23ef1e2553..96a4ed3218fb 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -945,6 +945,11 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { None, "Span must not be empty and have no suggestion", ); + debug_assert_eq!( + parts.array_windows().find(|[a, b]| a.span.overlaps(b.span)), + None, + "suggestion must not have overlapping parts", + ); self.push_suggestion(CodeSuggestion { substitutions: vec![Substitution { parts }], diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index afdb582fea65..9e32a85e361a 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -16,6 +16,8 @@ use std::iter; use std::path::Path; use std::sync::Arc; +use anstream::{AutoStream, ColorChoice}; +use anstyle::{AnsiColor, Effects}; use derive_setters::Setters; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sync::{DynSend, IntoDynSyncSend}; @@ -25,7 +27,6 @@ use rustc_lint_defs::pluralize; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::SourceMap; use rustc_span::{FileLines, FileName, SourceFile, Span, char_width, str_width}; -use termcolor::{Buffer, BufferWriter, Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; use tracing::{debug, instrument, trace, warn}; use crate::registry::Registry; @@ -525,10 +526,6 @@ impl Emitter for HumanEmitter { !self.short_message } - fn supports_color(&self) -> bool { - self.dst.supports_color() - } - fn translator(&self) -> &Translator { &self.translator } @@ -1701,7 +1698,6 @@ impl HumanEmitter { } else { col_sep_before_no_show_source = true; } - // print out the span location and spacer before we print the annotated source // to do this, we need to know if this span will be primary let is_primary = primary_lo.file.name == annotated_file.file.name; @@ -2354,6 +2350,7 @@ impl HumanEmitter { .sum(); let underline_start = (span_start_pos + start) as isize + offset; let underline_end = (span_start_pos + start + sub_len) as isize + offset; + assert!(underline_start >= 0 && underline_end >= 0); let padding: usize = max_line_num_len + 3; for p in underline_start..underline_end { if let DisplaySuggestion::Underline = show_code_change @@ -2702,8 +2699,7 @@ impl HumanEmitter { [SubstitutionHighlight { start: 0, end }] if *end == line_to_add.len() => { buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition); } - [] => { - // FIXME: needed? Doesn't get exercised in any test. + [] | [SubstitutionHighlight { start: 0, end: 0 }] => { self.draw_col_separator_no_space(buffer, *row_num, max_line_num_len + 1); } _ => { @@ -3127,7 +3123,6 @@ impl FileWithAnnotatedLines { multiline_depth: 0, }); } - let mut output = vec![]; let mut multiline_annotations = vec![]; @@ -3361,7 +3356,7 @@ const OUTPUT_REPLACEMENTS: &[(char, &str)] = &[ ('\u{2069}', "�"), ]; -fn normalize_whitespace(s: &str) -> String { +pub(crate) fn normalize_whitespace(s: &str) -> String { const { let mut i = 1; while i < OUTPUT_REPLACEMENTS.len() { @@ -3406,7 +3401,7 @@ fn overlaps(a1: &Annotation, a2: &Annotation, padding: usize) -> bool { ) } -fn emit_to_destination( +pub(crate) fn emit_to_destination( rendered_buffer: &[Vec], lvl: &Level, dst: &mut Destination, @@ -3429,10 +3424,8 @@ fn emit_to_destination( let _buffer_lock = lock::acquire_global_lock("rustc_errors"); for (pos, line) in rendered_buffer.iter().enumerate() { for part in line { - let style = part.style.color_spec(*lvl); - dst.set_color(&style)?; - write!(dst, "{}", part.text)?; - dst.reset()?; + let style = part.style.anstyle(*lvl); + write!(dst, "{style}{}{style:#}", part.text)?; } if !short_message && (!lvl.is_failure_note() || pos != rendered_buffer.len() - 1) { writeln!(dst)?; @@ -3442,11 +3435,11 @@ fn emit_to_destination( Ok(()) } -pub type Destination = Box; +pub type Destination = AutoStream>; struct Buffy { - buffer_writer: BufferWriter, - buffer: Buffer, + buffer_writer: std::io::Stderr, + buffer: Vec, } impl Write for Buffy { @@ -3455,7 +3448,7 @@ impl Write for Buffy { } fn flush(&mut self) -> io::Result<()> { - self.buffer_writer.print(&self.buffer)?; + self.buffer_writer.write_all(&self.buffer)?; self.buffer.clear(); Ok(()) } @@ -3470,22 +3463,16 @@ impl Drop for Buffy { } } -impl WriteColor for Buffy { - fn supports_color(&self) -> bool { - self.buffer.supports_color() - } - - fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { - self.buffer.set_color(spec) - } - - fn reset(&mut self) -> io::Result<()> { - self.buffer.reset() - } -} - pub fn stderr_destination(color: ColorConfig) -> Destination { + let buffer_writer = std::io::stderr(); let choice = color.to_color_choice(); + // We need to resolve `ColorChoice::Auto` before `Box`ing since + // `ColorChoice::Auto` on `dyn Write` will always resolve to `Never` + let choice = if matches!(choice, ColorChoice::Auto) { + AutoStream::choice(&buffer_writer) + } else { + choice + }; // On Windows we'll be performing global synchronization on the entire // system for emitting rustc errors, so there's no need to buffer // anything. @@ -3493,60 +3480,42 @@ pub fn stderr_destination(color: ColorConfig) -> Destination { // On non-Windows we rely on the atomicity of `write` to ensure errors // don't get all jumbled up. if cfg!(windows) { - Box::new(StandardStream::stderr(choice)) + AutoStream::new(Box::new(buffer_writer), choice) } else { - let buffer_writer = BufferWriter::stderr(choice); - let buffer = buffer_writer.buffer(); - Box::new(Buffy { buffer_writer, buffer }) + let buffer = Vec::new(); + AutoStream::new(Box::new(Buffy { buffer_writer, buffer }), choice) } } /// On Windows, BRIGHT_BLUE is hard to read on black. Use cyan instead. /// /// See #36178. -const BRIGHT_BLUE: Color = if cfg!(windows) { Color::Cyan } else { Color::Blue }; +const BRIGHT_BLUE: anstyle::Style = if cfg!(windows) { + AnsiColor::BrightCyan.on_default() +} else { + AnsiColor::BrightBlue.on_default() +}; impl Style { - fn color_spec(&self, lvl: Level) -> ColorSpec { - let mut spec = ColorSpec::new(); + pub(crate) fn anstyle(&self, lvl: Level) -> anstyle::Style { match self { - Style::Addition => { - spec.set_fg(Some(Color::Green)).set_intense(true); - } - Style::Removal => { - spec.set_fg(Some(Color::Red)).set_intense(true); - } - Style::LineAndColumn => {} - Style::LineNumber => { - spec.set_bold(true); - spec.set_intense(true); - spec.set_fg(Some(BRIGHT_BLUE)); - } - Style::Quotation => {} - Style::MainHeaderMsg => { - spec.set_bold(true); - if cfg!(windows) { - spec.set_intense(true).set_fg(Some(Color::White)); - } - } - Style::UnderlinePrimary | Style::LabelPrimary => { - spec = lvl.color(); - spec.set_bold(true); - } - Style::UnderlineSecondary | Style::LabelSecondary => { - spec.set_bold(true).set_intense(true); - spec.set_fg(Some(BRIGHT_BLUE)); - } - Style::HeaderMsg | Style::NoStyle => {} - Style::Level(lvl) => { - spec = lvl.color(); - spec.set_bold(true); - } - Style::Highlight => { - spec.set_bold(true).set_fg(Some(Color::Magenta)); + Style::Addition => AnsiColor::BrightGreen.on_default(), + Style::Removal => AnsiColor::BrightRed.on_default(), + Style::LineAndColumn => anstyle::Style::new(), + Style::LineNumber => BRIGHT_BLUE.effects(Effects::BOLD), + Style::Quotation => anstyle::Style::new(), + Style::MainHeaderMsg => if cfg!(windows) { + AnsiColor::BrightWhite.on_default() + } else { + anstyle::Style::new() } + .effects(Effects::BOLD), + Style::UnderlinePrimary | Style::LabelPrimary => lvl.color().effects(Effects::BOLD), + Style::UnderlineSecondary | Style::LabelSecondary => BRIGHT_BLUE.effects(Effects::BOLD), + Style::HeaderMsg | Style::NoStyle => anstyle::Style::new(), + Style::Level(lvl) => lvl.color().effects(Effects::BOLD), + Style::Highlight => AnsiColor::Magenta.on_default().effects(Effects::BOLD), } - spec } } diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index 719d4ca625ae..03ce1d82ef3c 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -15,6 +15,7 @@ use std::path::Path; use std::sync::{Arc, Mutex}; use std::vec; +use anstream::{AutoStream, ColorChoice}; use derive_setters::Setters; use rustc_data_structures::sync::IntoDynSyncSend; use rustc_error_messages::FluentArgs; @@ -23,7 +24,6 @@ use rustc_span::Span; use rustc_span::hygiene::ExpnData; use rustc_span::source_map::{FilePathMapping, SourceMap}; use serde::Serialize; -use termcolor::{ColorSpec, WriteColor}; use crate::diagnostic::IsLint; use crate::emitter::{ @@ -333,7 +333,7 @@ impl Diagnostic { // generate regular command line output and store it in the json // A threadsafe buffer for writing. - #[derive(Default, Clone)] + #[derive(Clone)] struct BufWriter(Arc>>); impl Write for BufWriter { @@ -344,19 +344,6 @@ impl Diagnostic { self.0.lock().unwrap().flush() } } - impl WriteColor for BufWriter { - fn supports_color(&self) -> bool { - false - } - - fn set_color(&mut self, _spec: &ColorSpec) -> io::Result<()> { - Ok(()) - } - - fn reset(&mut self) -> io::Result<()> { - Ok(()) - } - } let translated_message = je.translator.translate_messages(&diag.messages, &args); @@ -382,13 +369,15 @@ impl Diagnostic { children .insert(0, Diagnostic::from_sub_diagnostic(&diag.emitted_at_sub_diag(), &args, je)); } - let buf = BufWriter::default(); - let mut dst: Destination = Box::new(buf.clone()); + let buf = BufWriter(Arc::new(Mutex::new(Vec::new()))); let short = je.json_rendered.short(); - match je.color_config { - ColorConfig::Always | ColorConfig::Auto => dst = Box::new(termcolor::Ansi::new(dst)), - ColorConfig::Never => {} - } + let dst: Destination = AutoStream::new( + Box::new(buf.clone()), + match je.color_config.to_color_choice() { + ColorChoice::Auto => ColorChoice::Always, + choice => choice, + }, + ); HumanEmitter::new(dst, je.translator.clone()) .short_message(short) .sm(je.sm.clone()) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 6bec65f2d538..17cd466f96b8 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -39,6 +39,12 @@ use std::path::{Path, PathBuf}; use std::{fmt, panic}; use Level::*; +// Used by external projects such as `rust-gpu`. +// See https://github.com/rust-lang/rust/pull/115393. +pub use anstream::{AutoStream, ColorChoice}; +pub use anstyle::{ + Ansi256Color, AnsiColor, Color, EffectIter, Effects, Reset, RgbColor, Style as Anstyle, +}; pub use codes::*; pub use decorate_diag::{BufferedEarlyLint, DecorateDiagCompat, LintBuffer}; pub use diagnostic::{ @@ -69,9 +75,6 @@ pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker}; use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, DUMMY_SP, Loc, Span}; pub use snippet::Style; -// Used by external projects such as `rust-gpu`. -// See https://github.com/rust-lang/rust/pull/115393. -pub use termcolor::{Color, ColorSpec, WriteColor}; use tracing::debug; use crate::emitter::TimingEvent; @@ -397,17 +400,6 @@ impl CodeSuggestion { // Assumption: all spans are in the same file, and all spans // are disjoint. Sort in ascending order. substitution.parts.sort_by_key(|part| part.span.lo()); - // Verify the assumption that all spans are disjoint - assert_eq!( - substitution.parts.array_windows().find(|[a, b]| a.span.overlaps(b.span)), - None, - "all spans must be disjoint", - ); - - // Account for cases where we are suggesting the same code that's already - // there. This shouldn't happen often, but in some cases for multipart - // suggestions it's much easier to handle it here than in the origin. - substitution.parts.retain(|p| is_different(sm, &p.snippet, p.span)); // Find the bounding span. let lo = substitution.parts.iter().map(|part| part.span.lo()).min()?; @@ -502,12 +494,16 @@ impl CodeSuggestion { _ => 1, }) .sum(); - - line_highlight.push(SubstitutionHighlight { - start: (cur_lo.col.0 as isize + acc) as usize, - end: (cur_lo.col.0 as isize + acc + len) as usize, - }); - + if !is_different(sm, &part.snippet, part.span) { + // Account for cases where we are suggesting the same code that's already + // there. This shouldn't happen often, but in some cases for multipart + // suggestions it's much easier to handle it here than in the origin. + } else { + line_highlight.push(SubstitutionHighlight { + start: (cur_lo.col.0 as isize + acc) as usize, + end: (cur_lo.col.0 as isize + acc + len) as usize, + }); + } buf.push_str(&part.snippet); let cur_hi = sm.lookup_char_pos(part.span.hi()); // Account for the difference between the width of the current code and the @@ -1982,25 +1978,21 @@ impl fmt::Display for Level { } impl Level { - fn color(self) -> ColorSpec { - let mut spec = ColorSpec::new(); + fn color(self) -> anstyle::Style { match self { - Bug | Fatal | Error | DelayedBug => { - spec.set_fg(Some(Color::Red)).set_intense(true); - } + Bug | Fatal | Error | DelayedBug => AnsiColor::BrightRed.on_default(), ForceWarning | Warning => { - spec.set_fg(Some(Color::Yellow)).set_intense(cfg!(windows)); + if cfg!(windows) { + AnsiColor::BrightYellow.on_default() + } else { + AnsiColor::Yellow.on_default() + } } - Note | OnceNote => { - spec.set_fg(Some(Color::Green)).set_intense(true); - } - Help | OnceHelp => { - spec.set_fg(Some(Color::Cyan)).set_intense(true); - } - FailureNote => {} + Note | OnceNote => AnsiColor::BrightGreen.on_default(), + Help | OnceHelp => AnsiColor::BrightCyan.on_default(), + FailureNote => anstyle::Style::new(), Allow | Expect => unreachable!(), } - spec } pub fn to_str(self) -> &'static str { diff --git a/compiler/rustc_errors/src/markdown/mod.rs b/compiler/rustc_errors/src/markdown/mod.rs index 64576cdc8cac..4f5e2328234d 100644 --- a/compiler/rustc_errors/src/markdown/mod.rs +++ b/compiler/rustc_errors/src/markdown/mod.rs @@ -4,7 +4,6 @@ use std::io; -use termcolor::{Buffer, BufferWriter, ColorChoice}; mod parse; mod term; @@ -19,15 +18,15 @@ impl<'a> MdStream<'a> { parse::entrypoint(s) } - /// Write formatted output to a termcolor buffer - pub fn write_termcolor_buf(&self, buf: &mut Buffer) -> io::Result<()> { + /// Write formatted output to an anstream buffer + pub fn write_anstream_buf(&self, buf: &mut Vec) -> io::Result<()> { term::entrypoint(self, buf) } } -/// Create a termcolor buffer with the `Always` color choice -pub fn create_stdout_bufwtr() -> BufferWriter { - BufferWriter::stdout(ColorChoice::Always) +/// Create an anstream buffer with the `Always` color choice +pub fn create_stdout_bufwtr() -> anstream::Stdout { + anstream::Stdout::always(std::io::stdout()) } /// A single tokentree within a Markdown document diff --git a/compiler/rustc_errors/src/markdown/term.rs b/compiler/rustc_errors/src/markdown/term.rs index fe1d80bdbe8e..b0ce01548f00 100644 --- a/compiler/rustc_errors/src/markdown/term.rs +++ b/compiler/rustc_errors/src/markdown/term.rs @@ -1,7 +1,7 @@ use std::cell::Cell; use std::io::{self, Write}; -use termcolor::{Buffer, Color, ColorSpec, WriteColor}; +use anstyle::{AnsiColor, Effects, Style}; use crate::markdown::{MdStream, MdTree}; @@ -15,7 +15,7 @@ thread_local! { } /// Print to terminal output to a buffer -pub(crate) fn entrypoint(stream: &MdStream<'_>, buf: &mut Buffer) -> io::Result<()> { +pub(crate) fn entrypoint(stream: &MdStream<'_>, buf: &mut Vec) -> io::Result<()> { #[cfg(not(test))] if let Some((w, _)) = termize::dimensions() { WIDTH.set(std::cmp::min(w, DEFAULT_COLUMN_WIDTH)); @@ -23,57 +23,65 @@ pub(crate) fn entrypoint(stream: &MdStream<'_>, buf: &mut Buffer) -> io::Result< write_stream(stream, buf, None, 0)?; buf.write_all(b"\n") } - /// Write the buffer, reset to the default style after each fn write_stream( MdStream(stream): &MdStream<'_>, - buf: &mut Buffer, - default: Option<&ColorSpec>, + buf: &mut Vec, + default: Option