diff options
310 files changed, 8218 insertions, 2995 deletions
diff --git a/.mailmap b/.mailmap index 122751a6be8..b6f4112392d 100644 --- a/.mailmap +++ b/.mailmap @@ -31,6 +31,8 @@ Alexis Beingessner <a.beingessner@gmail.com> Alfie John <alfie@alfie.wtf> Alfie John <alfiej@fastmail.fm> Alona Enraght-Moony <code@alona.page> <nixon.emoony@gmail.com> Alona Enraght-Moony <code@alona.page> <nixon@caminus.local> +Amanda Stjerna <mail@amandastjerna.se> <albin.stjerna@gmail.com> +Amanda Stjerna <mail@amandastjerna.se> <amanda.stjerna@it.uu.se> Amos Onn <amosonn@gmail.com> Ana-Maria Mihalache <mihalacheana.maria@yahoo.com> Anatoly Ikorsky <aikorsky@gmail.com> diff --git a/Cargo.lock b/Cargo.lock index 7aa243ad8b5..4421e526d10 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -143,9 +143,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anstyle-parse" @@ -370,11 +370,11 @@ checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "bytecount" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c" +checksum = "ad152d03a2c813c80bb94fedbf3a3f02b28f793e39e7c214c8a0bcc196343de7" dependencies = [ - "packed_simd_2", + "packed_simd", ] [[package]] @@ -502,6 +502,16 @@ dependencies = [ ] [[package]] +name = "clap-cargo" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "383f21342a464d4af96e9a4cad22a0b4f2880d4a5b3bbf5c9654dd1d9a224ee4" +dependencies = [ + "anstyle", + "clap", +] + +[[package]] name = "clap_builder" version = "4.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1777,7 +1787,7 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" dependencies = [ - "libm 0.2.7", + "libm", ] [[package]] @@ -2230,12 +2240,6 @@ dependencies = [ [[package]] name = "libm" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" - -[[package]] -name = "libm" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" @@ -2594,6 +2598,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -2743,13 +2748,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" [[package]] -name = "packed_simd_2" -version = "0.3.8" +name = "packed_simd" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1914cd452d8fccd6f9db48147b29fd4ae05bea9dc5d9ad578509f72415de282" +checksum = "1f9f08af0c877571712e2e3e686ad79efad9657dbf0f7c3c8ba943ff6c38932d" dependencies = [ "cfg-if", - "libm 0.1.4", + "num-traits", ] [[package]] @@ -3596,6 +3601,7 @@ version = "0.0.0" dependencies = [ "bitflags 1.3.2", "cstr", + "itertools", "libc", "measureme", "object", @@ -4273,6 +4279,7 @@ dependencies = [ "coverage_test_macros", "either", "itertools", + "rustc_arena", "rustc_ast", "rustc_attr", "rustc_const_eval", @@ -4514,9 +4521,7 @@ name = "rustc_smir" version = "0.0.0" dependencies = [ "rustc_data_structures", - "rustc_driver", "rustc_hir", - "rustc_interface", "rustc_middle", "rustc_span", "rustc_target", @@ -4758,21 +4763,20 @@ dependencies = [ [[package]] name = "rustfmt-nightly" -version = "1.6.0" +version = "1.7.0" dependencies = [ "annotate-snippets", "anyhow", "bytecount", "cargo_metadata 0.15.4", "clap", + "clap-cargo", "diff", "dirs", - "env_logger 0.10.0", "getopts", "ignore", "itertools", "lazy_static", - "log", "regex", "rustfmt-config_proc_macro", "serde", @@ -4780,9 +4784,11 @@ dependencies = [ "term", "thiserror", "toml 0.7.5", + "tracing", + "tracing-subscriber", + "unicode-properties", "unicode-segmentation", "unicode-width", - "unicode_categories", ] [[package]] @@ -5676,6 +5682,7 @@ dependencies = [ "thread_local", "tracing", "tracing-core", + "tracing-log", ] [[package]] @@ -5892,12 +5899,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] -name = "unicode_categories" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" - -[[package]] name = "unified-diff" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index ada5c95dc34..c1f6ad6a27d 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -53,7 +53,6 @@ pub mod visit; pub use self::ast::*; pub use self::ast_traits::{AstDeref, AstNodeWrapper, HasAttrs, HasNodeId, HasSpan, HasTokens}; -pub use self::format::*; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 46683d2d258..60eacde1c72 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -31,16 +31,6 @@ pub fn is_builtin_attr(attr: &Attribute) -> bool { attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name)) } -enum AttrError { - MultipleItem(String), - UnknownMetaItem(String, &'static [&'static str]), - MissingSince, - NonIdentFeature, - MissingFeature, - MultipleStabilityLevels, - UnsupportedLiteral(UnsupportedLiteralReason, /* is_bytestr */ bool), -} - pub(crate) enum UnsupportedLiteralReason { Generic, CfgString, @@ -48,37 +38,6 @@ pub(crate) enum UnsupportedLiteralReason { DeprecatedKvPair, } -fn handle_errors(sess: &ParseSess, span: Span, error: AttrError) { - match error { - AttrError::MultipleItem(item) => { - sess.emit_err(session_diagnostics::MultipleItem { span, item }); - } - AttrError::UnknownMetaItem(item, expected) => { - sess.emit_err(session_diagnostics::UnknownMetaItem { span, item, expected }); - } - AttrError::MissingSince => { - sess.emit_err(session_diagnostics::MissingSince { span }); - } - AttrError::NonIdentFeature => { - sess.emit_err(session_diagnostics::NonIdentFeature { span }); - } - AttrError::MissingFeature => { - sess.emit_err(session_diagnostics::MissingFeature { span }); - } - AttrError::MultipleStabilityLevels => { - sess.emit_err(session_diagnostics::MultipleStabilityLevels { span }); - } - AttrError::UnsupportedLiteral(reason, is_bytestr) => { - sess.emit_err(session_diagnostics::UnsupportedLiteral { - span, - reason, - is_bytestr, - start_point_span: sess.source_map().start_point(span), - }); - } - } -} - #[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] pub enum InlineAttr { None, @@ -241,7 +200,7 @@ pub fn find_stability( sym::rustc_allowed_through_unstable_modules => allowed_through_unstable_modules = true, sym::unstable => { if stab.is_some() { - handle_errors(&sess.parse_sess, attr.span, AttrError::MultipleStabilityLevels); + sess.emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span }); break; } @@ -251,7 +210,7 @@ pub fn find_stability( } sym::stable => { if stab.is_some() { - handle_errors(&sess.parse_sess, attr.span, AttrError::MultipleStabilityLevels); + sess.emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span }); break; } if let Some((feature, level)) = parse_stability(sess, attr) { @@ -295,7 +254,7 @@ pub fn find_const_stability( sym::rustc_promotable => promotable = true, sym::rustc_const_unstable => { if const_stab.is_some() { - handle_errors(&sess.parse_sess, attr.span, AttrError::MultipleStabilityLevels); + sess.emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span }); break; } @@ -306,7 +265,7 @@ pub fn find_const_stability( } sym::rustc_const_stable => { if const_stab.is_some() { - handle_errors(&sess.parse_sess, attr.span, AttrError::MultipleStabilityLevels); + sess.emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span }); break; } if let Some((feature, level)) = parse_stability(sess, attr) { @@ -340,7 +299,7 @@ pub fn find_body_stability( for attr in attrs { if attr.has_name(sym::rustc_default_body_unstable) { if body_stab.is_some() { - handle_errors(&sess.parse_sess, attr.span, AttrError::MultipleStabilityLevels); + sess.emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span }); break; } @@ -355,11 +314,10 @@ pub fn find_body_stability( fn insert_or_error(sess: &Session, meta: &MetaItem, item: &mut Option<Symbol>) -> Option<()> { if item.is_some() { - handle_errors( - &sess.parse_sess, - meta.span, - AttrError::MultipleItem(pprust::path_to_string(&meta.path)), - ); + sess.emit_err(session_diagnostics::MultipleItem { + span: meta.span, + item: pprust::path_to_string(&meta.path), + }); None } else if let Some(v) = meta.value_str() { *item = Some(v); @@ -380,11 +338,12 @@ fn parse_stability(sess: &Session, attr: &Attribute) -> Option<(Symbol, Stabilit let mut since = None; for meta in metas { let Some(mi) = meta.meta_item() else { - handle_errors( - &sess.parse_sess, - meta.span(), - AttrError::UnsupportedLiteral(UnsupportedLiteralReason::Generic, false), - ); + sess.emit_err(session_diagnostics::UnsupportedLiteral { + span: meta.span(), + reason: UnsupportedLiteralReason::Generic, + is_bytestr: false, + start_point_span: sess.source_map().start_point(meta.span()), + }); return None; }; @@ -392,14 +351,11 @@ fn parse_stability(sess: &Session, attr: &Attribute) -> Option<(Symbol, Stabilit sym::feature => insert_or_error(sess, mi, &mut feature)?, sym::since => insert_or_error(sess, mi, &mut since)?, _ => { - handle_errors( - &sess.parse_sess, - meta.span(), - AttrError::UnknownMetaItem( - pprust::path_to_string(&mi.path), - &["feature", "since"], - ), - ); + sess.emit_err(session_diagnostics::UnknownMetaItem { + span: meta.span(), + item: pprust::path_to_string(&mi.path), + expected: &["feature", "since"], + }); return None; } } @@ -417,11 +373,11 @@ fn parse_stability(sess: &Session, attr: &Attribute) -> Option<(Symbol, Stabilit Some((feature, level)) } (None, _) => { - handle_errors(&sess.parse_sess, attr.span, AttrError::MissingFeature); + sess.emit_err(session_diagnostics::MissingFeature { span: attr.span }); None } _ => { - handle_errors(&sess.parse_sess, attr.span, AttrError::MissingSince); + sess.emit_err(session_diagnostics::MissingSince { span: attr.span }); None } } @@ -441,11 +397,12 @@ fn parse_unstability(sess: &Session, attr: &Attribute) -> Option<(Symbol, Stabil let mut implied_by = None; for meta in metas { let Some(mi) = meta.meta_item() else { - handle_errors( - &sess.parse_sess, - meta.span(), - AttrError::UnsupportedLiteral(UnsupportedLiteralReason::Generic, false), - ); + sess.emit_err(session_diagnostics::UnsupportedLiteral { + span: meta.span(), + reason: UnsupportedLiteralReason::Generic, + is_bytestr: false, + start_point_span: sess.source_map().start_point(meta.span()), + }); return None; }; @@ -484,14 +441,11 @@ fn parse_unstability(sess: &Session, attr: &Attribute) -> Option<(Symbol, Stabil } sym::implied_by => insert_or_error(sess, mi, &mut implied_by)?, _ => { - handle_errors( - &sess.parse_sess, - meta.span(), - AttrError::UnknownMetaItem( - pprust::path_to_string(&mi.path), - &["feature", "reason", "issue", "soft", "implied_by"], - ), - ); + sess.emit_err(session_diagnostics::UnknownMetaItem { + span: meta.span(), + item: pprust::path_to_string(&mi.path), + expected: &["feature", "reason", "issue", "soft", "implied_by"], + }); return None; } } @@ -500,7 +454,7 @@ fn parse_unstability(sess: &Session, attr: &Attribute) -> Option<(Symbol, Stabil match (feature, reason, issue) { (Some(feature), reason, Some(_)) => { if !rustc_lexer::is_ident(feature.as_str()) { - handle_errors(&sess.parse_sess, attr.span, AttrError::NonIdentFeature); + sess.emit_err(session_diagnostics::NonIdentFeature { span: attr.span }); return None; } let level = StabilityLevel::Unstable { @@ -512,7 +466,7 @@ fn parse_unstability(sess: &Session, attr: &Attribute) -> Option<(Symbol, Stabil Some((feature, level)) } (None, _, _) => { - handle_errors(&sess.parse_sess, attr.span, AttrError::MissingFeature); + sess.emit_err(session_diagnostics::MissingFeature { span: attr.span }); return None; } _ => { @@ -659,11 +613,12 @@ pub fn eval_condition( ast::MetaItemKind::List(mis) => { for mi in mis.iter() { if !mi.is_meta_item() { - handle_errors( - sess, - mi.span(), - AttrError::UnsupportedLiteral(UnsupportedLiteralReason::Generic, false), - ); + sess.emit_err(session_diagnostics::UnsupportedLiteral { + span: mi.span(), + reason: UnsupportedLiteralReason::Generic, + is_bytestr: false, + start_point_span: sess.source_map().start_point(mi.span()), + }); return false; } } @@ -731,14 +686,12 @@ pub fn eval_condition( true } MetaItemKind::NameValue(lit) if !lit.kind.is_str() => { - handle_errors( - sess, - lit.span, - AttrError::UnsupportedLiteral( - UnsupportedLiteralReason::CfgString, - lit.kind.is_bytestr(), - ), - ); + sess.emit_err(session_diagnostics::UnsupportedLiteral { + span: lit.span, + reason: UnsupportedLiteralReason::CfgString, + is_bytestr: lit.kind.is_bytestr(), + start_point_span: sess.source_map().start_point(lit.span), + }); true } ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => { @@ -795,11 +748,10 @@ pub fn find_deprecation( MetaItemKind::List(list) => { let get = |meta: &MetaItem, item: &mut Option<Symbol>| { if item.is_some() { - handle_errors( - &sess.parse_sess, - meta.span, - AttrError::MultipleItem(pprust::path_to_string(&meta.path)), - ); + sess.emit_err(session_diagnostics::MultipleItem { + span: meta.span, + item: pprust::path_to_string(&meta.path), + }); return false; } if let Some(v) = meta.value_str() { @@ -807,14 +759,12 @@ pub fn find_deprecation( true } else { if let Some(lit) = meta.name_value_literal() { - handle_errors( - &sess.parse_sess, - lit.span, - AttrError::UnsupportedLiteral( - UnsupportedLiteralReason::DeprecatedString, - lit.kind.is_bytestr(), - ), - ); + sess.emit_err(session_diagnostics::UnsupportedLiteral { + span: lit.span, + reason: UnsupportedLiteralReason::DeprecatedString, + is_bytestr: lit.kind.is_bytestr(), + start_point_span: sess.source_map().start_point(lit.span), + }); } else { sess.emit_err(session_diagnostics::IncorrectMetaItem { span: meta.span, @@ -852,30 +802,25 @@ pub fn find_deprecation( } } _ => { - handle_errors( - &sess.parse_sess, - meta.span(), - AttrError::UnknownMetaItem( - pprust::path_to_string(&mi.path), - if features.deprecated_suggestion { - &["since", "note", "suggestion"] - } else { - &["since", "note"] - }, - ), - ); + sess.emit_err(session_diagnostics::UnknownMetaItem { + span: meta.span(), + item: pprust::path_to_string(&mi.path), + expected: if features.deprecated_suggestion { + &["since", "note", "suggestion"] + } else { + &["since", "note"] + }, + }); continue 'outer; } }, NestedMetaItem::Lit(lit) => { - handle_errors( - &sess.parse_sess, - lit.span, - AttrError::UnsupportedLiteral( - UnsupportedLiteralReason::DeprecatedKvPair, - false, - ), - ); + sess.emit_err(session_diagnostics::UnsupportedLiteral { + span: lit.span, + reason: UnsupportedLiteralReason::DeprecatedKvPair, + is_bytestr: false, + start_point_span: sess.source_map().start_point(lit.span), + }); continue 'outer; } } @@ -885,7 +830,7 @@ pub fn find_deprecation( if is_rustc { if since.is_none() { - handle_errors(&sess.parse_sess, attr.span, AttrError::MissingSince); + sess.emit_err(session_diagnostics::MissingSince { span: attr.span }); continue; } diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 6ea84620bbe..16814950b0d 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -272,21 +272,28 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> { loan_issued_at: Location, ) { let sccs = self.regioncx.constraint_sccs(); + let universal_regions = self.regioncx.universal_regions(); let issuing_region_scc = sccs.scc(issuing_region); // We first handle the cases where the loan doesn't go out of scope, depending on the issuing // region's successors. for scc in sccs.depth_first_search(issuing_region_scc) { - // 1. Via member constraints + // 1. Via applied member constraints // // The issuing region can flow into the choice regions, and they are either: // - placeholders or free regions themselves, // - or also transitively outlive a free region. // - // That is to say, if there are member constraints here, the loan escapes the function - // and cannot go out of scope. We can early return. - if self.regioncx.scc_has_member_constraints(scc) { - return; + // That is to say, if there are applied member constraints here, the loan escapes the + // function and cannot go out of scope. We could early return here. + // + // For additional insurance via fuzzing and crater, we verify that the constraint's min + // choice indeed escapes the function. In the future, we could e.g. turn this check into + // a debug assert and early return as an optimization. + for constraint in self.regioncx.applied_member_constraints(scc) { + if universal_regions.is_universal_region(constraint.min_choice) { + return; + } } // 2. Via regions that are live at all points: placeholders and free regions. @@ -413,12 +420,12 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> { let mut polonius_prec = PoloniusOutOfScopePrecomputer::new(body, regioncx); for (loan_idx, loan_data) in borrow_set.iter_enumerated() { let issuing_region = loan_data.region; - let issued_location = loan_data.reserve_location; + let loan_issued_at = loan_data.reserve_location; polonius_prec.precompute_loans_out_of_scope( loan_idx, issuing_region, - issued_location, + loan_issued_at, ); } diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index ecf7930ff6b..b5ad02dc688 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -2633,9 +2633,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { /* Check if the mpi is initialized as an argument */ let mut is_argument = false; for arg in self.body.args_iter() { - let path = self.move_data.rev_lookup.find_local(arg); - if mpis.contains(&path) { - is_argument = true; + if let Some(path) = self.move_data.rev_lookup.find_local(arg) { + if mpis.contains(&path) { + is_argument = true; + } } } diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 47a387e25e3..4b95b4783eb 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -46,6 +46,7 @@ mod mutability_errors; mod region_errors; pub(crate) use bound_region_errors::{ToUniverseInfo, UniverseInfo}; +pub(crate) use move_errors::{IllegalMoveOriginKind, MoveError}; pub(crate) use mutability_errors::AccessKind; pub(crate) use outlives_suggestion::OutlivesSuggestionBuilder; pub(crate) use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors}; @@ -470,7 +471,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } - ty.print(printer).unwrap().into_buffer() + ty.print(&mut printer).unwrap(); + printer.into_buffer() } /// Returns the name of the provided `Ty` (that must be a reference)'s region with a @@ -492,7 +494,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { bug!("ty for annotation of borrow region is not a reference"); }; - region.print(printer).unwrap().into_buffer() + region.print(&mut printer).unwrap(); + printer.into_buffer() } } diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index e05c04e1188..695ac6980cd 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -1,9 +1,7 @@ use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; use rustc_middle::mir::*; -use rustc_middle::ty; -use rustc_mir_dataflow::move_paths::{ - IllegalMoveOrigin, IllegalMoveOriginKind, LookupResult, MoveError, MovePathIndex, -}; +use rustc_middle::ty::{self, Ty}; +use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex}; use rustc_span::{BytePos, Span}; use crate::diagnostics::CapturedMessageOpt; @@ -11,6 +9,42 @@ use crate::diagnostics::{DescribePlaceOpt, UseSpans}; use crate::prefixes::PrefixSet; use crate::MirBorrowckCtxt; +#[derive(Debug)] +pub enum IllegalMoveOriginKind<'tcx> { + /// Illegal move due to attempt to move from behind a reference. + BorrowedContent { + /// The place the reference refers to: if erroneous code was trying to + /// move from `(*x).f` this will be `*x`. + target_place: Place<'tcx>, + }, + + /// Illegal move due to attempt to move from field of an ADT that + /// implements `Drop`. Rust maintains invariant that all `Drop` + /// ADT's remain fully-initialized so that user-defined destructor + /// can safely read from all of the ADT's fields. + InteriorOfTypeWithDestructor { container_ty: Ty<'tcx> }, + + /// Illegal move due to attempt to move out of a slice or array. + InteriorOfSliceOrArray { ty: Ty<'tcx>, is_index: bool }, +} + +#[derive(Debug)] +pub(crate) struct MoveError<'tcx> { + place: Place<'tcx>, + location: Location, + kind: IllegalMoveOriginKind<'tcx>, +} + +impl<'tcx> MoveError<'tcx> { + pub(crate) fn new( + place: Place<'tcx>, + location: Location, + kind: IllegalMoveOriginKind<'tcx>, + ) -> Self { + MoveError { place, location, kind } + } +} + // Often when desugaring a pattern match we may have many individual moves in // MIR that are all part of one operation from the user's point-of-view. For // example: @@ -53,20 +87,18 @@ enum GroupedMoveError<'tcx> { } impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { - pub(crate) fn report_move_errors(&mut self, move_errors: Vec<(Place<'tcx>, MoveError<'tcx>)>) { - let grouped_errors = self.group_move_errors(move_errors); + pub(crate) fn report_move_errors(&mut self) { + let grouped_errors = self.group_move_errors(); for error in grouped_errors { self.report(error); } } - fn group_move_errors( - &self, - errors: Vec<(Place<'tcx>, MoveError<'tcx>)>, - ) -> Vec<GroupedMoveError<'tcx>> { + fn group_move_errors(&mut self) -> Vec<GroupedMoveError<'tcx>> { let mut grouped_errors = Vec::new(); - for (original_path, error) in errors { - self.append_to_grouped_errors(&mut grouped_errors, original_path, error); + let errors = std::mem::take(&mut self.move_errors); + for error in errors { + self.append_to_grouped_errors(&mut grouped_errors, error); } grouped_errors } @@ -74,66 +106,58 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { fn append_to_grouped_errors( &self, grouped_errors: &mut Vec<GroupedMoveError<'tcx>>, - original_path: Place<'tcx>, error: MoveError<'tcx>, ) { - match error { - MoveError::UnionMove { .. } => { - unimplemented!("don't know how to report union move errors yet.") - } - MoveError::IllegalMove { cannot_move_out_of: IllegalMoveOrigin { location, kind } } => { - // Note: that the only time we assign a place isn't a temporary - // to a user variable is when initializing it. - // If that ever stops being the case, then the ever initialized - // flow could be used. - if let Some(StatementKind::Assign(box ( - place, - Rvalue::Use(Operand::Move(move_from)), - ))) = self.body.basic_blocks[location.block] - .statements - .get(location.statement_index) - .map(|stmt| &stmt.kind) + let MoveError { place: original_path, location, kind } = error; + + // Note: that the only time we assign a place isn't a temporary + // to a user variable is when initializing it. + // If that ever stops being the case, then the ever initialized + // flow could be used. + if let Some(StatementKind::Assign(box (place, Rvalue::Use(Operand::Move(move_from))))) = + self.body.basic_blocks[location.block] + .statements + .get(location.statement_index) + .map(|stmt| &stmt.kind) + { + if let Some(local) = place.as_local() { + let local_decl = &self.body.local_decls[local]; + // opt_match_place is the + // match_span is the span of the expression being matched on + // match *x.y { ... } match_place is Some(*x.y) + // ^^^^ match_span is the span of *x.y + // + // opt_match_place is None for let [mut] x = ... statements, + // whether or not the right-hand side is a place expression + if let LocalInfo::User(BindingForm::Var(VarBindingForm { + opt_match_place: Some((opt_match_place, match_span)), + binding_mode: _, + opt_ty_info: _, + pat_span: _, + })) = *local_decl.local_info() { - if let Some(local) = place.as_local() { - let local_decl = &self.body.local_decls[local]; - // opt_match_place is the - // match_span is the span of the expression being matched on - // match *x.y { ... } match_place is Some(*x.y) - // ^^^^ match_span is the span of *x.y - // - // opt_match_place is None for let [mut] x = ... statements, - // whether or not the right-hand side is a place expression - if let LocalInfo::User(BindingForm::Var(VarBindingForm { - opt_match_place: Some((opt_match_place, match_span)), - binding_mode: _, - opt_ty_info: _, - pat_span: _, - })) = *local_decl.local_info() - { - let stmt_source_info = self.body.source_info(location); - self.append_binding_error( - grouped_errors, - kind, - original_path, - *move_from, - local, - opt_match_place, - match_span, - stmt_source_info.span, - ); - return; - } - } + let stmt_source_info = self.body.source_info(location); + self.append_binding_error( + grouped_errors, + kind, + original_path, + *move_from, + local, + opt_match_place, + match_span, + stmt_source_info.span, + ); + return; } - - let move_spans = self.move_spans(original_path.as_ref(), location); - grouped_errors.push(GroupedMoveError::OtherIllegalMove { - use_spans: move_spans, - original_path, - kind, - }); } } + + let move_spans = self.move_spans(original_path.as_ref(), location); + grouped_errors.push(GroupedMoveError::OtherIllegalMove { + use_spans: move_spans, + original_path, + kind, + }); } fn append_binding_error( diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index ee34be847cc..1a74582389d 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -31,13 +31,8 @@ use rustc_index::{IndexSlice, IndexVec}; use rustc_infer::infer::{ InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, TyCtxtInferExt, }; -use rustc_middle::mir::{ - traversal, Body, ClearCrossCrate, Local, Location, MutBorrowKind, Mutability, - NonDivergingIntrinsic, Operand, Place, PlaceElem, PlaceRef, VarDebugInfoContents, -}; -use rustc_middle::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind}; -use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind}; -use rustc_middle::mir::{ProjectionElem, Promoted, Rvalue, Statement, StatementKind}; +use rustc_middle::mir::tcx::PlaceTy; +use rustc_middle::mir::*; use rustc_middle::query::Providers; use rustc_middle::traits::DefiningAnchor; use rustc_middle::ty::{self, CapturedPlace, ParamEnv, RegionVid, TyCtxt}; @@ -55,13 +50,13 @@ use rustc_mir_dataflow::impls::{ EverInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces, }; use rustc_mir_dataflow::move_paths::{InitIndex, MoveOutIndex, MovePathIndex}; -use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult, MoveData, MoveError}; +use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult, MoveData}; use rustc_mir_dataflow::Analysis; use rustc_mir_dataflow::MoveDataParamEnv; use crate::session_diagnostics::VarNeedNotMut; -use self::diagnostics::{AccessKind, RegionName}; +use self::diagnostics::{AccessKind, IllegalMoveOriginKind, MoveError, RegionName}; use self::location::LocationTable; use self::prefixes::PrefixSet; use consumers::{BodyWithBorrowckFacts, ConsumerOptions}; @@ -224,14 +219,10 @@ fn do_mir_borrowck<'tcx>( let location_table_owned = LocationTable::new(body); let location_table = &location_table_owned; - let (move_data, move_errors): (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>) = - match MoveData::gather_moves(&body, tcx, param_env) { - Ok(move_data) => (move_data, Vec::new()), - Err((move_data, move_errors)) => (move_data, move_errors), - }; - let promoted_errors = promoted + let move_data = MoveData::gather_moves(&body, tcx, param_env, |_| true); + let promoted_move_data = promoted .iter_enumerated() - .map(|(idx, body)| (idx, MoveData::gather_moves(&body, tcx, param_env))); + .map(|(idx, body)| (idx, MoveData::gather_moves(&body, tcx, param_env, |_| true))); let mdpe = MoveDataParamEnv { move_data, param_env }; @@ -313,36 +304,49 @@ fn do_mir_borrowck<'tcx>( true }; - for (idx, move_data_results) in promoted_errors { - let promoted_body = &promoted[idx]; + for (idx, move_data) in promoted_move_data { + use rustc_middle::mir::visit::Visitor; - if let Err((move_data, move_errors)) = move_data_results { - let mut promoted_mbcx = MirBorrowckCtxt { - infcx: &infcx, - param_env, - body: promoted_body, - move_data: &move_data, - location_table, // no need to create a real one for the promoted, it is not used - movable_coroutine, - fn_self_span_reported: Default::default(), - locals_are_invalidated_at_exit, - access_place_error_reported: Default::default(), - reservation_error_reported: Default::default(), - uninitialized_error_reported: Default::default(), - regioncx: regioncx.clone(), - used_mut: Default::default(), - used_mut_upvars: SmallVec::new(), - borrow_set: Rc::clone(&borrow_set), - upvars: Vec::new(), - local_names: IndexVec::from_elem(None, &promoted_body.local_decls), - region_names: RefCell::default(), - next_region_name: RefCell::new(1), - polonius_output: None, - errors, - }; - promoted_mbcx.report_move_errors(move_errors); - errors = promoted_mbcx.errors; + let promoted_body = &promoted[idx]; + let mut promoted_mbcx = MirBorrowckCtxt { + infcx: &infcx, + param_env, + body: promoted_body, + move_data: &move_data, + location_table, // no need to create a real one for the promoted, it is not used + movable_coroutine, + fn_self_span_reported: Default::default(), + locals_are_invalidated_at_exit, + access_place_error_reported: Default::default(), + reservation_error_reported: Default::default(), + uninitialized_error_reported: Default::default(), + regioncx: regioncx.clone(), + used_mut: Default::default(), + used_mut_upvars: SmallVec::new(), + borrow_set: Rc::clone(&borrow_set), + upvars: Vec::new(), + local_names: IndexVec::from_elem(None, &promoted_body.local_decls), + region_names: RefCell::default(), + next_region_name: RefCell::new(1), + polonius_output: None, + move_errors: Vec::new(), + errors, }; + MoveVisitor { ctxt: &mut promoted_mbcx }.visit_body(promoted_body); + promoted_mbcx.report_move_errors(); + errors = promoted_mbcx.errors; + + struct MoveVisitor<'a, 'cx, 'tcx> { + ctxt: &'a mut MirBorrowckCtxt<'cx, 'tcx>, + } + + impl<'tcx> Visitor<'tcx> for MoveVisitor<'_, '_, 'tcx> { + fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) { + if let Operand::Move(place) = operand { + self.ctxt.check_movable_place(location, *place); + } + } + } } let mut mbcx = MirBorrowckCtxt { @@ -366,6 +370,7 @@ fn do_mir_borrowck<'tcx>( region_names: RefCell::default(), next_region_name: RefCell::new(1), polonius_output, + move_errors: Vec::new(), errors, }; @@ -378,8 +383,6 @@ fn do_mir_borrowck<'tcx>( borrows: flow_borrows, }; - mbcx.report_move_errors(move_errors); - rustc_mir_dataflow::visit_results( body, traversal::reverse_postorder(body).map(|(bb, _)| bb), @@ -387,6 +390,8 @@ fn do_mir_borrowck<'tcx>( &mut mbcx, ); + mbcx.report_move_errors(); + // For each non-user used mutable variable, check if it's been assigned from // a user-declared local. If so, then put that local into the used_mut set. // Note that this set is expected to be small - only upvars from closures @@ -595,6 +600,7 @@ struct MirBorrowckCtxt<'cx, 'tcx> { polonius_output: Option<Rc<PoloniusOutput>>, errors: error::BorrowckErrors<'tcx>, + move_errors: Vec<MoveError<'tcx>>, } // Check that: @@ -725,7 +731,6 @@ impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorro } TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => { self.consume_operand(loc, (cond, span), flow_state); - use rustc_middle::mir::AssertKind; if let AssertKind::BoundsCheck { len, index } = &**msg { self.consume_operand(loc, (len, span), flow_state); self.consume_operand(loc, (index, span), flow_state); @@ -1409,7 +1414,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // As such we have to search for the local that this // capture comes from and mark it as being used as mut. - let temp_mpi = self.move_data.rev_lookup.find_local(local); + let Some(temp_mpi) = self.move_data.rev_lookup.find_local(local) else { + bug!("temporary should be tracked"); + }; let init = if let [init_index] = *self.move_data.init_path_map[temp_mpi] { &self.move_data.inits[init_index] } else { @@ -1469,6 +1476,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); } Operand::Move(place) => { + // Check if moving from this place makes sense. + self.check_movable_place(location, place); + // move of place: check if this is move of already borrowed path self.access_place( location, @@ -1590,6 +1600,131 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } + fn check_movable_place(&mut self, location: Location, place: Place<'tcx>) { + use IllegalMoveOriginKind::*; + + let body = self.body; + let tcx = self.infcx.tcx; + let mut place_ty = PlaceTy::from_ty(body.local_decls[place.local].ty); + for (place_ref, elem) in place.iter_projections() { + match elem { + ProjectionElem::Deref => match place_ty.ty.kind() { + ty::Ref(..) | ty::RawPtr(..) => { + self.move_errors.push(MoveError::new( + place, + location, + BorrowedContent { + target_place: place_ref.project_deeper(&[elem], tcx), + }, + )); + return; + } + ty::Adt(adt, _) => { + if !adt.is_box() { + bug!("Adt should be a box type when Place is deref"); + } + } + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Slice(_) + | ty::FnDef(_, _) + | ty::FnPtr(_) + | ty::Dynamic(_, _, _) + | ty::Closure(_, _) + | ty::Coroutine(_, _, _) + | ty::CoroutineWitness(..) + | ty::Never + | ty::Tuple(_) + | ty::Alias(_, _) + | ty::Param(_) + | ty::Bound(_, _) + | ty::Infer(_) + | ty::Error(_) + | ty::Placeholder(_) => { + bug!("When Place is Deref it's type shouldn't be {place_ty:#?}") + } + }, + ProjectionElem::Field(_, _) => match place_ty.ty.kind() { + ty::Adt(adt, _) => { + if adt.has_dtor(tcx) { + self.move_errors.push(MoveError::new( + place, + location, + InteriorOfTypeWithDestructor { container_ty: place_ty.ty }, + )); + return; + } + } + ty::Closure(_, _) | ty::Coroutine(_, _, _) | ty::Tuple(_) => (), + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Slice(_) + | ty::RawPtr(_) + | ty::Ref(_, _, _) + | ty::FnDef(_, _) + | ty::FnPtr(_) + | ty::Dynamic(_, _, _) + | ty::CoroutineWitness(..) + | ty::Never + | ty::Alias(_, _) + | ty::Param(_) + | ty::Bound(_, _) + | ty::Infer(_) + | ty::Error(_) + | ty::Placeholder(_) => bug!( + "When Place contains ProjectionElem::Field it's type shouldn't be {place_ty:#?}" + ), + }, + ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { + match place_ty.ty.kind() { + ty::Slice(_) => { + self.move_errors.push(MoveError::new( + place, + location, + InteriorOfSliceOrArray { ty: place_ty.ty, is_index: false }, + )); + return; + } + ty::Array(_, _) => (), + _ => bug!("Unexpected type {:#?}", place_ty.ty), + } + } + ProjectionElem::Index(_) => match place_ty.ty.kind() { + ty::Array(..) | ty::Slice(..) => { + self.move_errors.push(MoveError::new( + place, + location, + InteriorOfSliceOrArray { ty: place_ty.ty, is_index: true }, + )); + return; + } + _ => bug!("Unexpected type {place_ty:#?}"), + }, + // `OpaqueCast`: only transmutes the type, so no moves there. + // `Downcast` : only changes information about a `Place` without moving. + // `Subtype` : only transmutes the type, so no moves. + // So it's safe to skip these. + ProjectionElem::OpaqueCast(_) + | ProjectionElem::Subtype(_) + | ProjectionElem::Downcast(_, _) => (), + } + + place_ty = place_ty.projection_ty(tcx, elem); + } + } + fn check_if_full_path_is_moved( &mut self, location: Location, @@ -2074,7 +2209,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { local: Local, flow_state: &Flows<'cx, 'tcx>, ) -> Option<InitIndex> { - let mpi = self.move_data.rev_lookup.find_local(local); + let mpi = self.move_data.rev_lookup.find_local(local)?; let ii = &self.move_data.init_path_map[mpi]; ii.into_iter().find(|&&index| flow_state.ever_inits.contains(index)).copied() } diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 96cbe98c216..05c2cbd4969 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -644,11 +644,12 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.scc_universes[scc] } - /// Once region solving has completed, this function will return - /// the member constraints that were applied to the value of a given - /// region `r`. See `AppliedMemberConstraint`. - pub(crate) fn applied_member_constraints(&self, r: RegionVid) -> &[AppliedMemberConstraint] { - let scc = self.constraint_sccs.scc(r); + /// Once region solving has completed, this function will return the member constraints that + /// were applied to the value of a given SCC `scc`. See `AppliedMemberConstraint`. + pub(crate) fn applied_member_constraints( + &self, + scc: ConstraintSccIndex, + ) -> &[AppliedMemberConstraint] { binary_search_util::binary_search_slice( &self.member_constraints_applied, |applied| applied.member_region_scc, @@ -1945,7 +1946,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Member constraints can also give rise to `'r: 'x` edges that // were not part of the graph initially, so watch out for those. // (But they are extremely rare; this loop is very cold.) - for constraint in self.applied_member_constraints(r) { + for constraint in self.applied_member_constraints(self.constraint_sccs.scc(r)) { let p_c = &self.member_constraints[constraint.member_constraint_index]; let constraint = OutlivesConstraint { sup: r, @@ -2292,11 +2293,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.constraint_sccs.as_ref() } - /// Returns whether the given SCC has any member constraints. - pub(crate) fn scc_has_member_constraints(&self, scc: ConstraintSccIndex) -> bool { - self.member_constraints.indices(scc).next().is_some() - } - /// Returns whether the given SCC is live at all points: whether the representative is a /// placeholder or a free region. pub(crate) fn scc_is_live_at_all_points(&self, scc: ConstraintSccIndex) -> bool { diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 80e6c6c1dfb..cfefe0a3e65 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -317,7 +317,7 @@ impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> { fn compute_drop_live_points_for(&mut self, local: Local) { debug!("compute_drop_live_points_for(local={:?})", local); - let mpi = self.cx.move_data.rev_lookup.find_local(local); + let Some(mpi) = self.cx.move_data.rev_lookup.find_local(local) else { return }; debug!("compute_drop_live_points_for: mpi = {:?}", mpi); // Find the drops where `local` is initialized. diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index be09820d08d..e864337e5dc 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -9,6 +9,7 @@ test = false [dependencies] bitflags = "1.0" cstr = "0.2" +itertools = "0.10.5" libc = "0.2" measureme = "10.0.0" object = { version = "0.32.0", default-features = false, features = [ diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs index 84319b4ba2d..93a8a4b1d5e 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs @@ -1,16 +1,18 @@ use crate::coverageinfo::ffi::{Counter, CounterExpression, ExprKind}; +use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxIndexSet; use rustc_index::bit_set::BitSet; use rustc_middle::mir::coverage::{ CodeRegion, CounterId, CovTerm, Expression, ExpressionId, FunctionCoverageInfo, Mapping, Op, }; use rustc_middle::ty::Instance; +use rustc_span::Symbol; /// Holds all of the coverage mapping data associated with a function instance, /// collected during traversal of `Coverage` statements in the function's MIR. #[derive(Debug)] -pub struct FunctionCoverage<'tcx> { +pub struct FunctionCoverageCollector<'tcx> { /// Coverage info that was attached to this function by the instrumentor. function_coverage_info: &'tcx FunctionCoverageInfo, is_used: bool, @@ -26,7 +28,7 @@ pub struct FunctionCoverage<'tcx> { expressions_seen: BitSet<ExpressionId>, } -impl<'tcx> FunctionCoverage<'tcx> { +impl<'tcx> FunctionCoverageCollector<'tcx> { /// Creates a new set of coverage data for a used (called) function. pub fn new( instance: Instance<'tcx>, @@ -76,11 +78,6 @@ impl<'tcx> FunctionCoverage<'tcx> { } } - /// Returns true for a used (called) function, and false for an unused function. - pub fn is_used(&self) -> bool { - self.is_used - } - /// Marks a counter ID as having been seen in a counter-increment statement. #[instrument(level = "debug", skip(self))] pub(crate) fn mark_counter_id_seen(&mut self, id: CounterId) { @@ -165,64 +162,71 @@ impl<'tcx> FunctionCoverage<'tcx> { ZeroExpressions(zero_expressions) } + pub(crate) fn into_finished(self) -> FunctionCoverage<'tcx> { + let zero_expressions = self.identify_zero_expressions(); + let FunctionCoverageCollector { function_coverage_info, is_used, counters_seen, .. } = self; + + FunctionCoverage { function_coverage_info, is_used, counters_seen, zero_expressions } + } +} + +pub(crate) struct FunctionCoverage<'tcx> { + function_coverage_info: &'tcx FunctionCoverageInfo, + is_used: bool, + + counters_seen: BitSet<CounterId>, + zero_expressions: ZeroExpressions, +} + +impl<'tcx> FunctionCoverage<'tcx> { + /// Returns true for a used (called) function, and false for an unused function. + pub(crate) fn is_used(&self) -> bool { + self.is_used + } + /// Return the source hash, generated from the HIR node structure, and used to indicate whether /// or not the source code structure changed between different compilations. pub fn source_hash(&self) -> u64 { if self.is_used { self.function_coverage_info.function_source_hash } else { 0 } } - /// Generate an array of CounterExpressions, and an iterator over all `Counter`s and their - /// associated `Regions` (from which the LLVM-specific `CoverageMapGenerator` will create - /// `CounterMappingRegion`s. - pub fn get_expressions_and_counter_regions( - &self, - ) -> (Vec<CounterExpression>, impl Iterator<Item = (Counter, &CodeRegion)>) { - let zero_expressions = self.identify_zero_expressions(); - - let counter_expressions = self.counter_expressions(&zero_expressions); - // Expression IDs are indices into `self.expressions`, and on the LLVM - // side they will be treated as indices into `counter_expressions`, so - // the two vectors should correspond 1:1. - assert_eq!(self.function_coverage_info.expressions.len(), counter_expressions.len()); - - let counter_regions = self.counter_regions(zero_expressions); - - (counter_expressions, counter_regions) + /// Returns an iterator over all filenames used by this function's mappings. + pub(crate) fn all_file_names(&self) -> impl Iterator<Item = Symbol> + Captures<'_> { + self.function_coverage_info.mappings.iter().map(|mapping| mapping.code_region.file_name) } /// Convert this function's coverage expression data into a form that can be /// passed through FFI to LLVM. - fn counter_expressions(&self, zero_expressions: &ZeroExpressions) -> Vec<CounterExpression> { + pub(crate) fn counter_expressions( + &self, + ) -> impl Iterator<Item = CounterExpression> + ExactSizeIterator + Captures<'_> { // We know that LLVM will optimize out any unused expressions before // producing the final coverage map, so there's no need to do the same // thing on the Rust side unless we're confident we can do much better. // (See `CounterExpressionsMinimizer` in `CoverageMappingWriter.cpp`.) let counter_from_operand = |operand: CovTerm| match operand { - CovTerm::Expression(id) if zero_expressions.contains(id) => Counter::ZERO, + CovTerm::Expression(id) if self.zero_expressions.contains(id) => Counter::ZERO, _ => Counter::from_term(operand), }; - self.function_coverage_info - .expressions - .iter() - .map(|&Expression { lhs, op, rhs }| CounterExpression { + self.function_coverage_info.expressions.iter().map(move |&Expression { lhs, op, rhs }| { + CounterExpression { lhs: counter_from_operand(lhs), kind: match op { Op::Add => ExprKind::Add, Op::Subtract => ExprKind::Subtract, }, rhs: counter_from_operand(rhs), - }) - .collect::<Vec<_>>() + } + }) } /// Converts this function's coverage mappings into an intermediate form /// that will be used by `mapgen` when preparing for FFI. - fn counter_regions( + pub(crate) fn counter_regions( &self, - zero_expressions: ZeroExpressions, - ) -> impl Iterator<Item = (Counter, &CodeRegion)> { + ) -> impl Iterator<Item = (Counter, &CodeRegion)> + ExactSizeIterator { // Historically, mappings were stored directly in counter/expression // statements in MIR, and MIR optimizations would sometimes remove them. // That's mostly no longer true, so now we detect cases where that would @@ -230,7 +234,7 @@ impl<'tcx> FunctionCoverage<'tcx> { let counter_for_term = move |term: CovTerm| { let force_to_zero = match term { CovTerm::Counter(id) => !self.counters_seen.contains(id), - CovTerm::Expression(id) => zero_expressions.contains(id), + CovTerm::Expression(id) => self.zero_expressions.contains(id), CovTerm::Zero => false, }; if force_to_zero { Counter::ZERO } else { Counter::from_term(term) } diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index 2f825b801ac..274e0aeaaba 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -1,11 +1,12 @@ use crate::common::CodegenCx; use crate::coverageinfo; use crate::coverageinfo::ffi::CounterMappingRegion; -use crate::coverageinfo::map_data::FunctionCoverage; +use crate::coverageinfo::map_data::{FunctionCoverage, FunctionCoverageCollector}; use crate::llvm; +use itertools::Itertools as _; use rustc_codegen_ssa::traits::{BaseTypeMethods, ConstMethods}; -use rustc_data_structures::fx::FxIndexSet; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_index::IndexVec; @@ -57,11 +58,32 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) { return; } - let mut global_file_table = GlobalFileTable::new(tcx); + let function_coverage_entries = function_coverage_map + .into_iter() + .map(|(instance, function_coverage)| (instance, function_coverage.into_finished())) + .collect::<Vec<_>>(); + + let all_file_names = + function_coverage_entries.iter().flat_map(|(_, fn_cov)| fn_cov.all_file_names()); + let global_file_table = GlobalFileTable::new(all_file_names); + + // Encode all filenames referenced by coverage mappings in this CGU. + let filenames_buffer = global_file_table.make_filenames_buffer(tcx); + + let filenames_size = filenames_buffer.len(); + let filenames_val = cx.const_bytes(&filenames_buffer); + let filenames_ref = coverageinfo::hash_bytes(&filenames_buffer); + + // Generate the coverage map header, which contains the filenames used by + // this CGU's coverage mappings, and store it in a well-known global. + let cov_data_val = generate_coverage_map(cx, version, filenames_size, filenames_val); + coverageinfo::save_cov_data_to_mod(cx, cov_data_val); + + let mut unused_function_names = Vec::new(); + let covfun_section_name = coverageinfo::covfun_section_name(cx); // Encode coverage mappings and generate function records - let mut function_data = Vec::new(); - for (instance, function_coverage) in function_coverage_map { + for (instance, function_coverage) in function_coverage_entries { debug!("Generate function coverage for {}, {:?}", cx.codegen_unit.name(), instance); let mangled_function_name = tcx.symbol_name(instance).name; @@ -69,7 +91,7 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) { let is_used = function_coverage.is_used(); let coverage_mapping_buffer = - encode_mappings_for_function(&mut global_file_table, &function_coverage); + encode_mappings_for_function(&global_file_table, &function_coverage); if coverage_mapping_buffer.is_empty() { if function_coverage.is_used() { @@ -83,23 +105,6 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) { } } - function_data.push((mangled_function_name, source_hash, is_used, coverage_mapping_buffer)); - } - - // Encode all filenames referenced by counters/expressions in this module - let filenames_buffer = global_file_table.into_filenames_buffer(); - - let filenames_size = filenames_buffer.len(); - let filenames_val = cx.const_bytes(&filenames_buffer); - let filenames_ref = coverageinfo::hash_bytes(&filenames_buffer); - - // Generate the LLVM IR representation of the coverage map and store it in a well-known global - let cov_data_val = generate_coverage_map(cx, version, filenames_size, filenames_val); - - let mut unused_function_names = Vec::new(); - - let covfun_section_name = coverageinfo::covfun_section_name(cx); - for (mangled_function_name, source_hash, is_used, coverage_mapping_buffer) in function_data { if !is_used { unused_function_names.push(mangled_function_name); } @@ -133,92 +138,125 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) { llvm::set_linkage(array, llvm::Linkage::InternalLinkage); llvm::set_initializer(array, initializer); } - - // Save the coverage data value to LLVM IR - coverageinfo::save_cov_data_to_mod(cx, cov_data_val); } +/// Maps "global" (per-CGU) file ID numbers to their underlying filenames. struct GlobalFileTable { - global_file_table: FxIndexSet<Symbol>, + /// This "raw" table doesn't include the working dir, so a filename's + /// global ID is its index in this set **plus one**. + raw_file_table: FxIndexSet<Symbol>, } impl GlobalFileTable { - fn new(tcx: TyCtxt<'_>) -> Self { - let mut global_file_table = FxIndexSet::default(); + fn new(all_file_names: impl IntoIterator<Item = Symbol>) -> Self { + // Collect all of the filenames into a set. Filenames usually come in + // contiguous runs, so we can dedup adjacent ones to save work. + let mut raw_file_table = all_file_names.into_iter().dedup().collect::<FxIndexSet<Symbol>>(); + + // Sort the file table by its actual string values, not the arbitrary + // ordering of its symbols. + raw_file_table.sort_unstable_by(|a, b| a.as_str().cmp(b.as_str())); + + Self { raw_file_table } + } + + fn global_file_id_for_file_name(&self, file_name: Symbol) -> u32 { + let raw_id = self.raw_file_table.get_index_of(&file_name).unwrap_or_else(|| { + bug!("file name not found in prepared global file table: {file_name}"); + }); + // The raw file table doesn't include an entry for the working dir + // (which has ID 0), so add 1 to get the correct ID. + (raw_id + 1) as u32 + } + + fn make_filenames_buffer(&self, tcx: TyCtxt<'_>) -> Vec<u8> { // LLVM Coverage Mapping Format version 6 (zero-based encoded as 5) // requires setting the first filename to the compilation directory. // Since rustc generates coverage maps with relative paths, the // compilation directory can be combined with the relative paths // to get absolute paths, if needed. use rustc_session::RemapFileNameExt; - let working_dir = - Symbol::intern(&tcx.sess.opts.working_dir.for_codegen(&tcx.sess).to_string_lossy()); - global_file_table.insert(working_dir); - Self { global_file_table } - } - - fn global_file_id_for_file_name(&mut self, file_name: Symbol) -> u32 { - let (global_file_id, _) = self.global_file_table.insert_full(file_name); - global_file_id as u32 - } - - fn into_filenames_buffer(self) -> Vec<u8> { - // This method takes `self` so that the caller can't accidentally - // modify the original file table after encoding it into a buffer. + let working_dir: &str = &tcx.sess.opts.working_dir.for_codegen(&tcx.sess).to_string_lossy(); llvm::build_byte_buffer(|buffer| { coverageinfo::write_filenames_section_to_buffer( - self.global_file_table.iter().map(Symbol::as_str), + // Insert the working dir at index 0, before the other filenames. + std::iter::once(working_dir).chain(self.raw_file_table.iter().map(Symbol::as_str)), buffer, ); }) } } +rustc_index::newtype_index! { + // Tell the newtype macro to not generate `Encode`/`Decode` impls. + #[custom_encodable] + struct LocalFileId {} +} + +/// Holds a mapping from "local" (per-function) file IDs to "global" (per-CGU) +/// file IDs. +#[derive(Default)] +struct VirtualFileMapping { + local_to_global: IndexVec<LocalFileId, u32>, + global_to_local: FxIndexMap<u32, LocalFileId>, +} + +impl VirtualFileMapping { + fn local_id_for_global(&mut self, global_file_id: u32) -> LocalFileId { + *self + .global_to_local + .entry(global_file_id) + .or_insert_with(|| self.local_to_global.push(global_file_id)) + } + + fn into_vec(self) -> Vec<u32> { + self.local_to_global.raw + } +} + /// Using the expressions and counter regions collected for a single function, /// generate the variable-sized payload of its corresponding `__llvm_covfun` /// entry. The payload is returned as a vector of bytes. /// /// Newly-encountered filenames will be added to the global file table. fn encode_mappings_for_function( - global_file_table: &mut GlobalFileTable, + global_file_table: &GlobalFileTable, function_coverage: &FunctionCoverage<'_>, ) -> Vec<u8> { - let (expressions, counter_regions) = function_coverage.get_expressions_and_counter_regions(); - - let mut counter_regions = counter_regions.collect::<Vec<_>>(); + let counter_regions = function_coverage.counter_regions(); if counter_regions.is_empty() { return Vec::new(); } - let mut virtual_file_mapping = IndexVec::<u32, u32>::new(); + let expressions = function_coverage.counter_expressions().collect::<Vec<_>>(); + + let mut virtual_file_mapping = VirtualFileMapping::default(); let mut mapping_regions = Vec::with_capacity(counter_regions.len()); - // Sort and group the list of (counter, region) mapping pairs by filename. - // (Preserve any further ordering imposed by `FunctionCoverage`.) + // Group mappings into runs with the same filename, preserving the order + // yielded by `FunctionCoverage`. // Prepare file IDs for each filename, and prepare the mapping data so that // we can pass it through FFI to LLVM. - counter_regions.sort_by_key(|(_counter, region)| region.file_name); - for counter_regions_for_file in - counter_regions.group_by(|(_, a), (_, b)| a.file_name == b.file_name) + for (file_name, counter_regions_for_file) in + &counter_regions.group_by(|(_counter, region)| region.file_name) { - // Look up (or allocate) the global file ID for this filename. - let file_name = counter_regions_for_file[0].1.file_name; + // Look up the global file ID for this filename. let global_file_id = global_file_table.global_file_id_for_file_name(file_name); // Associate that global file ID with a local file ID for this function. - let local_file_id: u32 = virtual_file_mapping.push(global_file_id); - debug!(" file id: local {local_file_id} => global {global_file_id} = '{file_name:?}'"); + let local_file_id = virtual_file_mapping.local_id_for_global(global_file_id); + debug!(" file id: {local_file_id:?} => global {global_file_id} = '{file_name:?}'"); // For each counter/region pair in this function+file, convert it to a // form suitable for FFI. - for &(counter, region) in counter_regions_for_file { + for (counter, region) in counter_regions_for_file { let CodeRegion { file_name: _, start_line, start_col, end_line, end_col } = *region; debug!("Adding counter {counter:?} to map for {region:?}"); mapping_regions.push(CounterMappingRegion::code_region( counter, - local_file_id, + local_file_id.as_u32(), start_line, start_col, end_line, @@ -230,7 +268,7 @@ fn encode_mappings_for_function( // Encode the function's coverage mappings into a buffer. llvm::build_byte_buffer(|buffer| { coverageinfo::write_mapping_to_buffer( - virtual_file_mapping.raw, + virtual_file_mapping.into_vec(), expressions, mapping_regions, buffer, @@ -419,7 +457,7 @@ fn add_unused_function_coverage<'tcx>( ) { // An unused function's mappings will automatically be rewritten to map to // zero, because none of its counters/expressions are marked as seen. - let function_coverage = FunctionCoverage::unused(instance, function_coverage_info); + let function_coverage = FunctionCoverageCollector::unused(instance, function_coverage_info); if let Some(coverage_context) = cx.coverage_context() { coverage_context.function_coverage_map.borrow_mut().insert(instance, function_coverage); diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index cf7c7e6be60..7d69756181a 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -3,7 +3,7 @@ use crate::llvm; use crate::builder::Builder; use crate::common::CodegenCx; use crate::coverageinfo::ffi::{CounterExpression, CounterMappingRegion}; -use crate::coverageinfo::map_data::FunctionCoverage; +use crate::coverageinfo::map_data::FunctionCoverageCollector; use libc::c_uint; use rustc_codegen_ssa::traits::{ @@ -29,7 +29,8 @@ const VAR_ALIGN_BYTES: usize = 8; /// A context object for maintaining all state needed by the coverageinfo module. pub struct CrateCoverageContext<'ll, 'tcx> { /// Coverage data for each instrumented function identified by DefId. - pub(crate) function_coverage_map: RefCell<FxHashMap<Instance<'tcx>, FunctionCoverage<'tcx>>>, + pub(crate) function_coverage_map: + RefCell<FxHashMap<Instance<'tcx>, FunctionCoverageCollector<'tcx>>>, pub(crate) pgo_func_name_var_map: RefCell<FxHashMap<Instance<'tcx>, &'ll llvm::Value>>, } @@ -41,7 +42,9 @@ impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> { } } - pub fn take_function_coverage_map(&self) -> FxHashMap<Instance<'tcx>, FunctionCoverage<'tcx>> { + pub fn take_function_coverage_map( + &self, + ) -> FxHashMap<Instance<'tcx>, FunctionCoverageCollector<'tcx>> { self.function_coverage_map.replace(FxHashMap::default()) } } @@ -93,7 +96,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { let mut coverage_map = coverage_context.function_coverage_map.borrow_mut(); let func_coverage = coverage_map .entry(instance) - .or_insert_with(|| FunctionCoverage::new(instance, function_coverage_info)); + .or_insert_with(|| FunctionCoverageCollector::new(instance, function_coverage_info)); let Coverage { kind } = coverage; match *kind { diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index c8bf7286edf..4832b147a54 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -50,7 +50,6 @@ mod utils; pub use self::create_scope_map::compute_mir_scopes; pub use self::metadata::build_global_var_di_node; -pub use self::metadata::extend_scope_to_file; #[allow(non_upper_case_globals)] const DW_TAG_auto_variable: c_uint = 0x100; diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 7a390d35a2b..8a6a5f79b3b 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -8,12 +8,13 @@ #![cfg_attr(not(bootstrap), feature(rustdoc_internals))] #![cfg_attr(not(bootstrap), doc(rust_logo))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![feature(exact_size_is_empty)] #![feature(extern_types)] #![feature(hash_raw_entry)] #![feature(iter_intersperse)] #![feature(let_chains)] +#![feature(min_specialization)] #![feature(never_type)] -#![feature(slice_group_by)] #![feature(impl_trait_in_assoc_type)] #![recursion_limit = "256"] #![allow(rustc::potential_query_instability)] diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index 38e8220569a..01e82339664 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -6,7 +6,6 @@ use crate::llvm; use crate::type_of::LayoutLlvmExt; use rustc_codegen_ssa::traits::*; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -pub use rustc_middle::mir::mono::MonoItem; use rustc_middle::mir::mono::{Linkage, Visibility}; use rustc_middle::ty::layout::{FnAbiOf, LayoutOf}; use rustc_middle::ty::{self, Instance, TypeVisitableExt}; 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 6b612c34837..46e00d0f176 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -3,7 +3,7 @@ use std::mem; use either::{Left, Right}; use rustc_hir::def::DefKind; -use rustc_middle::mir::interpret::{ErrorHandled, InterpErrorInfo}; +use rustc_middle::mir::interpret::{AllocId, ErrorHandled, InterpErrorInfo}; use rustc_middle::mir::pretty::write_allocation_bytes; use rustc_middle::mir::{self, ConstAlloc, ConstValue}; use rustc_middle::traits::Reveal; @@ -285,7 +285,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>( let def = cid.instance.def.def_id(); let is_static = tcx.is_static(def); - let mut ecx = InterpCx::new( + let ecx = InterpCx::new( tcx, tcx.def_span(def), key.param_env, @@ -293,7 +293,14 @@ pub fn eval_to_allocation_raw_provider<'tcx>( // they do not have to behave "as if" they were evaluated at runtime. CompileTimeInterpreter::new(CanAccessStatics::from(is_static), CheckAlignment::Error), ); + eval_in_interpreter(ecx, cid, is_static) +} +pub fn eval_in_interpreter<'mir, 'tcx>( + mut ecx: InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + cid: GlobalId<'tcx>, + is_static: bool, +) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> { let res = ecx.load_mir(cid.instance.def, cid.promoted); match res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body)) { Err(error) => { @@ -306,7 +313,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>( // If the current item has generics, we'd like to enrich the message with the // instance and its args: to show the actual compile-time values, in addition to // the expression, leading to the const eval error. - let instance = &key.value.instance; + let instance = &cid.instance; if !instance.args.is_empty() { let instance = with_no_trimmed_paths!(instance.to_string()); ("const_with_path", instance) @@ -331,56 +338,14 @@ pub fn eval_to_allocation_raw_provider<'tcx>( Ok(mplace) => { // Since evaluation had no errors, validate the resulting constant. // This is a separate `try` block to provide more targeted error reporting. - let validation: Result<_, InterpErrorInfo<'_>> = try { - let mut ref_tracking = RefTracking::new(mplace.clone()); - let mut inner = false; - while let Some((mplace, path)) = ref_tracking.todo.pop() { - let mode = match tcx.static_mutability(cid.instance.def_id()) { - Some(_) if cid.promoted.is_some() => { - // Promoteds in statics are allowed to point to statics. - CtfeValidationMode::Const { inner, allow_static_ptrs: true } - } - Some(_) => CtfeValidationMode::Regular, // a `static` - None => CtfeValidationMode::Const { inner, allow_static_ptrs: false }, - }; - ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode)?; - inner = true; - } - }; + let validation = + const_validate_mplace(&ecx, &mplace, is_static, cid.promoted.is_some()); + let alloc_id = mplace.ptr().provenance.unwrap(); // Validation failed, report an error. if let Err(error) = validation { - let (error, backtrace) = error.into_parts(); - backtrace.print_backtrace(); - - let ub_note = matches!(error, InterpError::UndefinedBehavior(_)).then(|| {}); - - let alloc = ecx.tcx.global_alloc(alloc_id).unwrap_memory().inner(); - let mut bytes = String::new(); - if alloc.size() != abi::Size::ZERO { - bytes = "\n".into(); - // FIXME(translation) there might be pieces that are translatable. - write_allocation_bytes(*ecx.tcx, alloc, &mut bytes, " ").unwrap(); - } - let raw_bytes = errors::RawBytesNote { - size: alloc.size().bytes(), - align: alloc.align.bytes(), - bytes, - }; - - Err(super::report( - *ecx.tcx, - error, - None, - || super::get_span_and_frames(&ecx), - move |span, frames| errors::UndefinedBehavior { - span, - ub_note, - frames, - raw_bytes, - }, - )) + Err(const_report_error(&ecx, error, alloc_id)) } else { // Convert to raw constant Ok(ConstAlloc { alloc_id, ty: mplace.layout.ty }) @@ -388,3 +353,61 @@ pub fn eval_to_allocation_raw_provider<'tcx>( } } } + +#[inline(always)] +pub fn const_validate_mplace<'mir, 'tcx>( + ecx: &InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + mplace: &MPlaceTy<'tcx>, + is_static: bool, + is_promoted: bool, +) -> InterpResult<'tcx> { + let mut ref_tracking = RefTracking::new(mplace.clone()); + let mut inner = false; + while let Some((mplace, path)) = ref_tracking.todo.pop() { + let mode = if is_static { + if is_promoted { + // Promoteds in statics are allowed to point to statics. + CtfeValidationMode::Const { inner, allow_static_ptrs: true } + } else { + // a `static` + CtfeValidationMode::Regular + } + } else { + CtfeValidationMode::Const { inner, allow_static_ptrs: false } + }; + ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode)?; + inner = true; + } + + Ok(()) +} + +#[inline(always)] +pub fn const_report_error<'mir, 'tcx>( + ecx: &InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + error: InterpErrorInfo<'tcx>, + alloc_id: AllocId, +) -> ErrorHandled { + let (error, backtrace) = error.into_parts(); + backtrace.print_backtrace(); + + let ub_note = matches!(error, InterpError::UndefinedBehavior(_)).then(|| {}); + + let alloc = ecx.tcx.global_alloc(alloc_id).unwrap_memory().inner(); + let mut bytes = String::new(); + if alloc.size() != abi::Size::ZERO { + bytes = "\n".into(); + // FIXME(translation) there might be pieces that are translatable. + write_allocation_bytes(*ecx.tcx, alloc, &mut bytes, " ").unwrap(); + } + let raw_bytes = + errors::RawBytesNote { size: alloc.size().bytes(), align: alloc.align.bytes(), bytes }; + + crate::const_eval::report( + *ecx.tcx, + error, + None, + || crate::const_eval::get_span_and_frames(ecx), + move |span, frames| errors::UndefinedBehavior { span, ub_note, frames, raw_bytes }, + ) +} diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 791370660fe..1202b0030ca 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -1074,17 +1074,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { instance: ty::Instance<'tcx>, ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { let gid = GlobalId { instance, promoted: None }; - // For statics we pick `ParamEnv::reveal_all`, because statics don't have generics - // and thus don't care about the parameter environment. While we could just use - // `self.param_env`, that would mean we invoke the query to evaluate the static - // with different parameter environments, thus causing the static to be evaluated - // multiple times. - let param_env = if self.tcx.is_static(gid.instance.def_id()) { - ty::ParamEnv::reveal_all() + let val = if self.tcx.is_static(gid.instance.def_id()) { + let alloc_id = self.tcx.reserve_and_set_static_alloc(gid.instance.def_id()); + + let ty = instance.ty(self.tcx.tcx, self.param_env); + mir::ConstAlloc { alloc_id, ty } } else { - self.param_env + self.ctfe_query(|tcx| tcx.eval_to_allocation_raw(self.param_env.and(gid)))? }; - let val = self.ctfe_query(|tcx| tcx.eval_to_allocation_raw(param_env.and(gid)))?; self.raw_const_to_mplace(val) } diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 99424518ad4..6716888290d 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -107,10 +107,10 @@ impl<Prov: Provenance> std::fmt::Display for ImmTy<'_, Prov> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { /// Helper function for printing a scalar to a FmtPrinter fn p<'a, 'tcx, Prov: Provenance>( - cx: FmtPrinter<'a, 'tcx>, + cx: &mut FmtPrinter<'a, 'tcx>, s: Scalar<Prov>, ty: Ty<'tcx>, - ) -> Result<FmtPrinter<'a, 'tcx>, std::fmt::Error> { + ) -> Result<(), std::fmt::Error> { match s { Scalar::Int(int) => cx.pretty_print_const_scalar_int(int, ty, true), Scalar::Ptr(ptr, _sz) => { @@ -125,8 +125,9 @@ impl<Prov: Provenance> std::fmt::Display for ImmTy<'_, Prov> { match self.imm { Immediate::Scalar(s) => { if let Some(ty) = tcx.lift(self.layout.ty) { - let cx = FmtPrinter::new(tcx, Namespace::ValueNS); - f.write_str(&p(cx, s, ty)?.into_buffer())?; + let s = + FmtPrinter::print_string(tcx, Namespace::ValueNS, |cx| p(cx, s, ty))?; + f.write_str(&s)?; return Ok(()); } write!(f, "{:x}: {}", s, self.layout.ty) diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 56b7b6bf826..d21fef58f3f 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -929,7 +929,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// - no pointers to statics. /// - no `UnsafeCell` or non-ZST `&mut`. #[inline(always)] - pub fn const_validate_operand( + pub(crate) fn const_validate_operand( &self, op: &OpTy<'tcx, M::Provenance>, path: Vec<PathElem>, diff --git a/compiler/rustc_const_eval/src/util/type_name.rs b/compiler/rustc_const_eval/src/util/type_name.rs index 8c7c360acbf..a82b65b19a8 100644 --- a/compiler/rustc_const_eval/src/util/type_name.rs +++ b/compiler/rustc_const_eval/src/util/type_name.rs @@ -18,11 +18,11 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { self.tcx } - fn print_region(self, _region: ty::Region<'_>) -> Result<Self, PrintError> { - Ok(self) + fn print_region(&mut self, _region: ty::Region<'_>) -> Result<(), PrintError> { + Ok(()) } - fn print_type(mut self, ty: Ty<'tcx>) -> Result<Self, PrintError> { + fn print_type(&mut self, ty: Ty<'tcx>) -> Result<(), PrintError> { match *ty.kind() { // Types without identity. ty::Bool @@ -43,7 +43,7 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { // Placeholders (all printed as `_` to uniformize them). ty::Param(_) | ty::Bound(..) | ty::Placeholder(_) | ty::Infer(_) | ty::Error(_) => { write!(self, "_")?; - Ok(self) + Ok(()) } // Types with identity (print the module path). @@ -60,44 +60,44 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { } } - fn print_const(self, ct: ty::Const<'tcx>) -> Result<Self, PrintError> { + fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError> { self.pretty_print_const(ct, false) } fn print_dyn_existential( - self, + &mut self, predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { self.pretty_print_dyn_existential(predicates) } - fn path_crate(mut self, cnum: CrateNum) -> Result<Self, PrintError> { + fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> { self.path.push_str(self.tcx.crate_name(cnum).as_str()); - Ok(self) + Ok(()) } fn path_qualified( - self, + &mut self, self_ty: Ty<'tcx>, trait_ref: Option<ty::TraitRef<'tcx>>, - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { self.pretty_path_qualified(self_ty, trait_ref) } fn path_append_impl( - self, - print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>, + &mut self, + print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, _disambiguated_data: &DisambiguatedDefPathData, self_ty: Ty<'tcx>, trait_ref: Option<ty::TraitRef<'tcx>>, - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { self.pretty_path_append_impl( - |mut cx| { - cx = print_prefix(cx)?; + |cx| { + print_prefix(cx)?; cx.path.push_str("::"); - Ok(cx) + Ok(()) }, self_ty, trait_ref, @@ -105,29 +105,29 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { } fn path_append( - mut self, - print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>, + &mut self, + print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, disambiguated_data: &DisambiguatedDefPathData, - ) -> Result<Self, PrintError> { - self = print_prefix(self)?; + ) -> Result<(), PrintError> { + print_prefix(self)?; write!(self.path, "::{}", disambiguated_data.data).unwrap(); - Ok(self) + Ok(()) } fn path_generic_args( - mut self, - print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>, + &mut self, + print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, args: &[GenericArg<'tcx>], - ) -> Result<Self, PrintError> { - self = print_prefix(self)?; + ) -> Result<(), PrintError> { + print_prefix(self)?; let args = args.iter().cloned().filter(|arg| !matches!(arg.unpack(), GenericArgKind::Lifetime(_))); if args.clone().next().is_some() { self.generic_delimiters(|cx| cx.comma_sep(args)) } else { - Ok(self) + Ok(()) } } } @@ -136,31 +136,31 @@ impl<'tcx> PrettyPrinter<'tcx> for AbsolutePathPrinter<'tcx> { fn should_print_region(&self, _region: ty::Region<'_>) -> bool { false } - fn comma_sep<T>(mut self, mut elems: impl Iterator<Item = T>) -> Result<Self, PrintError> + fn comma_sep<T>(&mut self, mut elems: impl Iterator<Item = T>) -> Result<(), PrintError> where T: Print<'tcx, Self>, { if let Some(first) = elems.next() { - self = first.print(self)?; + first.print(self)?; for elem in elems { self.path.push_str(", "); - self = elem.print(self)?; + elem.print(self)?; } } - Ok(self) + Ok(()) } fn generic_delimiters( - mut self, - f: impl FnOnce(Self) -> Result<Self, PrintError>, - ) -> Result<Self, PrintError> { + &mut self, + f: impl FnOnce(&mut Self) -> Result<(), PrintError>, + ) -> Result<(), PrintError> { write!(self, "<")?; - self = f(self)?; + f(self)?; write!(self, ">")?; - Ok(self) + Ok(()) } fn should_print_verbose(&self) -> bool { @@ -177,5 +177,7 @@ impl Write for AbsolutePathPrinter<'_> { } pub fn type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> String { - AbsolutePathPrinter { tcx, path: String::new() }.print_type(ty).unwrap().path + let mut printer = AbsolutePathPrinter { tcx, path: String::new() }; + printer.print_type(ty).unwrap(); + printer.path } diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index 62fff604f81..f957734b04d 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -54,7 +54,7 @@ pub use worker_local::{Registry, WorkerLocal}; mod parallel; #[cfg(parallel_compiler)] pub use parallel::scope; -pub use parallel::{join, par_for_each_in, par_map, parallel_guard}; +pub use parallel::{join, par_for_each_in, par_map, parallel_guard, try_par_for_each_in}; pub use std::sync::atomic::Ordering; pub use std::sync::atomic::Ordering::SeqCst; diff --git a/compiler/rustc_data_structures/src/sync/parallel.rs b/compiler/rustc_data_structures/src/sync/parallel.rs index 1944ddfb710..39dddb59569 100644 --- a/compiler/rustc_data_structures/src/sync/parallel.rs +++ b/compiler/rustc_data_structures/src/sync/parallel.rs @@ -77,6 +77,15 @@ mod disabled { }) } + pub fn try_par_for_each_in<T: IntoIterator, E: Copy>( + t: T, + mut for_each: impl FnMut(T::Item) -> Result<(), E>, + ) -> Result<(), E> { + parallel_guard(|guard| { + t.into_iter().fold(Ok(()), |ret, i| guard.run(|| for_each(i)).unwrap_or(ret).and(ret)) + }) + } + pub fn par_map<T: IntoIterator, R, C: FromIterator<R>>( t: T, mut map: impl FnMut(<<T as IntoIterator>::IntoIter as Iterator>::Item) -> R, @@ -167,6 +176,26 @@ mod enabled { }); } + pub fn try_par_for_each_in< + T: IntoIterator + IntoParallelIterator<Item = <T as IntoIterator>::Item>, + E: Copy + Send, + >( + t: T, + for_each: impl Fn(<T as IntoIterator>::Item) -> Result<(), E> + DynSync + DynSend, + ) -> Result<(), E> { + parallel_guard(|guard| { + if mode::is_dyn_thread_safe() { + let for_each = FromDyn::from(for_each); + t.into_par_iter() + .fold_with(Ok(()), |ret, i| guard.run(|| for_each(i)).unwrap_or(ret).and(ret)) + .reduce(|| Ok(()), |a, b| a.and(b)) + } else { + t.into_iter() + .fold(Ok(()), |ret, i| guard.run(|| for_each(i)).unwrap_or(ret).and(ret)) + } + }) + } + pub fn par_map< I, T: IntoIterator<Item = I> + IntoParallelIterator<Item = I>, diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index ac133f8b039..ed19274a7cc 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -99,7 +99,7 @@ declare_features! ( /// Allows generators to be cloned. (removed, generator_clone, "1.65.0", Some(95360), None, Some("renamed to `coroutine_clone`")), /// Allows defining generators. - (removed, generators, "1.21.0", Some(43122), None, Some("renamed to `coroutine`")), + (removed, generators, "1.21.0", Some(43122), None, Some("renamed to `coroutines`")), /// Allows `impl Trait` in bindings (`let`, `const`, `static`). (removed, impl_trait_in_bindings, "1.55.0", Some(63065), None, Some("the implementation was not maintainable, the feature may get reintroduced once the current refactorings are done")), diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 34c28bce5d8..3f31ce7aa58 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -92,7 +92,8 @@ pub(super) fn enter_wf_checking_ctxt<'tcx, F>( span: Span, body_def_id: LocalDefId, f: F, -) where +) -> Result<(), ErrorGuaranteed> +where F: for<'a> FnOnce(&WfCheckingCtxt<'a, 'tcx>), { let param_env = tcx.param_env(body_def_id); @@ -106,40 +107,46 @@ pub(super) fn enter_wf_checking_ctxt<'tcx, F>( } f(&mut wfcx); - let assumed_wf_types = match wfcx.ocx.assumed_wf_types_and_report_errors(param_env, body_def_id) - { - Ok(wf_types) => wf_types, - Err(_guar) => return, - }; + let assumed_wf_types = wfcx.ocx.assumed_wf_types_and_report_errors(param_env, body_def_id)?; let implied_bounds = infcx.implied_bounds_tys(param_env, body_def_id, assumed_wf_types); let errors = wfcx.select_all_or_error(); if !errors.is_empty() { - infcx.err_ctxt().report_fulfillment_errors(errors); - return; + let err = infcx.err_ctxt().report_fulfillment_errors(errors); + if tcx.sess.err_count() > 0 { + return Err(err); + } else { + // HACK(oli-obk): tests/ui/specialization/min_specialization/specialize_on_type_error.rs causes an + // error (delay_span_bug) during normalization, without reporting an error, so we need to act as if + // no error happened, in order to let our callers continue and report an error later in + // check_impl_items_against_trait. + return Ok(()); + } } let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); - let _ = wfcx.ocx.resolve_regions_and_report_errors(body_def_id, &outlives_env); + wfcx.ocx.resolve_regions_and_report_errors(body_def_id, &outlives_env)?; + infcx.tainted_by_errors().error_reported() } -fn check_well_formed(tcx: TyCtxt<'_>, def_id: hir::OwnerId) { +fn check_well_formed(tcx: TyCtxt<'_>, def_id: hir::OwnerId) -> Result<(), ErrorGuaranteed> { let node = tcx.hir().owner(def_id); - match node { - hir::OwnerNode::Crate(_) => {} + let mut res = match node { + hir::OwnerNode::Crate(_) => bug!("check_well_formed cannot be applied to the crate root"), hir::OwnerNode::Item(item) => check_item(tcx, item), hir::OwnerNode::TraitItem(item) => check_trait_item(tcx, item), hir::OwnerNode::ImplItem(item) => check_impl_item(tcx, item), hir::OwnerNode::ForeignItem(item) => check_foreign_item(tcx, item), - } + }; if let Some(generics) = node.generics() { for param in generics.params { - check_param_wf(tcx, param) + res = res.and(check_param_wf(tcx, param)); } } + res } /// Checks that the field types (in a struct def'n) or argument types (in an enum def'n) are @@ -156,7 +163,7 @@ fn check_well_formed(tcx: TyCtxt<'_>, def_id: hir::OwnerId) { /// not included it frequently leads to confusing errors in fn bodies. So it's better to check /// the types first. #[instrument(skip(tcx), level = "debug")] -fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) { +fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) -> Result<(), ErrorGuaranteed> { let def_id = item.owner_id.def_id; debug!( @@ -186,31 +193,32 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) { let is_auto = tcx .impl_trait_ref(def_id) .is_some_and(|trait_ref| tcx.trait_is_auto(trait_ref.skip_binder().def_id)); + let mut res = Ok(()); if let (hir::Defaultness::Default { .. }, true) = (impl_.defaultness, is_auto) { let sp = impl_.of_trait.as_ref().map_or(item.span, |t| t.path.span); let mut err = tcx.sess.struct_span_err(sp, "impls of auto traits cannot be default"); err.span_labels(impl_.defaultness_span, "default because of this"); err.span_label(sp, "auto trait"); - err.emit(); + res = Err(err.emit()); } // We match on both `ty::ImplPolarity` and `ast::ImplPolarity` just to get the `!` span. match (tcx.impl_polarity(def_id), impl_.polarity) { (ty::ImplPolarity::Positive, _) => { - check_impl(tcx, item, impl_.self_ty, &impl_.of_trait); + res = res.and(check_impl(tcx, item, impl_.self_ty, &impl_.of_trait)); } (ty::ImplPolarity::Negative, ast::ImplPolarity::Negative(span)) => { // FIXME(#27579): what amount of WF checking do we need for neg impls? if let hir::Defaultness::Default { .. } = impl_.defaultness { let mut spans = vec![span]; spans.extend(impl_.defaultness_span); - struct_span_err!( + res = Err(struct_span_err!( tcx.sess, spans, E0750, "negative impls cannot be default impls" ) - .emit(); + .emit()); } } (ty::ImplPolarity::Reservation, _) => { @@ -218,49 +226,52 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) { } _ => unreachable!(), } + res } hir::ItemKind::Fn(ref sig, ..) => { - check_item_fn(tcx, def_id, item.ident, item.span, sig.decl); + check_item_fn(tcx, def_id, item.ident, item.span, sig.decl) } hir::ItemKind::Static(ty, ..) => { - check_item_type(tcx, def_id, ty.span, UnsizedHandling::Forbid); + check_item_type(tcx, def_id, ty.span, UnsizedHandling::Forbid) } hir::ItemKind::Const(ty, ..) => { - check_item_type(tcx, def_id, ty.span, UnsizedHandling::Forbid); + check_item_type(tcx, def_id, ty.span, UnsizedHandling::Forbid) } hir::ItemKind::Struct(_, ast_generics) => { - check_type_defn(tcx, item, false); + let res = check_type_defn(tcx, item, false); check_variances_for_type_defn(tcx, item, ast_generics); + res } hir::ItemKind::Union(_, ast_generics) => { - check_type_defn(tcx, item, true); + let res = check_type_defn(tcx, item, true); check_variances_for_type_defn(tcx, item, ast_generics); + res } hir::ItemKind::Enum(_, ast_generics) => { - check_type_defn(tcx, item, true); + let res = check_type_defn(tcx, item, true); check_variances_for_type_defn(tcx, item, ast_generics); + res } - hir::ItemKind::Trait(..) => { - check_trait(tcx, item); - } - hir::ItemKind::TraitAlias(..) => { - check_trait(tcx, item); - } + hir::ItemKind::Trait(..) => check_trait(tcx, item), + hir::ItemKind::TraitAlias(..) => check_trait(tcx, item), // `ForeignItem`s are handled separately. - hir::ItemKind::ForeignMod { .. } => {} + hir::ItemKind::ForeignMod { .. } => Ok(()), hir::ItemKind::TyAlias(hir_ty, ast_generics) => { if tcx.type_alias_is_lazy(item.owner_id) { // Bounds of lazy type aliases and of eager ones that contain opaque types are respected. // E.g: `type X = impl Trait;`, `type X = (impl Trait, Y);`. - check_item_type(tcx, def_id, hir_ty.span, UnsizedHandling::Allow); + let res = check_item_type(tcx, def_id, hir_ty.span, UnsizedHandling::Allow); check_variances_for_type_defn(tcx, item, ast_generics); + res + } else { + Ok(()) } } - _ => {} + _ => Ok(()), } } -fn check_foreign_item(tcx: TyCtxt<'_>, item: &hir::ForeignItem<'_>) { +fn check_foreign_item(tcx: TyCtxt<'_>, item: &hir::ForeignItem<'_>) -> Result<(), ErrorGuaranteed> { let def_id = item.owner_id.def_id; debug!( @@ -275,11 +286,14 @@ fn check_foreign_item(tcx: TyCtxt<'_>, item: &hir::ForeignItem<'_>) { hir::ForeignItemKind::Static(ty, ..) => { check_item_type(tcx, def_id, ty.span, UnsizedHandling::AllowIfForeignTail) } - hir::ForeignItemKind::Type => (), + hir::ForeignItemKind::Type => Ok(()), } } -fn check_trait_item(tcx: TyCtxt<'_>, trait_item: &hir::TraitItem<'_>) { +fn check_trait_item( + tcx: TyCtxt<'_>, + trait_item: &hir::TraitItem<'_>, +) -> Result<(), ErrorGuaranteed> { let def_id = trait_item.owner_id.def_id; let (method_sig, span) = match trait_item.kind { @@ -288,18 +302,19 @@ fn check_trait_item(tcx: TyCtxt<'_>, trait_item: &hir::TraitItem<'_>) { _ => (None, trait_item.span), }; check_object_unsafe_self_trait_by_name(tcx, trait_item); - check_associated_item(tcx, def_id, span, method_sig); + let mut res = check_associated_item(tcx, def_id, span, method_sig); if matches!(trait_item.kind, hir::TraitItemKind::Fn(..)) { for &assoc_ty_def_id in tcx.associated_types_for_impl_traits_in_associated_fn(def_id) { - check_associated_item( + res = res.and(check_associated_item( tcx, assoc_ty_def_id.expect_local(), tcx.def_span(assoc_ty_def_id), None, - ); + )); } } + res } /// Require that the user writes where clauses on GATs for the implicit @@ -826,7 +841,7 @@ fn check_object_unsafe_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem } } -fn check_impl_item(tcx: TyCtxt<'_>, impl_item: &hir::ImplItem<'_>) { +fn check_impl_item(tcx: TyCtxt<'_>, impl_item: &hir::ImplItem<'_>) -> Result<(), ErrorGuaranteed> { let (method_sig, span) = match impl_item.kind { hir::ImplItemKind::Fn(ref sig, _) => (Some(sig), impl_item.span), // Constrain binding and overflow error spans to `<Ty>` in `type foo = <Ty>`. @@ -834,13 +849,13 @@ fn check_impl_item(tcx: TyCtxt<'_>, impl_item: &hir::ImplItem<'_>) { _ => (None, impl_item.span), }; - check_associated_item(tcx, impl_item.owner_id.def_id, span, method_sig); + check_associated_item(tcx, impl_item.owner_id.def_id, span, method_sig) } -fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { +fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(), ErrorGuaranteed> { match param.kind { // We currently only check wf of const params here. - hir::GenericParamKind::Lifetime { .. } | hir::GenericParamKind::Type { .. } => (), + hir::GenericParamKind::Lifetime { .. } | hir::GenericParamKind::Type { .. } => Ok(()), // Const parameters are well formed if their type is structural match. hir::GenericParamKind::Const { ty: hir_ty, default: _ } => { @@ -860,68 +875,66 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { ty, trait_def_id, ); - }); + }) } else { - let diag = match ty.kind() { - ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Error(_) => None, - ty::FnPtr(_) => Some(tcx.sess.struct_span_err( + let mut diag = match ty.kind() { + ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Error(_) => return Ok(()), + ty::FnPtr(_) => tcx.sess.struct_span_err( hir_ty.span, "using function pointers as const generic parameters is forbidden", - )), - ty::RawPtr(_) => Some(tcx.sess.struct_span_err( + ), + ty::RawPtr(_) => tcx.sess.struct_span_err( hir_ty.span, "using raw pointers as const generic parameters is forbidden", - )), - _ => Some(tcx.sess.struct_span_err( + ), + _ => tcx.sess.struct_span_err( hir_ty.span, format!("`{}` is forbidden as the type of a const generic parameter", ty), - )), + ), }; - if let Some(mut diag) = diag { - diag.note("the only supported types are integers, `bool` and `char`"); + diag.note("the only supported types are integers, `bool` and `char`"); - let cause = ObligationCause::misc(hir_ty.span, param.def_id); - let may_suggest_feature = match type_allowed_to_implement_const_param_ty( - tcx, - tcx.param_env(param.def_id), - ty, - cause, - ) { - // Can never implement `ConstParamTy`, don't suggest anything. - Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed) => false, - // May be able to implement `ConstParamTy`. Only emit the feature help - // if the type is local, since the user may be able to fix the local type. - Err(ConstParamTyImplementationError::InfrigingFields(..)) => { - fn ty_is_local(ty: Ty<'_>) -> bool { - match ty.kind() { - ty::Adt(adt_def, ..) => adt_def.did().is_local(), - // Arrays and slices use the inner type's `ConstParamTy`. - ty::Array(ty, ..) => ty_is_local(*ty), - ty::Slice(ty) => ty_is_local(*ty), - // `&` references use the inner type's `ConstParamTy`. - // `&mut` are not supported. - ty::Ref(_, ty, ast::Mutability::Not) => ty_is_local(*ty), - // Say that a tuple is local if any of its components are local. - // This is not strictly correct, but it's likely that the user can fix the local component. - ty::Tuple(tys) => tys.iter().any(|ty| ty_is_local(ty)), - _ => false, - } + let cause = ObligationCause::misc(hir_ty.span, param.def_id); + let may_suggest_feature = match type_allowed_to_implement_const_param_ty( + tcx, + tcx.param_env(param.def_id), + ty, + cause, + ) { + // Can never implement `ConstParamTy`, don't suggest anything. + Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed) => false, + // May be able to implement `ConstParamTy`. Only emit the feature help + // if the type is local, since the user may be able to fix the local type. + Err(ConstParamTyImplementationError::InfrigingFields(..)) => { + fn ty_is_local(ty: Ty<'_>) -> bool { + match ty.kind() { + ty::Adt(adt_def, ..) => adt_def.did().is_local(), + // Arrays and slices use the inner type's `ConstParamTy`. + ty::Array(ty, ..) => ty_is_local(*ty), + ty::Slice(ty) => ty_is_local(*ty), + // `&` references use the inner type's `ConstParamTy`. + // `&mut` are not supported. + ty::Ref(_, ty, ast::Mutability::Not) => ty_is_local(*ty), + // Say that a tuple is local if any of its components are local. + // This is not strictly correct, but it's likely that the user can fix the local component. + ty::Tuple(tys) => tys.iter().any(|ty| ty_is_local(ty)), + _ => false, } - - ty_is_local(ty) } - // Implments `ConstParamTy`, suggest adding the feature to enable. - Ok(..) => true, - }; - if may_suggest_feature && tcx.sess.is_nightly_build() { - diag.help( + + ty_is_local(ty) + } + // Implments `ConstParamTy`, suggest adding the feature to enable. + Ok(..) => true, + }; + if may_suggest_feature && tcx.sess.is_nightly_build() { + diag.help( "add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types", ); - } - - diag.emit(); } + + Err(diag.emit()) } } } @@ -933,7 +946,7 @@ fn check_associated_item( item_id: LocalDefId, span: Span, sig_if_method: Option<&hir::FnSig<'_>>, -) { +) -> Result<(), ErrorGuaranteed> { let loc = Some(WellFormedLoc::Ty(item_id)); enter_wf_checking_ctxt(tcx, span, item_id, |wfcx| { let item = tcx.associated_item(item_id); @@ -985,7 +998,11 @@ fn item_adt_kind(kind: &ItemKind<'_>) -> Option<AdtKind> { } /// In a type definition, we check that to ensure that the types of the fields are well-formed. -fn check_type_defn<'tcx>(tcx: TyCtxt<'tcx>, item: &hir::Item<'tcx>, all_sized: bool) { +fn check_type_defn<'tcx>( + tcx: TyCtxt<'tcx>, + item: &hir::Item<'tcx>, + all_sized: bool, +) -> Result<(), ErrorGuaranteed> { let _ = tcx.representability(item.owner_id.def_id); let adt_def = tcx.adt_def(item.owner_id); @@ -1080,11 +1097,11 @@ fn check_type_defn<'tcx>(tcx: TyCtxt<'tcx>, item: &hir::Item<'tcx>, all_sized: b } check_where_clauses(wfcx, item.span, item.owner_id.def_id); - }); + }) } #[instrument(skip(tcx, item))] -fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) { +fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) -> Result<(), ErrorGuaranteed> { debug!(?item.owner_id); let def_id = item.owner_id.def_id; @@ -1103,7 +1120,7 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) { } } - enter_wf_checking_ctxt(tcx, item.span, def_id, |wfcx| { + let res = enter_wf_checking_ctxt(tcx, item.span, def_id, |wfcx| { check_where_clauses(wfcx, item.span, def_id) }); @@ -1111,6 +1128,7 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) { if let hir::ItemKind::Trait(..) = item.kind { check_gat_where_clauses(tcx, item.owner_id.def_id); } + res } /// Checks all associated type defaults of trait `trait_def_id`. @@ -1142,7 +1160,7 @@ fn check_item_fn( ident: Ident, span: Span, decl: &hir::FnDecl<'_>, -) { +) -> Result<(), ErrorGuaranteed> { enter_wf_checking_ctxt(tcx, span, def_id, |wfcx| { let sig = tcx.fn_sig(def_id).instantiate_identity(); check_fn_or_method(wfcx, ident.span, sig, decl, def_id); @@ -1160,7 +1178,7 @@ fn check_item_type( item_id: LocalDefId, ty_span: Span, unsized_handling: UnsizedHandling, -) { +) -> Result<(), ErrorGuaranteed> { debug!("check_item_type: {:?}", item_id); enter_wf_checking_ctxt(tcx, ty_span, item_id, |wfcx| { @@ -1200,7 +1218,7 @@ fn check_item_type( tcx.require_lang_item(LangItem::Sync, Some(ty_span)), ); } - }); + }) } #[instrument(level = "debug", skip(tcx, ast_self_ty, ast_trait_ref))] @@ -1209,7 +1227,7 @@ fn check_impl<'tcx>( item: &'tcx hir::Item<'tcx>, ast_self_ty: &hir::Ty<'_>, ast_trait_ref: &Option<hir::TraitRef<'_>>, -) { +) -> Result<(), ErrorGuaranteed> { enter_wf_checking_ctxt(tcx, item.span, item.owner_id.def_id, |wfcx| { match ast_trait_ref { Some(ast_trait_ref) => { @@ -1258,7 +1276,7 @@ fn check_impl<'tcx>( } check_where_clauses(wfcx, item.span, item.owner_id.def_id); - }); + }) } /// Checks where-clauses and inline bounds that are declared on `def_id`. @@ -1879,12 +1897,12 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { } } -fn check_mod_type_wf(tcx: TyCtxt<'_>, module: LocalModDefId) { +fn check_mod_type_wf(tcx: TyCtxt<'_>, module: LocalModDefId) -> Result<(), ErrorGuaranteed> { let items = tcx.hir_module_items(module); - items.par_items(|item| tcx.ensure().check_well_formed(item.owner_id)); - items.par_impl_items(|item| tcx.ensure().check_well_formed(item.owner_id)); - items.par_trait_items(|item| tcx.ensure().check_well_formed(item.owner_id)); - items.par_foreign_items(|item| tcx.ensure().check_well_formed(item.owner_id)); + let mut res = items.par_items(|item| tcx.ensure().check_well_formed(item.owner_id)); + res = res.and(items.par_impl_items(|item| tcx.ensure().check_well_formed(item.owner_id))); + res = res.and(items.par_trait_items(|item| tcx.ensure().check_well_formed(item.owner_id))); + res.and(items.par_foreign_items(|item| tcx.ensure().check_well_formed(item.owner_id))) } fn error_392( diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index c7b3648099c..88f3db03a4e 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -205,10 +205,8 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> { })?; } - tcx.sess.track_errors(|| { - tcx.sess.time("wf_checking", || { - tcx.hir().par_for_each_module(|module| tcx.ensure().check_mod_type_wf(module)) - }); + tcx.sess.time("wf_checking", || { + tcx.hir().try_par_for_each_module(|module| tcx.ensure().check_mod_type_wf(module)) })?; // NOTE: This is copy/pasted in librustdoc/core.rs and should be kept in sync. diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 7677d6f953b..e93d180fc13 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -4,9 +4,7 @@ mod arg_matrix; mod checks; mod suggestions; -pub use _impl::*; use rustc_errors::ErrorGuaranteed; -pub use suggestions::*; use crate::coercion::DynamicCoerceMany; use crate::{Diverges, EnclosingBreakables, Inherited}; diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 110ec052b35..e0ccf04ab63 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -12,6 +12,7 @@ use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::{HirId, Pat, PatKind}; use rustc_infer::infer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::{self, Adt, BindingMode, Ty, TypeVisitableExt}; use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_span::edit_distance::find_best_match_for_name; @@ -2164,7 +2165,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { len: ty::Const<'tcx>, min_len: u64, ) -> (Option<Ty<'tcx>>, Ty<'tcx>) { - let guar = if let Some(len) = len.try_eval_target_usize(self.tcx, self.param_env) { + let len = match len.eval(self.tcx, self.param_env, None) { + Ok(val) => val + .try_to_scalar() + .and_then(|scalar| scalar.try_to_int().ok()) + .and_then(|int| int.try_to_target_usize(self.tcx).ok()), + Err(ErrorHandled::Reported(..)) => { + let guar = self.error_scrutinee_unfixed_length(span); + return (Some(Ty::new_error(self.tcx, guar)), arr_ty); + } + Err(ErrorHandled::TooGeneric(..)) => None, + }; + + let guar = if let Some(len) = len { // Now we know the length... if slice.is_none() { // ...and since there is no variable-length pattern, diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 97ab3d0b751..ba118f8110f 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -588,60 +588,60 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { self.tcx } - fn print_region(self, _region: ty::Region<'_>) -> Result<Self, PrintError> { + fn print_region(&mut self, _region: ty::Region<'_>) -> Result<(), PrintError> { Err(fmt::Error) } - fn print_type(self, _ty: Ty<'tcx>) -> Result<Self, PrintError> { + fn print_type(&mut self, _ty: Ty<'tcx>) -> Result<(), PrintError> { Err(fmt::Error) } fn print_dyn_existential( - self, + &mut self, _predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { Err(fmt::Error) } - fn print_const(self, _ct: ty::Const<'tcx>) -> Result<Self, PrintError> { + fn print_const(&mut self, _ct: ty::Const<'tcx>) -> Result<(), PrintError> { Err(fmt::Error) } - fn path_crate(mut self, cnum: CrateNum) -> Result<Self, PrintError> { + fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> { self.segments = vec![self.tcx.crate_name(cnum).to_string()]; - Ok(self) + Ok(()) } fn path_qualified( - self, + &mut self, _self_ty: Ty<'tcx>, _trait_ref: Option<ty::TraitRef<'tcx>>, - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { Err(fmt::Error) } fn path_append_impl( - self, - _print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>, + &mut self, + _print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, _disambiguated_data: &DisambiguatedDefPathData, _self_ty: Ty<'tcx>, _trait_ref: Option<ty::TraitRef<'tcx>>, - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { Err(fmt::Error) } fn path_append( - mut self, - print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>, + &mut self, + print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, disambiguated_data: &DisambiguatedDefPathData, - ) -> Result<Self, PrintError> { - self = print_prefix(self)?; + ) -> Result<(), PrintError> { + print_prefix(self)?; self.segments.push(disambiguated_data.to_string()); - Ok(self) + Ok(()) } fn path_generic_args( - self, - print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>, + &mut self, + print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, _args: &[GenericArg<'tcx>], - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { print_prefix(self) } } @@ -652,9 +652,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // let _ = [{struct Foo; Foo}, {struct Foo; Foo}]; if did1.krate != did2.krate { let abs_path = |def_id| { - AbsolutePathPrinter { tcx: self.tcx, segments: vec![] } - .print_def_path(def_id, &[]) - .map(|p| p.segments) + let mut printer = AbsolutePathPrinter { tcx: self.tcx, segments: vec![] }; + printer.print_def_path(def_id, &[]).map(|_| printer.segments) }; // We compare strings because DefPath can be different @@ -1071,7 +1070,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let get_lifetimes = |sig| { use rustc_hir::def::Namespace; - let (_, sig, reg) = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS) + let (sig, reg) = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS) .name_all_regions(sig) .unwrap(); let lts: Vec<String> = reg.into_values().map(|kind| kind.to_string()).collect(); diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index 7bc414ff522..4beb51da72c 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -200,12 +200,15 @@ fn ty_to_string<'tcx>( ty: Ty<'tcx>, called_method_def_id: Option<DefId>, ) -> String { - let printer = fmt_printer(infcx, Namespace::TypeNS); + let mut printer = fmt_printer(infcx, Namespace::TypeNS); let ty = infcx.resolve_vars_if_possible(ty); match (ty.kind(), called_method_def_id) { // We don't want the regular output for `fn`s because it includes its path in // invalid pseudo-syntax, we want the `fn`-pointer output instead. - (ty::FnDef(..), _) => ty.fn_sig(infcx.tcx).print(printer).unwrap().into_buffer(), + (ty::FnDef(..), _) => { + ty.fn_sig(infcx.tcx).print(&mut printer).unwrap(); + printer.into_buffer() + } (_, Some(def_id)) if ty.is_ty_or_numeric_infer() && infcx.tcx.get_diagnostic_item(sym::iterator_collect_fn) == Some(def_id) => @@ -218,7 +221,10 @@ fn ty_to_string<'tcx>( // // We do have to hide the `extern "rust-call"` ABI in that case though, // which is too much of a bother for now. - _ => ty.print(printer).unwrap().into_buffer(), + _ => { + ty.print(&mut printer).unwrap(); + printer.into_buffer() + } } } @@ -285,8 +291,9 @@ impl<'tcx> InferCtxt<'tcx> { if let Some(highlight) = highlight { printer.region_highlight_mode = highlight; } + ty.print(&mut printer).unwrap(); InferenceDiagnosticsData { - name: ty.print(printer).unwrap().into_buffer(), + name: printer.into_buffer(), span: None, kind: UnderspecifiedArgKind::Type { prefix: ty.prefix_string(self.tcx) }, parent: None, @@ -312,8 +319,9 @@ impl<'tcx> InferCtxt<'tcx> { if let Some(highlight) = highlight { printer.region_highlight_mode = highlight; } + ct.print(&mut printer).unwrap(); InferenceDiagnosticsData { - name: ct.print(printer).unwrap().into_buffer(), + name: printer.into_buffer(), span: Some(origin.span), kind: UnderspecifiedArgKind::Const { is_parameter: false }, parent: None, @@ -329,8 +337,9 @@ impl<'tcx> InferCtxt<'tcx> { if let Some(highlight) = highlight { printer.region_highlight_mode = highlight; } + ct.print(&mut printer).unwrap(); InferenceDiagnosticsData { - name: ct.print(printer).unwrap().into_buffer(), + name: printer.into_buffer(), span: None, kind: UnderspecifiedArgKind::Const { is_parameter: false }, parent: None, @@ -487,7 +496,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { { "Vec<_>".to_string() } else { - fmt_printer(self, Namespace::TypeNS) + let mut printer = fmt_printer(self, Namespace::TypeNS); + printer .comma_sep(generic_args.iter().copied().map(|arg| { if arg.is_suggestable(self.tcx, true) { return arg; @@ -512,8 +522,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { .into(), } })) - .unwrap() - .into_buffer() + .unwrap(); + printer.into_buffer() }; if !have_turbofish { @@ -525,8 +535,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } InferSourceKind::FullyQualifiedMethodCall { receiver, successor, args, def_id } => { - let printer = fmt_printer(self, Namespace::ValueNS); - let def_path = printer.print_def_path(def_id, args).unwrap().into_buffer(); + let mut printer = fmt_printer(self, Namespace::ValueNS); + printer.print_def_path(def_id, args).unwrap(); + let def_path = printer.into_buffer(); // We only care about whether we have to add `&` or `&mut ` for now. // This is the case if the last adjustment is a borrow and the diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs index d6a3bc32cc9..c38e5b8cd09 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs @@ -49,8 +49,8 @@ where let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS); printer.region_highlight_mode = self.highlight; - let s = self.value.print(printer)?.into_buffer(); - f.write_str(&s) + self.value.print(&mut printer)?; + f.write_str(&printer.into_buffer()) } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs index faea8bc98aa..00289f9bfb4 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs @@ -763,9 +763,9 @@ fn foo(&self) -> Self::T { String::new() } } pub fn format_generic_args(&self, args: &[ty::GenericArg<'tcx>]) -> String { - FmtPrinter::new(self.tcx, hir::def::Namespace::TypeNS) - .path_generic_args(Ok, args) - .expect("could not write to `String`.") - .into_buffer() + FmtPrinter::print_string(self.tcx, hir::def::Namespace::TypeNS, |cx| { + cx.path_generic_args(|_| Ok(()), args) + }) + .expect("could not write to `String`.") } } diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs index a5b2ccce85e..a26e676c521 100644 --- a/compiler/rustc_infer/src/traits/mod.rs +++ b/compiler/rustc_infer/src/traits/mod.rs @@ -19,7 +19,6 @@ use rustc_span::Span; pub use self::FulfillmentErrorCode::*; pub use self::ImplSource::*; -pub use self::ObligationCauseCode::*; pub use self::SelectionError::*; pub use self::engine::{TraitEngine, TraitEngineExt}; diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 1f08db30860..5b7ba03d9ad 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -1210,35 +1210,35 @@ impl<'tcx> LateContext<'tcx> { self.tcx } - fn print_region(self, _region: ty::Region<'_>) -> Result<Self, PrintError> { - Ok(self) + fn print_region(&mut self, _region: ty::Region<'_>) -> Result<(), PrintError> { + Ok(()) } - fn print_type(self, _ty: Ty<'tcx>) -> Result<Self, PrintError> { - Ok(self) + fn print_type(&mut self, _ty: Ty<'tcx>) -> Result<(), PrintError> { + Ok(()) } fn print_dyn_existential( - self, + &mut self, _predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, - ) -> Result<Self, PrintError> { - Ok(self) + ) -> Result<(), PrintError> { + Ok(()) } - fn print_const(self, _ct: ty::Const<'tcx>) -> Result<Self, PrintError> { - Ok(self) + fn print_const(&mut self, _ct: ty::Const<'tcx>) -> Result<(), PrintError> { + Ok(()) } - fn path_crate(mut self, cnum: CrateNum) -> Result<Self, PrintError> { + fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> { self.path = vec![self.tcx.crate_name(cnum)]; - Ok(self) + Ok(()) } fn path_qualified( - mut self, + &mut self, self_ty: Ty<'tcx>, trait_ref: Option<ty::TraitRef<'tcx>>, - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { if trait_ref.is_none() { if let ty::Adt(def, args) = self_ty.kind() { return self.print_def_path(def.did(), args); @@ -1251,21 +1251,21 @@ impl<'tcx> LateContext<'tcx> { Some(trait_ref) => Symbol::intern(&format!("{trait_ref:?}")), None => Symbol::intern(&format!("<{self_ty}>")), }]; - Ok(self) + Ok(()) }) } fn path_append_impl( - self, - print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>, + &mut self, + print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, _disambiguated_data: &DisambiguatedDefPathData, self_ty: Ty<'tcx>, trait_ref: Option<ty::TraitRef<'tcx>>, - ) -> Result<Self, PrintError> { - let mut path = print_prefix(self)?; + ) -> Result<(), PrintError> { + print_prefix(self)?; // This shouldn't ever be needed, but just in case: - path.path.push(match trait_ref { + self.path.push(match trait_ref { Some(trait_ref) => { with_no_trimmed_paths!(Symbol::intern(&format!( "<impl {} for {}>", @@ -1278,38 +1278,37 @@ impl<'tcx> LateContext<'tcx> { } }); - Ok(path) + Ok(()) } fn path_append( - self, - print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>, + &mut self, + print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, disambiguated_data: &DisambiguatedDefPathData, - ) -> Result<Self, PrintError> { - let mut path = print_prefix(self)?; + ) -> Result<(), PrintError> { + print_prefix(self)?; // Skip `::{{extern}}` blocks and `::{{constructor}}` on tuple/unit structs. if let DefPathData::ForeignMod | DefPathData::Ctor = disambiguated_data.data { - return Ok(path); + return Ok(()); } - path.path.push(Symbol::intern(&disambiguated_data.data.to_string())); - Ok(path) + self.path.push(Symbol::intern(&disambiguated_data.data.to_string())); + Ok(()) } fn path_generic_args( - self, - print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>, + &mut self, + print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, _args: &[GenericArg<'tcx>], - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { print_prefix(self) } } - AbsolutePathPrinter { tcx: self.tcx, path: vec![] } - .print_def_path(def_id, &[]) - .unwrap() - .path + let mut printer = AbsolutePathPrinter { tcx: self.tcx, path: vec![] }; + printer.print_def_path(def_id, &[]).unwrap(); + printer.path } /// Returns the associated type `name` for `self_ty` as an implementation of `trait_id`. diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index d0d41c614d6..d8a695b131b 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -114,6 +114,11 @@ struct QueryModifiers { /// Generate a `feed` method to set the query's value from another query. feedable: Option<Ident>, + + /// Forward the result on ensure if the query gets recomputed, and + /// return `Ok(())` otherwise. Only applicable to queries returning + /// `Result<(), ErrorGuaranteed>` + ensure_forwards_result_if_red: Option<Ident>, } fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> { @@ -128,6 +133,7 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> { let mut depth_limit = None; let mut separate_provide_extern = None; let mut feedable = None; + let mut ensure_forwards_result_if_red = None; while !input.is_empty() { let modifier: Ident = input.parse()?; @@ -187,6 +193,8 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> { try_insert!(separate_provide_extern = modifier); } else if modifier == "feedable" { try_insert!(feedable = modifier); + } else if modifier == "ensure_forwards_result_if_red" { + try_insert!(ensure_forwards_result_if_red = modifier); } else { return Err(Error::new(modifier.span(), "unknown query modifier")); } @@ -206,6 +214,7 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> { depth_limit, separate_provide_extern, feedable, + ensure_forwards_result_if_red, }) } @@ -325,6 +334,7 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { eval_always, depth_limit, separate_provide_extern, + ensure_forwards_result_if_red, ); if modifiers.cache.is_some() { diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 3ca26ec98c6..58c0c6bab49 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -6,7 +6,7 @@ use rustc_ast as ast; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::svh::Svh; -use rustc_data_structures::sync::{par_for_each_in, DynSend, DynSync}; +use rustc_data_structures::sync::{par_for_each_in, try_par_for_each_in, DynSend, DynSync}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId, LOCAL_CRATE}; use rustc_hir::definitions::{DefKey, DefPath, DefPathData, DefPathHash}; @@ -16,7 +16,7 @@ use rustc_index::Idx; use rustc_middle::hir::nested_filter; use rustc_span::def_id::StableCrateId; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::Span; +use rustc_span::{ErrorGuaranteed, Span}; use rustc_target::spec::abi::Abi; #[inline] @@ -632,6 +632,17 @@ impl<'hir> Map<'hir> { }) } + #[inline] + pub fn try_par_for_each_module( + self, + f: impl Fn(LocalModDefId) -> Result<(), ErrorGuaranteed> + DynSend + DynSync, + ) -> Result<(), ErrorGuaranteed> { + let crate_items = self.tcx.hir_crate_items(()); + try_par_for_each_in(&crate_items.submodules[..], |module| { + f(LocalModDefId::new_unchecked(module.def_id)) + }) + } + /// Returns an iterator for the nodes in the ancestor tree of the `current_id` /// until the crate root is reached. Prefer this over your own loop using `parent_id`. #[inline] diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 0da8fe9cca7..f28ec771169 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -9,12 +9,12 @@ pub mod place; use crate::query::Providers; use crate::ty::{EarlyBinder, ImplSubject, TyCtxt}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_data_structures::sync::{par_for_each_in, DynSend, DynSync}; +use rustc_data_structures::sync::{try_par_for_each_in, DynSend, DynSync}; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::*; use rustc_query_system::ich::StableHashingContext; -use rustc_span::{ExpnId, DUMMY_SP}; +use rustc_span::{ErrorGuaranteed, ExpnId, DUMMY_SP}; /// Top-level HIR node for current owner. This only contains the node for which /// `HirId::local_id == 0`, and excludes bodies. @@ -78,20 +78,32 @@ impl ModuleItems { self.owners().map(|id| id.def_id) } - pub fn par_items(&self, f: impl Fn(ItemId) + DynSend + DynSync) { - par_for_each_in(&self.items[..], |&id| f(id)) + pub fn par_items( + &self, + f: impl Fn(ItemId) -> Result<(), ErrorGuaranteed> + DynSend + DynSync, + ) -> Result<(), ErrorGuaranteed> { + try_par_for_each_in(&self.items[..], |&id| f(id)) } - pub fn par_trait_items(&self, f: impl Fn(TraitItemId) + DynSend + DynSync) { - par_for_each_in(&self.trait_items[..], |&id| f(id)) + pub fn par_trait_items( + &self, + f: impl Fn(TraitItemId) -> Result<(), ErrorGuaranteed> + DynSend + DynSync, + ) -> Result<(), ErrorGuaranteed> { + try_par_for_each_in(&self.trait_items[..], |&id| f(id)) } - pub fn par_impl_items(&self, f: impl Fn(ImplItemId) + DynSend + DynSync) { - par_for_each_in(&self.impl_items[..], |&id| f(id)) + pub fn par_impl_items( + &self, + f: impl Fn(ImplItemId) -> Result<(), ErrorGuaranteed> + DynSend + DynSync, + ) -> Result<(), ErrorGuaranteed> { + try_par_for_each_in(&self.impl_items[..], |&id| f(id)) } - pub fn par_foreign_items(&self, f: impl Fn(ForeignItemId) + DynSend + DynSync) { - par_for_each_in(&self.foreign_items[..], |&id| f(id)) + pub fn par_foreign_items( + &self, + f: impl Fn(ForeignItemId) -> Result<(), ErrorGuaranteed> + DynSend + DynSync, + ) -> Result<(), ErrorGuaranteed> { + try_par_for_each_in(&self.foreign_items[..], |&id| f(id)) } } diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index dee25df53bd..12057f5e1cb 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -998,9 +998,9 @@ impl<'tcx> Debug for Rvalue<'tcx> { ty::tls::with(|tcx| { let variant_def = &tcx.adt_def(adt_did).variant(variant); let args = tcx.lift(args).expect("could not lift for printing"); - let name = FmtPrinter::new(tcx, Namespace::ValueNS) - .print_def_path(variant_def.def_id, args)? - .into_buffer(); + let name = FmtPrinter::print_string(tcx, Namespace::ValueNS, |cx| { + cx.print_def_path(variant_def.def_id, args) + })?; match variant_def.ctor_kind() { Some(CtorKind::Const) => fmt.write_str(&name), @@ -1740,7 +1740,7 @@ fn pretty_print_const_value_tcx<'tcx>( let args = tcx.lift(args).unwrap(); let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS); cx.print_alloc_ids = true; - let cx = cx.print_value_path(variant_def.def_id, args)?; + cx.print_value_path(variant_def.def_id, args)?; fmt.write_str(&cx.into_buffer())?; match variant_def.ctor_kind() { @@ -1775,14 +1775,14 @@ fn pretty_print_const_value_tcx<'tcx>( let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS); cx.print_alloc_ids = true; let ty = tcx.lift(ty).unwrap(); - cx = cx.pretty_print_const_scalar(scalar, ty)?; + cx.pretty_print_const_scalar(scalar, ty)?; fmt.write_str(&cx.into_buffer())?; return Ok(()); } (ConstValue::ZeroSized, ty::FnDef(d, s)) => { let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS); cx.print_alloc_ids = true; - let cx = cx.print_value_path(*d, s)?; + cx.print_value_path(*d, s)?; fmt.write_str(&cx.into_buffer())?; return Ok(()); } diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index e3d346c0698..affa83fa348 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -3,12 +3,10 @@ use rustc_hir::LangItem; use smallvec::SmallVec; use super::{BasicBlock, InlineAsmOperand, Operand, SourceInfo, TerminatorKind, UnwindAction}; -pub use rustc_ast::Mutability; use rustc_macros::HashStable; use std::iter; use std::slice; -pub use super::query::*; use super::*; impl SwitchTargets { @@ -28,6 +26,15 @@ impl SwitchTargets { Self { values: smallvec![value], targets: smallvec![then, else_] } } + /// Inverse of `SwitchTargets::static_if`. + pub fn as_static_if(&self) -> Option<(u128, BasicBlock, BasicBlock)> { + if let &[value] = &self.values[..] && let &[then, else_] = &self.targets[..] { + Some((value, then, else_)) + } else { + None + } + } + /// Returns the fallback target that is jumped to when none of the values match the operand. pub fn otherwise(&self) -> BasicBlock { *self.targets.last().unwrap() diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index cd5206a837f..23d77a1ebe4 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -25,7 +25,9 @@ use crate::mir::interpret::{ use crate::mir::interpret::{LitToConstError, LitToConstInput}; use crate::mir::mono::CodegenUnit; use crate::query::erase::{erase, restore, Erase}; -use crate::query::plumbing::{query_ensure, query_get_at, CyclePlaceholder, DynamicQuery}; +use crate::query::plumbing::{ + query_ensure, query_ensure_error_guaranteed, query_get_at, CyclePlaceholder, DynamicQuery, +}; use crate::thir; use crate::traits::query::{ CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal, @@ -965,8 +967,9 @@ rustc_queries! { desc { |tcx| "checking that impls are well-formed in {}", describe_as_module(key, tcx) } } - query check_mod_type_wf(key: LocalModDefId) -> () { + query check_mod_type_wf(key: LocalModDefId) -> Result<(), ErrorGuaranteed> { desc { |tcx| "checking that types are well-formed in {}", describe_as_module(key, tcx) } + ensure_forwards_result_if_red } query collect_mod_item_types(key: LocalModDefId) -> () { @@ -1499,8 +1502,9 @@ rustc_queries! { feedable } - query check_well_formed(key: hir::OwnerId) -> () { + query check_well_formed(key: hir::OwnerId) -> Result<(), ErrorGuaranteed> { desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key) } + ensure_forwards_result_if_red } // The `DefId`s of all non-generic functions and statics in the given crate diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 34e5b02ba5b..f4a8ada8f68 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -173,6 +173,45 @@ pub fn query_ensure<'tcx, Cache>( } } +#[inline] +pub fn query_ensure_error_guaranteed<'tcx, Cache>( + tcx: TyCtxt<'tcx>, + execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option<Cache::Value>, + query_cache: &Cache, + key: Cache::Key, + check_cache: bool, +) -> Result<(), ErrorGuaranteed> +where + Cache: QueryCache<Value = super::erase::Erase<Result<(), ErrorGuaranteed>>>, +{ + let key = key.into_query_param(); + if let Some(res) = try_get_cached(tcx, query_cache, &key) { + super::erase::restore(res) + } else { + execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache }) + .map(super::erase::restore) + // Either we actually executed the query, which means we got a full `Result`, + // or we can just assume the query succeeded, because it was green in the + // incremental cache. If it is green, that means that the previous compilation + // that wrote to the incremental cache compiles successfully. That is only + // possible if the cache entry was `Ok(())`, so we emit that here, without + // actually encoding the `Result` in the cache or loading it from there. + .unwrap_or(Ok(())) + } +} + +macro_rules! query_ensure { + ([]$($args:tt)*) => { + query_ensure($($args)*) + }; + ([(ensure_forwards_result_if_red) $($rest:tt)*]$($args:tt)*) => { + query_ensure_error_guaranteed($($args)*) + }; + ([$other:tt $($modifiers:tt)*]$($args:tt)*) => { + query_ensure!([$($modifiers)*]$($args)*) + }; +} + macro_rules! query_helper_param_ty { (DefId) => { impl IntoQueryParam<DefId> }; (LocalDefId) => { impl IntoQueryParam<LocalDefId> }; @@ -220,6 +259,18 @@ macro_rules! separate_provide_extern_decl { }; } +macro_rules! ensure_result { + ([][$ty:ty]) => { + () + }; + ([(ensure_forwards_result_if_red) $($rest:tt)*][$ty:ty]) => { + Result<(), ErrorGuaranteed> + }; + ([$other:tt $($modifiers:tt)*][$($args:tt)*]) => { + ensure_result!([$($modifiers)*][$($args)*]) + }; +} + macro_rules! separate_provide_extern_default { ([][$name:ident]) => { () @@ -343,14 +394,15 @@ macro_rules! define_callbacks { impl<'tcx> TyCtxtEnsure<'tcx> { $($(#[$attr])* #[inline(always)] - pub fn $name(self, key: query_helper_param_ty!($($K)*)) { - query_ensure( + pub fn $name(self, key: query_helper_param_ty!($($K)*)) -> ensure_result!([$($modifiers)*][$V]) { + query_ensure!( + [$($modifiers)*] self.tcx, self.tcx.query_system.fns.engine.$name, &self.tcx.query_system.caches.$name, key.into_query_param(), false, - ); + ) })* } diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index f77a8c6712e..94a5ff13158 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -1,5 +1,3 @@ -pub use self::AssocItemContainer::*; - use crate::ty; use rustc_data_structures::sorted_map::SortedIndexMultiMap; use rustc_hir as hir; diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 8b4375f0270..184a70ed4cb 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -315,26 +315,25 @@ impl<'tcx> Ty<'tcx> { impl<'tcx> TyCtxt<'tcx> { pub fn ty_string_with_limit(self, ty: Ty<'tcx>, length_limit: usize) -> String { let mut type_limit = 50; - let regular = FmtPrinter::new(self, hir::def::Namespace::TypeNS) - .pretty_print_type(ty) - .expect("could not write to `String`") - .into_buffer(); + let regular = FmtPrinter::print_string(self, hir::def::Namespace::TypeNS, |cx| { + cx.pretty_print_type(ty) + }) + .expect("could not write to `String`"); if regular.len() <= length_limit { return regular; } let mut short; loop { // Look for the longest properly trimmed path that still fits in length_limit. - short = with_forced_trimmed_paths!( - FmtPrinter::new_with_limit( + short = with_forced_trimmed_paths!({ + let mut cx = FmtPrinter::new_with_limit( self, hir::def::Namespace::TypeNS, rustc_session::Limit(type_limit), - ) - .pretty_print_type(ty) - .expect("could not write to `String`") - .into_buffer() - ); + ); + cx.pretty_print_type(ty).expect("could not write to `String`"); + cx.into_buffer() + }); if short.len() <= length_limit || type_limit == 0 { break; } @@ -344,10 +343,10 @@ impl<'tcx> TyCtxt<'tcx> { } pub fn short_ty_string(self, ty: Ty<'tcx>) -> (String, Option<PathBuf>) { - let regular = FmtPrinter::new(self, hir::def::Namespace::TypeNS) - .pretty_print_type(ty) - .expect("could not write to `String`") - .into_buffer(); + let regular = FmtPrinter::print_string(self, hir::def::Namespace::TypeNS, |cx| { + cx.pretty_print_type(ty) + }) + .expect("could not write to `String`"); if !self.sess.opts.unstable_opts.write_long_types_to_disk { return (regular, None); diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index a7d6e97c941..cebefbccc11 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -298,9 +298,9 @@ fn fmt_instance( ty::tls::with(|tcx| { let args = tcx.lift(instance.args).expect("could not lift for printing"); - let s = FmtPrinter::new_with_limit(tcx, Namespace::ValueNS, type_length) - .print_def_path(instance.def_id(), args)? - .into_buffer(); + let mut cx = FmtPrinter::new_with_limit(tcx, Namespace::ValueNS, type_length); + cx.print_def_path(instance.def_id(), args)?; + let s = cx.into_buffer(); f.write_str(&s) })?; diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index 164e4232e4c..80af8a92553 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -15,7 +15,7 @@ pub type PrintError = std::fmt::Error; // FIXME(eddyb) false positive, the lifetime parameters are used with `P: Printer<...>`. #[allow(unused_lifetimes)] pub trait Print<'tcx, P> { - fn print(&self, cx: P) -> Result<P, PrintError>; + fn print(&self, cx: &mut P) -> Result<(), PrintError>; } /// Interface for outputting user-facing "type-system entities" @@ -31,70 +31,70 @@ pub trait Printer<'tcx>: Sized { fn tcx<'a>(&'a self) -> TyCtxt<'tcx>; fn print_def_path( - self, + &mut self, def_id: DefId, args: &'tcx [GenericArg<'tcx>], - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { self.default_print_def_path(def_id, args) } fn print_impl_path( - self, + &mut self, impl_def_id: DefId, args: &'tcx [GenericArg<'tcx>], self_ty: Ty<'tcx>, trait_ref: Option<ty::TraitRef<'tcx>>, - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { self.default_print_impl_path(impl_def_id, args, self_ty, trait_ref) } - fn print_region(self, region: ty::Region<'tcx>) -> Result<Self, PrintError>; + fn print_region(&mut self, region: ty::Region<'tcx>) -> Result<(), PrintError>; - fn print_type(self, ty: Ty<'tcx>) -> Result<Self, PrintError>; + fn print_type(&mut self, ty: Ty<'tcx>) -> Result<(), PrintError>; fn print_dyn_existential( - self, + &mut self, predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, - ) -> Result<Self, PrintError>; + ) -> Result<(), PrintError>; - fn print_const(self, ct: ty::Const<'tcx>) -> Result<Self, PrintError>; + fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError>; - fn path_crate(self, cnum: CrateNum) -> Result<Self, PrintError>; + fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError>; fn path_qualified( - self, + &mut self, self_ty: Ty<'tcx>, trait_ref: Option<ty::TraitRef<'tcx>>, - ) -> Result<Self, PrintError>; + ) -> Result<(), PrintError>; fn path_append_impl( - self, - print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>, + &mut self, + print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, disambiguated_data: &DisambiguatedDefPathData, self_ty: Ty<'tcx>, trait_ref: Option<ty::TraitRef<'tcx>>, - ) -> Result<Self, PrintError>; + ) -> Result<(), PrintError>; fn path_append( - self, - print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>, + &mut self, + print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, disambiguated_data: &DisambiguatedDefPathData, - ) -> Result<Self, PrintError>; + ) -> Result<(), PrintError>; fn path_generic_args( - self, - print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>, + &mut self, + print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, args: &[GenericArg<'tcx>], - ) -> Result<Self, PrintError>; + ) -> Result<(), PrintError>; // Defaults (should not be overridden): #[instrument(skip(self), level = "debug")] fn default_print_def_path( - self, + &mut self, def_id: DefId, args: &'tcx [GenericArg<'tcx>], - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { let key = self.tcx().def_key(def_id); debug!(?key); @@ -161,7 +161,7 @@ pub trait Printer<'tcx>: Sized { } self.path_append( - |cx: Self| { + |cx: &mut Self| { if trait_qualify_parent { let trait_ref = ty::TraitRef::new( cx.tcx(), @@ -180,12 +180,12 @@ pub trait Printer<'tcx>: Sized { } fn default_print_impl_path( - self, + &mut self, impl_def_id: DefId, _args: &'tcx [GenericArg<'tcx>], self_ty: Ty<'tcx>, impl_trait_ref: Option<ty::TraitRef<'tcx>>, - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { debug!( "default_print_impl_path: impl_def_id={:?}, self_ty={}, impl_trait_ref={:?}", impl_def_id, self_ty, impl_trait_ref @@ -286,25 +286,25 @@ pub fn characteristic_def_id_of_type(ty: Ty<'_>) -> Option<DefId> { } impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::Region<'tcx> { - fn print(&self, cx: P) -> Result<P, PrintError> { + fn print(&self, cx: &mut P) -> Result<(), PrintError> { cx.print_region(*self) } } impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for Ty<'tcx> { - fn print(&self, cx: P) -> Result<P, PrintError> { + fn print(&self, cx: &mut P) -> Result<(), PrintError> { cx.print_type(*self) } } impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>> { - fn print(&self, cx: P) -> Result<P, PrintError> { + fn print(&self, cx: &mut P) -> Result<(), PrintError> { cx.print_dyn_existential(self) } } impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::Const<'tcx> { - fn print(&self, cx: P) -> Result<P, PrintError> { + fn print(&self, cx: &mut P) -> Result<(), PrintError> { cx.print_const(*self) } } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 38b3096f851..316370977a4 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -42,10 +42,10 @@ macro_rules! p { write!(scoped_cx!(), $($data),+)? }; (@print($x:expr)) => { - scoped_cx!() = $x.print(scoped_cx!())? + $x.print(scoped_cx!())? }; (@$method:ident($($arg:expr),*)) => { - scoped_cx!() = scoped_cx!().$method($($arg),*)? + scoped_cx!().$method($($arg),*)? }; ($($elem:tt $(($($args:tt)*))?),+) => {{ $(p!(@ $elem $(($($args)*))?);)+ @@ -209,25 +209,25 @@ impl<'tcx> RegionHighlightMode<'tcx> { pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { /// Like `print_def_path` but for value paths. fn print_value_path( - self, + &mut self, def_id: DefId, args: &'tcx [GenericArg<'tcx>], - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { self.print_def_path(def_id, args) } - fn in_binder<T>(self, value: &ty::Binder<'tcx, T>) -> Result<Self, PrintError> + fn in_binder<T>(&mut self, value: &ty::Binder<'tcx, T>) -> Result<(), PrintError> where T: Print<'tcx, Self> + TypeFoldable<TyCtxt<'tcx>>, { value.as_ref().skip_binder().print(self) } - fn wrap_binder<T, F: FnOnce(&T, Self) -> Result<Self, fmt::Error>>( - self, + fn wrap_binder<T, F: FnOnce(&T, &mut Self) -> Result<(), fmt::Error>>( + &mut self, value: &ty::Binder<'tcx, T>, f: F, - ) -> Result<Self, PrintError> + ) -> Result<(), PrintError> where T: Print<'tcx, Self> + TypeFoldable<TyCtxt<'tcx>>, { @@ -235,40 +235,40 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } /// Prints comma-separated elements. - fn comma_sep<T>(mut self, mut elems: impl Iterator<Item = T>) -> Result<Self, PrintError> + fn comma_sep<T>(&mut self, mut elems: impl Iterator<Item = T>) -> Result<(), PrintError> where T: Print<'tcx, Self>, { if let Some(first) = elems.next() { - self = first.print(self)?; + first.print(self)?; for elem in elems { self.write_str(", ")?; - self = elem.print(self)?; + elem.print(self)?; } } - Ok(self) + Ok(()) } /// Prints `{f: t}` or `{f as t}` depending on the `cast` argument fn typed_value( - mut self, - f: impl FnOnce(Self) -> Result<Self, PrintError>, - t: impl FnOnce(Self) -> Result<Self, PrintError>, + &mut self, + f: impl FnOnce(&mut Self) -> Result<(), PrintError>, + t: impl FnOnce(&mut Self) -> Result<(), PrintError>, conversion: &str, - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { self.write_str("{")?; - self = f(self)?; + f(self)?; self.write_str(conversion)?; - self = t(self)?; + t(self)?; self.write_str("}")?; - Ok(self) + Ok(()) } /// Prints `<...>` around what `f` prints. fn generic_delimiters( - self, - f: impl FnOnce(Self) -> Result<Self, PrintError>, - ) -> Result<Self, PrintError>; + &mut self, + f: impl FnOnce(&mut Self) -> Result<(), PrintError>, + ) -> Result<(), PrintError>; /// Returns `true` if the region should be printed in /// optional positions, e.g., `&'a T` or `dyn Tr + 'b`. @@ -282,9 +282,9 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { /// If possible, this returns a global path resolving to `def_id` that is visible /// from at least one local module, and returns `true`. If the crate defining `def_id` is /// declared with an `extern crate`, the path is guaranteed to use the `extern crate`. - fn try_print_visible_def_path(self, def_id: DefId) -> Result<(Self, bool), PrintError> { + fn try_print_visible_def_path(&mut self, def_id: DefId) -> Result<bool, PrintError> { if NO_VISIBLE_PATH.with(|flag| flag.get()) { - return Ok((self, false)); + return Ok(false); } let mut callers = Vec::new(); @@ -296,7 +296,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { // For enum variants, if they have an unique name, then we only print the name, otherwise we // print the enum name and the variant name. Otherwise, we do not print anything and let the // caller use the `print_def_path` fallback. - fn force_print_trimmed_def_path(mut self, def_id: DefId) -> Result<(Self, bool), PrintError> { + fn force_print_trimmed_def_path(&mut self, def_id: DefId) -> Result<bool, PrintError> { let key = self.tcx().def_key(def_id); let visible_parent_map = self.tcx().visible_parent_map(()); let kind = self.tcx().def_kind(def_id); @@ -324,7 +324,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { { // If `Assoc` is unique, we don't want to talk about `Trait::Assoc`. self.write_str(get_local_name(&self, *symbol, def_id, key).as_str())?; - return Ok((self, true)); + return Ok(true); } if let Some(symbol) = key.get_opt_name() { if let DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy = kind @@ -357,36 +357,35 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { { } else { // If not covered above, like for example items out of `impl` blocks, fallback. - return Ok((self, false)); + return Ok(false); } self.write_str(get_local_name(&self, symbol, def_id, key).as_str())?; - return Ok((self, true)); + return Ok(true); } - Ok((self, false)) + Ok(false) } /// Try to see if this path can be trimmed to a unique symbol name. - fn try_print_trimmed_def_path(mut self, def_id: DefId) -> Result<(Self, bool), PrintError> { + fn try_print_trimmed_def_path(&mut self, def_id: DefId) -> Result<bool, PrintError> { if FORCE_TRIMMED_PATH.with(|flag| flag.get()) { - let (s, trimmed) = self.force_print_trimmed_def_path(def_id)?; + let trimmed = self.force_print_trimmed_def_path(def_id)?; if trimmed { - return Ok((s, true)); + return Ok(true); } - self = s; } if !self.tcx().sess.opts.unstable_opts.trim_diagnostic_paths || matches!(self.tcx().sess.opts.trimmed_def_paths, TrimmedDefPaths::Never) || NO_TRIMMED_PATH.with(|flag| flag.get()) || SHOULD_PREFIX_WITH_CRATE.with(|flag| flag.get()) { - return Ok((self, false)); + return Ok(false); } match self.tcx().trimmed_def_paths(()).get(&def_id) { - None => Ok((self, false)), + None => Ok(false), Some(symbol) => { write!(self, "{}", Ident::with_dummy_span(*symbol))?; - Ok((self, true)) + Ok(true) } } } @@ -405,10 +404,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { /// This method returns false if we can't print the visible path, so /// `print_def_path` can fall back on the item's real definition path. fn try_print_visible_def_path_recur( - mut self, + &mut self, def_id: DefId, callers: &mut Vec<DefId>, - ) -> Result<(Self, bool), PrintError> { + ) -> Result<bool, PrintError> { define_scoped_cx!(self); debug!("try_print_visible_def_path: def_id={:?}", def_id); @@ -417,7 +416,8 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { // path to the crate followed by the path to the item within the crate. if let Some(cnum) = def_id.as_crate_root() { if cnum == LOCAL_CRATE { - return Ok((self.path_crate(cnum)?, true)); + self.path_crate(cnum)?; + return Ok(true); } // In local mode, when we encounter a crate other than @@ -440,7 +440,8 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { // or avoid ending up with `ExternCrateSource::Extern`, // for the injected `std`/`core`. if span.is_dummy() { - return Ok((self.path_crate(cnum)?, true)); + self.path_crate(cnum)?; + return Ok(true); } // Disable `try_print_trimmed_def_path` behavior within @@ -448,23 +449,25 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { // in cases where the `extern crate foo` has non-trivial // parents, e.g. it's nested in `impl foo::Trait for Bar` // (see also issues #55779 and #87932). - self = with_no_visible_paths!(self.print_def_path(def_id, &[])?); + with_no_visible_paths!(self.print_def_path(def_id, &[])?); - return Ok((self, true)); + return Ok(true); } (ExternCrateSource::Path, LOCAL_CRATE) => { - return Ok((self.path_crate(cnum)?, true)); + self.path_crate(cnum)?; + return Ok(true); } _ => {} }, None => { - return Ok((self.path_crate(cnum)?, true)); + self.path_crate(cnum)?; + return Ok(true); } } } if def_id.is_local() { - return Ok((self, false)); + return Ok(false); } let visible_parent_map = self.tcx().visible_parent_map(()); @@ -485,7 +488,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } let Some(visible_parent) = visible_parent_map.get(&def_id).cloned() else { - return Ok((self, false)); + return Ok(false); }; let actual_parent = self.tcx().opt_parent(def_id); @@ -548,7 +551,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { *name = new_name; } else { // There is no name that is public and isn't `_`, so bail. - return Ok((self, false)); + return Ok(false); } } // Re-exported `extern crate` (#43189). @@ -560,7 +563,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { debug!("try_print_visible_def_path: data={:?}", data); if callers.contains(&visible_parent) { - return Ok((self, false)); + return Ok(false); } callers.push(visible_parent); // HACK(eddyb) this bypasses `path_append`'s prefix printing to avoid @@ -568,19 +571,19 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { // To support printers that do not implement `PrettyPrinter`, a `Vec` or // linked list on the stack would need to be built, before any printing. match self.try_print_visible_def_path_recur(visible_parent, callers)? { - (cx, false) => return Ok((cx, false)), - (cx, true) => self = cx, + false => return Ok(false), + true => {} } callers.pop(); - - Ok((self.path_append(Ok, &DisambiguatedDefPathData { data, disambiguator: 0 })?, true)) + self.path_append(|_| Ok(()), &DisambiguatedDefPathData { data, disambiguator: 0 })?; + Ok(true) } fn pretty_path_qualified( - self, + &mut self, self_ty: Ty<'tcx>, trait_ref: Option<ty::TraitRef<'tcx>>, - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { if trait_ref.is_none() { // Inherent impls. Try to print `Foo::bar` for an inherent // impl on `Foo`, but fallback to `<Foo>::bar` if self-type is @@ -601,26 +604,26 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } } - self.generic_delimiters(|mut cx| { + self.generic_delimiters(|cx| { define_scoped_cx!(cx); p!(print(self_ty)); if let Some(trait_ref) = trait_ref { p!(" as ", print(trait_ref.print_only_trait_path())); } - Ok(cx) + Ok(()) }) } fn pretty_path_append_impl( - mut self, - print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>, + &mut self, + print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, self_ty: Ty<'tcx>, trait_ref: Option<ty::TraitRef<'tcx>>, - ) -> Result<Self, PrintError> { - self = print_prefix(self)?; + ) -> Result<(), PrintError> { + print_prefix(self)?; - self.generic_delimiters(|mut cx| { + self.generic_delimiters(|cx| { define_scoped_cx!(cx); p!("impl "); @@ -629,11 +632,11 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } p!(print(self_ty)); - Ok(cx) + Ok(()) }) } - fn pretty_print_type(mut self, ty: Ty<'tcx>) -> Result<Self, PrintError> { + fn pretty_print_type(&mut self, ty: Ty<'tcx>) -> Result<(), PrintError> { define_scoped_cx!(self); match *ty.kind() { @@ -679,7 +682,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty::Infer(infer_ty) => { if self.should_print_verbose() { p!(write("{:?}", ty.kind())); - return Ok(self); + return Ok(()); } if let ty::TyVar(ty_vid) = infer_ty { @@ -696,7 +699,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty::Param(ref param_ty) => p!(print(param_ty)), ty::Bound(debruijn, bound_ty) => match bound_ty.kind { ty::BoundTyKind::Anon => { - rustc_type_ir::debug_bound_var(&mut self, debruijn, bound_ty.var)? + rustc_type_ir::debug_bound_var(self, debruijn, bound_ty.var)? } ty::BoundTyKind::Param(_, s) => match self.should_print_verbose() { true => p!(write("{:?}", ty.kind())), @@ -751,7 +754,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { if self.should_print_verbose() { // FIXME(eddyb) print this with `print_def_path`. p!(write("Opaque({:?}, {})", def_id, args.print_as_list())); - return Ok(self); + return Ok(()); } let parent = self.tcx().parent(def_id); @@ -766,17 +769,17 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { // If the type alias directly starts with the `impl` of the // opaque type we're printing, then skip the `::{opaque#1}`. p!(print_def_path(parent, args)); - return Ok(self); + return Ok(()); } } // Complex opaque type, e.g. `type Foo = (i32, impl Debug);` p!(print_def_path(def_id, args)); - return Ok(self); + return Ok(()); } _ => { if with_no_queries() { p!(print_def_path(def_id, &[])); - return Ok(self); + return Ok(()); } else { return self.pretty_print_opaque_impl_type(def_id, args); } @@ -817,7 +820,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { if !args.as_coroutine().is_valid() { p!("unavailable"); } else { - self = self.comma_sep(args.as_coroutine().upvar_tys().iter())?; + self.comma_sep(args.as_coroutine().upvar_tys().iter())?; } p!(")"); @@ -887,7 +890,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { print(args.as_closure().sig_as_fn_ptr_ty()) ); p!(" upvar_tys=("); - self = self.comma_sep(args.as_closure().upvar_tys().iter())?; + self.comma_sep(args.as_closure().upvar_tys().iter())?; p!(")"); } } @@ -897,14 +900,14 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty::Slice(ty) => p!("[", print(ty), "]"), } - Ok(self) + Ok(()) } fn pretty_print_opaque_impl_type( - mut self, + &mut self, def_id: DefId, args: &'tcx ty::List<ty::GenericArg<'tcx>>, - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { let tcx = self.tcx(); // Grab the "TraitA + TraitB" from `impl TraitA + TraitB`, @@ -962,7 +965,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { write!(self, "{}", if first { "" } else { " + " })?; write!(self, "{}", if paren_needed { "(" } else { "" })?; - self = self.wrap_binder(&fn_once_trait_ref, |trait_ref, mut cx| { + self.wrap_binder(&fn_once_trait_ref, |trait_ref, cx| { define_scoped_cx!(cx); // Get the (single) generic ty (the args) of this FnOnce trait ref. let generics = tcx.generics_of(trait_ref.def_id); @@ -1019,7 +1022,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } } - Ok(cx) + Ok(()) })?; } @@ -1027,7 +1030,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { for (trait_ref, assoc_items) in traits { write!(self, "{}", if first { "" } else { " + " })?; - self = self.wrap_binder(&trait_ref, |trait_ref, mut cx| { + self.wrap_binder(&trait_ref, |trait_ref, cx| { define_scoped_cx!(cx); p!(print(trait_ref.print_only_trait_name())); @@ -1091,7 +1094,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } first = false; - Ok(cx) + Ok(()) })?; } @@ -1104,7 +1107,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { if !FORCE_TRIMMED_PATH.with(|flag| flag.get()) { for re in lifetimes { write!(self, " + ")?; - self = self.print_region(re)?; + self.print_region(re)?; } } @@ -1117,11 +1120,11 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { { let num_args = self.tcx().generics_of(fn_def_id).count(); write!(self, " {{ ")?; - self = self.print_def_path(fn_def_id, &args[..num_args])?; + self.print_def_path(fn_def_id, &args[..num_args])?; write!(self, "() }}")?; } - Ok(self) + Ok(()) } /// Insert the trait ref and optionally a projection type associated with it into either the @@ -1172,9 +1175,9 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } fn pretty_print_inherent_projection( - self, + &mut self, alias_ty: &ty::AliasTy<'tcx>, - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { let def_key = self.tcx().def_key(alias_ty.def_id); self.path_generic_args( |cx| { @@ -1196,14 +1199,14 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } fn pretty_print_dyn_existential( - mut self, + &mut self, predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { // Generate the main trait ref, including associated types. let mut first = true; if let Some(principal) = predicates.principal() { - self = self.wrap_binder(&principal, |principal, mut cx| { + self.wrap_binder(&principal, |principal, cx| { define_scoped_cx!(cx); p!(print_def_path(principal.def_id, &[])); @@ -1243,8 +1246,8 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { }); if !args.is_empty() || !projections.is_empty() { - p!(generic_delimiters(|mut cx| { - cx = cx.comma_sep(args.iter().copied())?; + p!(generic_delimiters(|cx| { + cx.comma_sep(args.iter().copied())?; if !args.is_empty() && !projections.is_empty() { write!(cx, ", ")?; } @@ -1252,7 +1255,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { })); } } - Ok(cx) + Ok(()) })?; first = false; @@ -1283,15 +1286,15 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { p!(print_def_path(def_id, &[])); } - Ok(self) + Ok(()) } fn pretty_fn_sig( - mut self, + &mut self, inputs: &[Ty<'tcx>], c_variadic: bool, output: Ty<'tcx>, - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { define_scoped_cx!(self); p!("(", comma_sep(inputs.iter().copied())); @@ -1306,28 +1309,28 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { p!(" -> ", print(output)); } - Ok(self) + Ok(()) } fn pretty_print_const( - mut self, + &mut self, ct: ty::Const<'tcx>, print_ty: bool, - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { define_scoped_cx!(self); if self.should_print_verbose() { p!(write("{:?}", ct)); - return Ok(self); + return Ok(()); } macro_rules! print_underscore { () => {{ if print_ty { - self = self.typed_value( - |mut this| { + self.typed_value( + |this| { write!(this, "_")?; - Ok(this) + Ok(()) }, |this| this.print_type(ct.ty()), ": ", @@ -1378,7 +1381,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } ty::ConstKind::Bound(debruijn, bound_var) => { - rustc_type_ir::debug_bound_var(&mut self, debruijn, bound_var)? + rustc_type_ir::debug_bound_var(self, debruijn, bound_var)? } ty::ConstKind::Placeholder(placeholder) => p!(write("{placeholder:?}")), // FIXME(generic_const_exprs): @@ -1386,10 +1389,14 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty::ConstKind::Expr(_) => p!("{{const expr}}"), ty::ConstKind::Error(_) => p!("{{const error}}"), }; - Ok(self) + Ok(()) } - fn pretty_print_const_scalar(self, scalar: Scalar, ty: Ty<'tcx>) -> Result<Self, PrintError> { + fn pretty_print_const_scalar( + &mut self, + scalar: Scalar, + ty: Ty<'tcx>, + ) -> Result<(), PrintError> { match scalar { Scalar::Ptr(ptr, _size) => self.pretty_print_const_scalar_ptr(ptr, ty), Scalar::Int(int) => { @@ -1399,10 +1406,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } fn pretty_print_const_scalar_ptr( - mut self, + &mut self, ptr: Pointer, ty: Ty<'tcx>, - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { define_scoped_cx!(self); let (alloc_id, offset) = ptr.into_parts(); @@ -1433,7 +1440,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { Some(GlobalAlloc::VTable(..)) => p!("<vtable>"), None => p!("<dangling pointer>"), } - return Ok(self); + return Ok(()); } } } @@ -1444,27 +1451,27 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { if let Some(GlobalAlloc::Function(instance)) = self.tcx().try_get_global_alloc(alloc_id) { - self = self.typed_value( + self.typed_value( |this| this.print_value_path(instance.def_id(), instance.args), |this| this.print_type(ty), " as ", )?; - return Ok(self); + return Ok(()); } } _ => {} } // Any pointer values not covered by a branch above - self = self.pretty_print_const_pointer(ptr, ty)?; - Ok(self) + self.pretty_print_const_pointer(ptr, ty)?; + Ok(()) } fn pretty_print_const_scalar_int( - mut self, + &mut self, int: ScalarInt, ty: Ty<'tcx>, print_ty: bool, - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { define_scoped_cx!(self); match ty.kind() { @@ -1491,10 +1498,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { // Pointer types ty::Ref(..) | ty::RawPtr(_) | ty::FnPtr(_) => { let data = int.assert_bits(self.tcx().data_layout.pointer_size); - self = self.typed_value( - |mut this| { + self.typed_value( + |this| { write!(this, "0x{data:x}")?; - Ok(this) + Ok(()) }, |this| this.print_type(ty), " as ", @@ -1502,57 +1509,57 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } // Nontrivial types with scalar bit representation _ => { - let print = |mut this: Self| { + let print = |this: &mut Self| { if int.size() == Size::ZERO { write!(this, "transmute(())")?; } else { write!(this, "transmute(0x{int:x})")?; } - Ok(this) + Ok(()) }; - self = if print_ty { + if print_ty { self.typed_value(print, |this| this.print_type(ty), ": ")? } else { print(self)? }; } } - Ok(self) + Ok(()) } /// This is overridden for MIR printing because we only want to hide alloc ids from users, not /// from MIR where it is actually useful. fn pretty_print_const_pointer<Prov: Provenance>( - self, + &mut self, _: Pointer<Prov>, ty: Ty<'tcx>, - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { self.typed_value( - |mut this| { + |this| { this.write_str("&_")?; - Ok(this) + Ok(()) }, |this| this.print_type(ty), ": ", ) } - fn pretty_print_byte_str(mut self, byte_str: &'tcx [u8]) -> Result<Self, PrintError> { + fn pretty_print_byte_str(&mut self, byte_str: &'tcx [u8]) -> Result<(), PrintError> { write!(self, "b\"{}\"", byte_str.escape_ascii())?; - Ok(self) + Ok(()) } fn pretty_print_const_valtree( - mut self, + &mut self, valtree: ty::ValTree<'tcx>, ty: Ty<'tcx>, print_ty: bool, - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { define_scoped_cx!(self); if self.should_print_verbose() { p!(write("ValTree({:?}: ", valtree), print(ty), ")"); - return Ok(self); + return Ok(()); } let u8_type = self.tcx().types.u8; @@ -1573,12 +1580,12 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { bug!("expected to convert valtree to raw bytes for type {:?}", ty) }); p!(write("{:?}", String::from_utf8_lossy(bytes))); - return Ok(self); + return Ok(()); } _ => { p!("&"); p!(pretty_print_const_valtree(valtree, *inner_ty, print_ty)); - return Ok(self); + return Ok(()); } }, (ty::ValTree::Branch(_), ty::Array(t, _)) if *t == u8_type => { @@ -1587,7 +1594,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { }); p!("*"); p!(pretty_print_byte_str(bytes)); - return Ok(self); + return Ok(()); } // Aggregates, printed as array/tuple/struct/variant construction syntax. (ty::ValTree::Branch(_), ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) => { @@ -1606,10 +1613,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { p!(")"); } ty::Adt(def, _) if def.variants().is_empty() => { - self = self.typed_value( - |mut this| { + self.typed_value( + |this| { write!(this, "unreachable()")?; - Ok(this) + Ok(()) }, |this| this.print_type(ty), ": ", @@ -1641,7 +1648,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } _ => unreachable!(), } - return Ok(self); + return Ok(()); } (ty::ValTree::Leaf(leaf), ty::Ref(_, inner_ty, _)) => { p!(write("&")); @@ -1664,18 +1671,15 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { if print_ty { p!(": ", print(ty)); } - Ok(self) + Ok(()) } - fn pretty_closure_as_impl( - mut self, - closure: ty::ClosureArgs<'tcx>, - ) -> Result<Self, PrintError> { + fn pretty_closure_as_impl(&mut self, closure: ty::ClosureArgs<'tcx>) -> Result<(), PrintError> { let sig = closure.sig(); let kind = closure.kind_ty().to_opt_closure_kind().unwrap_or(ty::ClosureKind::Fn); write!(self, "impl ")?; - self.wrap_binder(&sig, |sig, mut cx| { + self.wrap_binder(&sig, |sig, cx| { define_scoped_cx!(cx); p!(print(kind), "("); @@ -1691,7 +1695,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { p!(" -> ", print(sig.output())); } - Ok(cx) + Ok(()) }) } @@ -1709,7 +1713,7 @@ pub(crate) fn pretty_print_const<'tcx>( let literal = tcx.lift(c).unwrap(); let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS); cx.print_alloc_ids = true; - let cx = cx.pretty_print_const(literal, print_types)?; + cx.pretty_print_const(literal, print_types)?; fmt.write_str(&cx.into_buffer())?; Ok(()) }) @@ -1760,6 +1764,16 @@ impl<'a, 'tcx> FmtPrinter<'a, 'tcx> { Self::new_with_limit(tcx, ns, limit) } + pub fn print_string( + tcx: TyCtxt<'tcx>, + ns: Namespace, + f: impl FnOnce(&mut Self) -> Result<(), PrintError>, + ) -> Result<String, PrintError> { + let mut c = FmtPrinter::new(tcx, ns); + f(&mut c)?; + Ok(c.into_buffer()) + } + pub fn new_with_limit(tcx: TyCtxt<'tcx>, ns: Namespace, type_length_limit: Limit) -> Self { FmtPrinter(Box::new(FmtPrinterData { tcx, @@ -1820,7 +1834,8 @@ impl<'t> TyCtxt<'t> { let def_id = def_id.into_query_param(); let ns = guess_def_namespace(self, def_id); debug!("def_path_str: def_id={:?}, ns={:?}", def_id, ns); - FmtPrinter::new(self, ns).print_def_path(def_id, args).unwrap().into_buffer() + + FmtPrinter::print_string(self, ns, |cx| cx.print_def_path(def_id, args)).unwrap() } pub fn value_path_str_with_args( @@ -1831,7 +1846,8 @@ impl<'t> TyCtxt<'t> { let def_id = def_id.into_query_param(); let ns = guess_def_namespace(self, def_id); debug!("value_path_str: def_id={:?}, ns={:?}", def_id, ns); - FmtPrinter::new(self, ns).print_value_path(def_id, args).unwrap().into_buffer() + + FmtPrinter::print_string(self, ns, |cx| cx.print_value_path(def_id, args)).unwrap() } } @@ -1848,21 +1864,21 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { } fn print_def_path( - mut self, + &mut self, def_id: DefId, args: &'tcx [GenericArg<'tcx>], - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { define_scoped_cx!(self); if args.is_empty() { match self.try_print_trimmed_def_path(def_id)? { - (cx, true) => return Ok(cx), - (cx, false) => self = cx, + true => return Ok(()), + false => {} } match self.try_print_visible_def_path(def_id)? { - (cx, true) => return Ok(cx), - (cx, false) => self = cx, + true => return Ok(()), + false => {} } } @@ -1883,7 +1899,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { let parent_def_id = DefId { index: key.parent.unwrap(), ..def_id }; let span = self.tcx.def_span(def_id); - self = self.print_def_path(parent_def_id, &[])?; + self.print_def_path(parent_def_id, &[])?; // HACK(eddyb) copy of `path_append` to avoid // constructing a `DisambiguatedDefPathData`. @@ -1899,40 +1915,40 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { )?; self.empty_path = false; - return Ok(self); + return Ok(()); } } self.default_print_def_path(def_id, args) } - fn print_region(self, region: ty::Region<'tcx>) -> Result<Self, PrintError> { + fn print_region(&mut self, region: ty::Region<'tcx>) -> Result<(), PrintError> { self.pretty_print_region(region) } - fn print_type(mut self, ty: Ty<'tcx>) -> Result<Self, PrintError> { + fn print_type(&mut self, ty: Ty<'tcx>) -> Result<(), PrintError> { if self.type_length_limit.value_within_limit(self.printed_type_count) { self.printed_type_count += 1; self.pretty_print_type(ty) } else { self.truncated = true; write!(self, "...")?; - Ok(self) + Ok(()) } } fn print_dyn_existential( - self, + &mut self, predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { self.pretty_print_dyn_existential(predicates) } - fn print_const(self, ct: ty::Const<'tcx>) -> Result<Self, PrintError> { + fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError> { self.pretty_print_const(ct, false) } - fn path_crate(mut self, cnum: CrateNum) -> Result<Self, PrintError> { + fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> { self.empty_path = true; if cnum == LOCAL_CRATE { if self.tcx.sess.at_least_rust_2018() { @@ -1946,52 +1962,52 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { write!(self, "{}", self.tcx.crate_name(cnum))?; self.empty_path = false; } - Ok(self) + Ok(()) } fn path_qualified( - mut self, + &mut self, self_ty: Ty<'tcx>, trait_ref: Option<ty::TraitRef<'tcx>>, - ) -> Result<Self, PrintError> { - self = self.pretty_path_qualified(self_ty, trait_ref)?; + ) -> Result<(), PrintError> { + self.pretty_path_qualified(self_ty, trait_ref)?; self.empty_path = false; - Ok(self) + Ok(()) } fn path_append_impl( - mut self, - print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>, + &mut self, + print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, _disambiguated_data: &DisambiguatedDefPathData, self_ty: Ty<'tcx>, trait_ref: Option<ty::TraitRef<'tcx>>, - ) -> Result<Self, PrintError> { - self = self.pretty_path_append_impl( - |mut cx| { - cx = print_prefix(cx)?; + ) -> Result<(), PrintError> { + self.pretty_path_append_impl( + |cx| { + print_prefix(cx)?; if !cx.empty_path { write!(cx, "::")?; } - Ok(cx) + Ok(()) }, self_ty, trait_ref, )?; self.empty_path = false; - Ok(self) + Ok(()) } fn path_append( - mut self, - print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>, + &mut self, + print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, disambiguated_data: &DisambiguatedDefPathData, - ) -> Result<Self, PrintError> { - self = print_prefix(self)?; + ) -> Result<(), PrintError> { + print_prefix(self)?; // Skip `::{{extern}}` blocks and `::{{constructor}}` on tuple/unit structs. if let DefPathData::ForeignMod | DefPathData::Ctor = disambiguated_data.data { - return Ok(self); + return Ok(()); } let name = disambiguated_data.data.name(); @@ -2006,19 +2022,19 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { } let verbose = self.should_print_verbose(); - disambiguated_data.fmt_maybe_verbose(&mut self, verbose)?; + disambiguated_data.fmt_maybe_verbose(self, verbose)?; self.empty_path = false; - Ok(self) + Ok(()) } fn path_generic_args( - mut self, - print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>, + &mut self, + print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, args: &[GenericArg<'tcx>], - ) -> Result<Self, PrintError> { - self = print_prefix(self)?; + ) -> Result<(), PrintError> { + print_prefix(self)?; let tcx = self.tcx; @@ -2052,7 +2068,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { } self.generic_delimiters(|cx| cx.comma_sep(args.into_iter())) } else { - Ok(self) + Ok(()) } } } @@ -2071,29 +2087,29 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> { } fn print_value_path( - mut self, + &mut self, def_id: DefId, args: &'tcx [GenericArg<'tcx>], - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { let was_in_value = std::mem::replace(&mut self.in_value, true); - self = self.print_def_path(def_id, args)?; + self.print_def_path(def_id, args)?; self.in_value = was_in_value; - Ok(self) + Ok(()) } - fn in_binder<T>(self, value: &ty::Binder<'tcx, T>) -> Result<Self, PrintError> + fn in_binder<T>(&mut self, value: &ty::Binder<'tcx, T>) -> Result<(), PrintError> where T: Print<'tcx, Self> + TypeFoldable<TyCtxt<'tcx>>, { self.pretty_in_binder(value) } - fn wrap_binder<T, C: FnOnce(&T, Self) -> Result<Self, PrintError>>( - self, + fn wrap_binder<T, C: FnOnce(&T, &mut Self) -> Result<(), PrintError>>( + &mut self, value: &ty::Binder<'tcx, T>, f: C, - ) -> Result<Self, PrintError> + ) -> Result<(), PrintError> where T: Print<'tcx, Self> + TypeFoldable<TyCtxt<'tcx>>, { @@ -2101,33 +2117,33 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> { } fn typed_value( - mut self, - f: impl FnOnce(Self) -> Result<Self, PrintError>, - t: impl FnOnce(Self) -> Result<Self, PrintError>, + &mut self, + f: impl FnOnce(&mut Self) -> Result<(), PrintError>, + t: impl FnOnce(&mut Self) -> Result<(), PrintError>, conversion: &str, - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { self.write_str("{")?; - self = f(self)?; + f(self)?; self.write_str(conversion)?; let was_in_value = std::mem::replace(&mut self.in_value, false); - self = t(self)?; + t(self)?; self.in_value = was_in_value; self.write_str("}")?; - Ok(self) + Ok(()) } fn generic_delimiters( - mut self, - f: impl FnOnce(Self) -> Result<Self, PrintError>, - ) -> Result<Self, PrintError> { + &mut self, + f: impl FnOnce(&mut Self) -> Result<(), PrintError>, + ) -> Result<(), PrintError> { write!(self, "<")?; let was_in_value = std::mem::replace(&mut self.in_value, false); - let mut inner = f(self)?; - inner.in_value = was_in_value; + f(self)?; + self.in_value = was_in_value; - write!(inner, ">")?; - Ok(inner) + write!(self, ">")?; + Ok(()) } fn should_print_region(&self, region: ty::Region<'tcx>) -> bool { @@ -2176,18 +2192,18 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> { } fn pretty_print_const_pointer<Prov: Provenance>( - self, + &mut self, p: Pointer<Prov>, ty: Ty<'tcx>, - ) -> Result<Self, PrintError> { - let print = |mut this: Self| { + ) -> Result<(), PrintError> { + let print = |this: &mut Self| { define_scoped_cx!(this); if this.print_alloc_ids { p!(write("{:?}", p)); } else { p!("&_"); } - Ok(this) + Ok(()) }; self.typed_value(print, |this| this.print_type(ty), ": ") } @@ -2195,19 +2211,19 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> { // HACK(eddyb) limited to `FmtPrinter` because of `region_highlight_mode`. impl<'tcx> FmtPrinter<'_, 'tcx> { - pub fn pretty_print_region(mut self, region: ty::Region<'tcx>) -> Result<Self, fmt::Error> { + pub fn pretty_print_region(&mut self, region: ty::Region<'tcx>) -> Result<(), fmt::Error> { define_scoped_cx!(self); // Watch out for region highlights. let highlight = self.region_highlight_mode; if let Some(n) = highlight.region_highlighted(region) { p!(write("'{}", n)); - return Ok(self); + return Ok(()); } if self.should_print_verbose() { p!(write("{:?}", region)); - return Ok(self); + return Ok(()); } let identify_regions = self.tcx.sess.opts.unstable_opts.identify_regions; @@ -2220,7 +2236,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { ty::ReEarlyBound(ref data) => { if data.name != kw::Empty { p!(write("{}", data.name)); - return Ok(self); + return Ok(()); } } ty::ReLateBound(_, ty::BoundRegion { kind: br, .. }) @@ -2232,32 +2248,32 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { && br.is_named() { p!(write("{}", name)); - return Ok(self); + return Ok(()); } if let Some((region, counter)) = highlight.highlight_bound_region { if br == region { p!(write("'{}", counter)); - return Ok(self); + return Ok(()); } } } ty::ReVar(region_vid) if identify_regions => { p!(write("{:?}", region_vid)); - return Ok(self); + return Ok(()); } ty::ReVar(_) => {} ty::ReErased => {} ty::ReError(_) => {} ty::ReStatic => { p!("'static"); - return Ok(self); + return Ok(()); } } p!("'_"); - Ok(self) + Ok(()) } } @@ -2340,9 +2356,9 @@ impl<'a, 'tcx> ty::TypeFolder<TyCtxt<'tcx>> for RegionFolder<'a, 'tcx> { // `region_index` and `used_region_names`. impl<'tcx> FmtPrinter<'_, 'tcx> { pub fn name_all_regions<T>( - mut self, + &mut self, value: &ty::Binder<'tcx, T>, - ) -> Result<(Self, T, BTreeMap<ty::BoundRegion, ty::Region<'tcx>>), fmt::Error> + ) -> Result<(T, BTreeMap<ty::BoundRegion, ty::Region<'tcx>>), fmt::Error> where T: Print<'tcx, Self> + TypeFoldable<TyCtxt<'tcx>>, { @@ -2417,10 +2433,10 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { // anyways. let (new_value, map) = if self.should_print_verbose() { for var in value.bound_vars().iter() { - start_or_continue(&mut self, "for<", ", "); + start_or_continue(self, "for<", ", "); write!(self, "{var:?}")?; } - start_or_continue(&mut self, "", "> "); + start_or_continue(self, "", "> "); (value.clone().skip_binder(), BTreeMap::default()) } else { let tcx = self.tcx; @@ -2484,8 +2500,8 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { }; if !trim_path { - start_or_continue(&mut self, "for<", ", "); - do_continue(&mut self, name); + start_or_continue(self, "for<", ", "); + do_continue(self, name); } ty::Region::new_late_bound( tcx, @@ -2502,42 +2518,42 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { let new_value = value.clone().skip_binder().fold_with(&mut folder); let region_map = folder.region_map; if !trim_path { - start_or_continue(&mut self, "", "> "); + start_or_continue(self, "", "> "); } (new_value, region_map) }; self.binder_depth += 1; self.region_index = region_index; - Ok((self, new_value, map)) + Ok((new_value, map)) } - pub fn pretty_in_binder<T>(self, value: &ty::Binder<'tcx, T>) -> Result<Self, fmt::Error> + pub fn pretty_in_binder<T>(&mut self, value: &ty::Binder<'tcx, T>) -> Result<(), fmt::Error> where T: Print<'tcx, Self> + TypeFoldable<TyCtxt<'tcx>>, { let old_region_index = self.region_index; - let (new, new_value, _) = self.name_all_regions(value)?; - let mut inner = new_value.print(new)?; - inner.region_index = old_region_index; - inner.binder_depth -= 1; - Ok(inner) + let (new_value, _) = self.name_all_regions(value)?; + new_value.print(self)?; + self.region_index = old_region_index; + self.binder_depth -= 1; + Ok(()) } - pub fn pretty_wrap_binder<T, C: FnOnce(&T, Self) -> Result<Self, fmt::Error>>( - self, + pub fn pretty_wrap_binder<T, C: FnOnce(&T, &mut Self) -> Result<(), fmt::Error>>( + &mut self, value: &ty::Binder<'tcx, T>, f: C, - ) -> Result<Self, fmt::Error> + ) -> Result<(), fmt::Error> where T: Print<'tcx, Self> + TypeFoldable<TyCtxt<'tcx>>, { let old_region_index = self.region_index; - let (new, new_value, _) = self.name_all_regions(value)?; - let mut inner = f(&new_value, new)?; - inner.region_index = old_region_index; - inner.binder_depth -= 1; - Ok(inner) + let (new_value, _) = self.name_all_regions(value)?; + f(&new_value, self)?; + self.region_index = old_region_index; + self.binder_depth -= 1; + Ok(()) } fn prepare_region_info<T>(&mut self, value: &ty::Binder<'tcx, T>) @@ -2597,7 +2613,7 @@ impl<'tcx, T, P: PrettyPrinter<'tcx>> Print<'tcx, P> for ty::Binder<'tcx, T> where T: Print<'tcx, P> + TypeFoldable<TyCtxt<'tcx>>, { - fn print(&self, cx: P) -> Result<P, PrintError> { + fn print(&self, cx: &mut P) -> Result<(), PrintError> { cx.in_binder(self) } } @@ -2607,10 +2623,10 @@ where T: Print<'tcx, P>, U: Print<'tcx, P>, { - fn print(&self, mut cx: P) -> Result<P, PrintError> { + fn print(&self, cx: &mut P) -> Result<(), PrintError> { define_scoped_cx!(cx); p!(print(self.0), ": ", print(self.1)); - Ok(cx) + Ok(()) } } @@ -2620,9 +2636,10 @@ macro_rules! forward_display_to_print { $(#[allow(unused_lifetimes)] impl<'tcx> fmt::Display for $ty { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ty::tls::with(|tcx| { - let cx = tcx.lift(*self) + let mut cx = FmtPrinter::new(tcx, Namespace::TypeNS); + tcx.lift(*self) .expect("could not lift for printing") - .print(FmtPrinter::new(tcx, Namespace::TypeNS))?; + .print(&mut cx)?; f.write_str(&cx.into_buffer())?; Ok(()) }) @@ -2634,13 +2651,13 @@ macro_rules! forward_display_to_print { macro_rules! define_print_and_forward_display { (($self:ident, $cx:ident): $($ty:ty $print:block)+) => { $(impl<'tcx, P: PrettyPrinter<'tcx>> Print<'tcx, P> for $ty { - fn print(&$self, $cx: P) -> Result<P, PrintError> { + fn print(&$self, $cx: &mut P) -> Result<(), PrintError> { #[allow(unused_mut)] let mut $cx = $cx; define_scoped_cx!($cx); let _: () = $print; #[allow(unreachable_code)] - Ok($cx) + Ok(()) } })+ diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 012bb749412..bf2e61b23b2 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -22,11 +22,10 @@ impl fmt::Debug for ty::TraitDef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ty::tls::with(|tcx| { with_no_trimmed_paths!({ - f.write_str( - &FmtPrinter::new(tcx, Namespace::TypeNS) - .print_def_path(self.def_id, &[])? - .into_buffer(), - ) + let s = FmtPrinter::print_string(tcx, Namespace::TypeNS, |cx| { + cx.print_def_path(self.def_id, &[]) + })?; + f.write_str(&s) }) }) } @@ -36,11 +35,10 @@ impl<'tcx> fmt::Debug for ty::AdtDef<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ty::tls::with(|tcx| { with_no_trimmed_paths!({ - f.write_str( - &FmtPrinter::new(tcx, Namespace::TypeNS) - .print_def_path(self.did(), &[])? - .into_buffer(), - ) + let s = FmtPrinter::print_string(tcx, Namespace::TypeNS, |cx| { + cx.print_def_path(self.did(), &[]) + })?; + f.write_str(&s) }) }) } @@ -350,9 +348,8 @@ impl<'tcx> DebugWithInfcx<TyCtxt<'tcx>> for ty::Const<'tcx> { let ConstKind::Value(valtree) = lifted.kind() else { bug!("we checked that this is a valtree") }; - let cx = FmtPrinter::new(tcx, Namespace::ValueNS); - let cx = - cx.pretty_print_const_valtree(valtree, lifted.ty(), /*print_ty*/ true)?; + let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS); + cx.pretty_print_const_valtree(valtree, lifted.ty(), /*print_ty*/ true)?; f.write_str(&cx.into_buffer()) }); } diff --git a/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs b/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs index 0d466bbe56e..163d74cc9cf 100644 --- a/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs +++ b/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs @@ -1,6 +1,6 @@ use crate::elaborate_drops::DropFlagState; use rustc_middle::mir::{self, Body, Location, Terminator, TerminatorKind}; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::TyCtxt; use rustc_target::abi::VariantIdx; use super::indexes::MovePathIndex; @@ -55,60 +55,6 @@ pub fn on_all_children_bits<'tcx, F>( ) where F: FnMut(MovePathIndex), { - #[inline] - fn is_terminal_path<'tcx>( - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - move_data: &MoveData<'tcx>, - path: MovePathIndex, - ) -> bool { - let place = move_data.move_paths[path].place; - - // When enumerating the child fragments of a path, don't recurse into - // paths (1.) past arrays, slices, and pointers, nor (2.) into a type - // that implements `Drop`. - // - // Places behind references or arrays are not tracked by elaboration - // and are always assumed to be initialized when accessible. As - // references and indexes can be reseated, trying to track them can - // only lead to trouble. - // - // Places behind ADT's with a Drop impl are not tracked by - // elaboration since they can never have a drop-flag state that - // differs from that of the parent with the Drop impl. - // - // In both cases, the contents can only be accessed if and only if - // their parents are initialized. This implies for example that there - // is no need to maintain separate drop flags to track such state. - // - // FIXME: we have to do something for moving slice patterns. - let ty = place.ty(body, tcx).ty; - match ty.kind() { - ty::Adt(def, _) if (def.has_dtor(tcx) && !def.is_box()) || def.is_union() => { - debug!( - "place_contents_drop_state_cannot_differ place: {:?} ty: {:?} Drop => true", - place, ty - ); - true - } - ty::Array(..) => { - debug!( - "place_contents_drop_state_cannot_differ place: {:?} ty: {:?} => false", - place, ty - ); - false - } - ty::Slice(..) | ty::Ref(..) | ty::RawPtr(..) => { - debug!( - "place_contents_drop_state_cannot_differ place: {:?} ty: {:?} refd => true", - place, ty - ); - true - } - _ => false, - } - } - fn on_all_children_bits<'tcx, F>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, @@ -120,10 +66,6 @@ pub fn on_all_children_bits<'tcx, F>( { each_child(move_path_index); - if is_terminal_path(tcx, body, move_data, move_path_index) { - return; - } - let mut next_child_index = move_data.move_paths[move_path_index].first_child; while let Some(child_index) = next_child_index { on_all_children_bits(tcx, body, move_data, child_index, each_child); @@ -133,29 +75,6 @@ pub fn on_all_children_bits<'tcx, F>( on_all_children_bits(tcx, body, move_data, move_path_index, &mut each_child); } -pub fn on_all_drop_children_bits<'tcx, F>( - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - ctxt: &MoveDataParamEnv<'tcx>, - path: MovePathIndex, - mut each_child: F, -) where - F: FnMut(MovePathIndex), -{ - on_all_children_bits(tcx, body, &ctxt.move_data, path, |child| { - let place = &ctxt.move_data.move_paths[path].place; - let ty = place.ty(body, tcx).ty; - debug!("on_all_drop_children_bits({:?}, {:?} : {:?})", path, place, ty); - - let erased_ty = tcx.erase_regions(ty); - if erased_ty.needs_drop(tcx, ctxt.param_env) { - each_child(child); - } else { - debug!("on_all_drop_children_bits - skipping") - } - }) -} - pub fn drop_flag_effects_for_function_entry<'tcx, F>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs index b785a999f08..5020a1cf0b2 100644 --- a/compiler/rustc_mir_dataflow/src/framework/mod.rs +++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs @@ -48,7 +48,7 @@ mod visitor; pub use self::cursor::{AnalysisResults, ResultsClonedCursor, ResultsCursor, ResultsRefCursor}; pub use self::direction::{Backward, Direction, Forward}; pub use self::engine::{Engine, EntrySets, Results, ResultsCloned}; -pub use self::lattice::{JoinSemiLattice, MaybeReachable, MeetSemiLattice}; +pub use self::lattice::{JoinSemiLattice, MaybeReachable}; pub use self::visitor::{visit_results, ResultsVisitable, ResultsVisitor}; /// Analysis domains are all bitsets of various kinds. This trait holds diff --git a/compiler/rustc_mir_dataflow/src/impls/initialized.rs b/compiler/rustc_mir_dataflow/src/impls/initialized.rs index 182f2590137..c968e7aea8f 100644 --- a/compiler/rustc_mir_dataflow/src/impls/initialized.rs +++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs @@ -10,7 +10,7 @@ use crate::framework::SwitchIntEdgeEffects; use crate::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex}; use crate::on_lookup_result_bits; use crate::MoveDataParamEnv; -use crate::{drop_flag_effects, on_all_children_bits, on_all_drop_children_bits}; +use crate::{drop_flag_effects, on_all_children_bits}; use crate::{lattice, AnalysisDomain, GenKill, GenKillAnalysis, MaybeReachable}; /// `MaybeInitializedPlaces` tracks all places that might be @@ -72,7 +72,7 @@ impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> { ) -> bool { if let LookupResult::Exact(path) = self.move_data().rev_lookup.find(place.as_ref()) { let mut maybe_live = false; - on_all_drop_children_bits(self.tcx, self.body, self.mdpe, path, |child| { + on_all_children_bits(self.tcx, self.body, self.move_data(), path, |child| { maybe_live |= state.contains(child); }); !maybe_live @@ -690,9 +690,13 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { if let mir::StatementKind::StorageDead(local) = stmt.kind { // End inits for StorageDead, so that an immutable variable can // be reinitialized on the next iteration of the loop. - let move_path_index = rev_lookup.find_local(local); - debug!("clears the ever initialized status of {:?}", init_path_map[move_path_index]); - trans.kill_all(init_path_map[move_path_index].iter().copied()); + if let Some(move_path_index) = rev_lookup.find_local(local) { + debug!( + "clears the ever initialized status of {:?}", + init_path_map[move_path_index] + ); + trans.kill_all(init_path_map[move_path_index].iter().copied()); + } } } diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index ecf46715cd0..eea0e030e7d 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -23,8 +23,7 @@ use rustc_span::symbol::{sym, Symbol}; pub use self::drop_flag_effects::{ drop_flag_effects_for_function_entry, drop_flag_effects_for_location, - move_path_children_matching, on_all_children_bits, on_all_drop_children_bits, - on_lookup_result_bits, + move_path_children_matching, on_all_children_bits, on_lookup_result_bits, }; pub use self::framework::{ fmt, graphviz, lattice, visit_results, Analysis, AnalysisDomain, AnalysisResults, Backward, diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 91a96593173..ccf3dc7941f 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -1,58 +1,66 @@ use rustc_index::IndexVec; -use rustc_middle::mir::tcx::RvalueInitializationState; +use rustc_middle::mir::tcx::{PlaceTy, RvalueInitializationState}; use rustc_middle::mir::*; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use smallvec::{smallvec, SmallVec}; use std::mem; use super::abs_domain::Lift; -use super::IllegalMoveOriginKind::*; -use super::{Init, InitIndex, InitKind, InitLocation, LookupResult, MoveError}; +use super::{Init, InitIndex, InitKind, InitLocation, LookupResult}; use super::{ LocationMap, MoveData, MoveOut, MoveOutIndex, MovePath, MovePathIndex, MovePathLookup, }; -struct MoveDataBuilder<'a, 'tcx> { +struct MoveDataBuilder<'a, 'tcx, F> { body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, data: MoveData<'tcx>, - errors: Vec<(Place<'tcx>, MoveError<'tcx>)>, + filter: F, } -impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { - fn new(body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self { +impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { + fn new( + body: &'a Body<'tcx>, + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + filter: F, + ) -> Self { let mut move_paths = IndexVec::new(); let mut path_map = IndexVec::new(); let mut init_path_map = IndexVec::new(); + let locals = body + .local_decls + .iter_enumerated() + .map(|(i, l)| { + if l.is_deref_temp() { + return None; + } + if filter(l.ty) { + Some(new_move_path( + &mut move_paths, + &mut path_map, + &mut init_path_map, + None, + Place::from(i), + )) + } else { + None + } + }) + .collect(); + MoveDataBuilder { body, tcx, param_env, - errors: Vec::new(), data: MoveData { moves: IndexVec::new(), loc_map: LocationMap::new(body), rev_lookup: MovePathLookup { - locals: body - .local_decls - .iter_enumerated() - .map(|(i, l)| { - if l.is_deref_temp() { - MovePathIndex::MAX - } else { - Self::new_move_path( - &mut move_paths, - &mut path_map, - &mut init_path_map, - None, - Place::from(i), - ) - } - }) - .collect(), + locals, projections: Default::default(), un_derefer: Default::default(), }, @@ -62,35 +70,42 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { init_loc_map: LocationMap::new(body), init_path_map, }, + filter, } } +} - fn new_move_path( - move_paths: &mut IndexVec<MovePathIndex, MovePath<'tcx>>, - path_map: &mut IndexVec<MovePathIndex, SmallVec<[MoveOutIndex; 4]>>, - init_path_map: &mut IndexVec<MovePathIndex, SmallVec<[InitIndex; 4]>>, - parent: Option<MovePathIndex>, - place: Place<'tcx>, - ) -> MovePathIndex { - let move_path = - move_paths.push(MovePath { next_sibling: None, first_child: None, parent, place }); +fn new_move_path<'tcx>( + move_paths: &mut IndexVec<MovePathIndex, MovePath<'tcx>>, + path_map: &mut IndexVec<MovePathIndex, SmallVec<[MoveOutIndex; 4]>>, + init_path_map: &mut IndexVec<MovePathIndex, SmallVec<[InitIndex; 4]>>, + parent: Option<MovePathIndex>, + place: Place<'tcx>, +) -> MovePathIndex { + let move_path = + move_paths.push(MovePath { next_sibling: None, first_child: None, parent, place }); + + if let Some(parent) = parent { + let next_sibling = mem::replace(&mut move_paths[parent].first_child, Some(move_path)); + move_paths[move_path].next_sibling = next_sibling; + } - if let Some(parent) = parent { - let next_sibling = mem::replace(&mut move_paths[parent].first_child, Some(move_path)); - move_paths[move_path].next_sibling = next_sibling; - } + let path_map_ent = path_map.push(smallvec![]); + assert_eq!(path_map_ent, move_path); - let path_map_ent = path_map.push(smallvec![]); - assert_eq!(path_map_ent, move_path); + let init_path_map_ent = init_path_map.push(smallvec![]); + assert_eq!(init_path_map_ent, move_path); - let init_path_map_ent = init_path_map.push(smallvec![]); - assert_eq!(init_path_map_ent, move_path); + move_path +} - move_path - } +enum MovePathResult { + Path(MovePathIndex), + Union(MovePathIndex), + Error, } -impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { +impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { /// This creates a MovePath for a given place, returning an `MovePathError` /// if that place can't be moved from. /// @@ -98,11 +113,13 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { /// problematic for borrowck. /// /// Maybe we should have separate "borrowck" and "moveck" modes. - fn move_path_for(&mut self, place: Place<'tcx>) -> Result<MovePathIndex, MoveError<'tcx>> { + fn move_path_for(&mut self, place: Place<'tcx>) -> MovePathResult { let data = &mut self.builder.data; debug!("lookup({:?})", place); - let mut base = data.rev_lookup.find_local(place.local); + let Some(mut base) = data.rev_lookup.find_local(place.local) else { + return MovePathResult::Error; + }; // The move path index of the first union that we find. Once this is // some we stop creating child move paths, since moves from unions @@ -118,12 +135,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { match elem { ProjectionElem::Deref => match place_ty.kind() { ty::Ref(..) | ty::RawPtr(..) => { - return Err(MoveError::cannot_move_out_of( - self.loc, - BorrowedContent { - target_place: place_ref.project_deeper(&[elem], tcx), - }, - )); + return MovePathResult::Error; } ty::Adt(adt, _) => { if !adt.is_box() { @@ -159,10 +171,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { ProjectionElem::Field(_, _) => match place_ty.kind() { ty::Adt(adt, _) => { if adt.has_dtor(tcx) { - return Err(MoveError::cannot_move_out_of( - self.loc, - InteriorOfTypeWithDestructor { container_ty: place_ty }, - )); + return MovePathResult::Error; } if adt.is_union() { union_path.get_or_insert(base); @@ -197,33 +206,15 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { match place_ty.kind() { ty::Slice(_) => { - return Err(MoveError::cannot_move_out_of( - self.loc, - InteriorOfSliceOrArray { - ty: place_ty, - is_index: matches!(elem, ProjectionElem::Index(..)), - }, - )); + return MovePathResult::Error; } ty::Array(_, _) => (), _ => bug!("Unexpected type {:#?}", place_ty.is_array()), } } ProjectionElem::Index(_) => match place_ty.kind() { - ty::Array(..) => { - return Err(MoveError::cannot_move_out_of( - self.loc, - InteriorOfSliceOrArray { ty: place_ty, is_index: true }, - )); - } - ty::Slice(_) => { - return Err(MoveError::cannot_move_out_of( - self.loc, - InteriorOfSliceOrArray { - ty: place_ty, - is_index: matches!(elem, ProjectionElem::Index(..)), - }, - )); + ty::Array(..) | ty::Slice(_) => { + return MovePathResult::Error; } _ => bug!("Unexpected type {place_ty:#?}"), }, @@ -235,11 +226,15 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { | ProjectionElem::Subtype(_) | ProjectionElem::Downcast(_, _) => (), } + let elem_ty = PlaceTy::from_ty(place_ty).projection_ty(tcx, elem).ty; + if !(self.builder.filter)(elem_ty) { + return MovePathResult::Error; + } if union_path.is_none() { // inlined from add_move_path because of a borrowck conflict with the iterator base = *data.rev_lookup.projections.entry((base, elem.lift())).or_insert_with(|| { - MoveDataBuilder::new_move_path( + new_move_path( &mut data.move_paths, &mut data.path_map, &mut data.init_path_map, @@ -252,9 +247,9 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { if let Some(base) = union_path { // Move out of union - always move the entire union. - Err(MoveError::UnionMove { path: base }) + MovePathResult::Union(base) } else { - Ok(base) + MovePathResult::Path(base) } } @@ -270,13 +265,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { .. } = self.builder; *rev_lookup.projections.entry((base, elem.lift())).or_insert_with(move || { - MoveDataBuilder::new_move_path( - move_paths, - path_map, - init_path_map, - Some(base), - mk_place(*tcx), - ) + new_move_path(move_paths, path_map, init_path_map, Some(base), mk_place(*tcx)) }) } @@ -287,11 +276,8 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { } } -pub type MoveDat<'tcx> = - Result<MoveData<'tcx>, (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>)>; - -impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { - fn finalize(self) -> MoveDat<'tcx> { +impl<'a, 'tcx, F> MoveDataBuilder<'a, 'tcx, F> { + fn finalize(self) -> MoveData<'tcx> { debug!("{}", { debug!("moves for {:?}:", self.body.span); for (j, mo) in self.data.moves.iter_enumerated() { @@ -304,7 +290,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { "done dumping moves" }); - if self.errors.is_empty() { Ok(self.data) } else { Err((self.data, self.errors)) } + self.data } } @@ -312,8 +298,9 @@ pub(super) fn gather_moves<'tcx>( body: &Body<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, -) -> MoveDat<'tcx> { - let mut builder = MoveDataBuilder::new(body, tcx, param_env); + filter: impl Fn(Ty<'tcx>) -> bool, +) -> MoveData<'tcx> { + let mut builder = MoveDataBuilder::new(body, tcx, param_env, filter); builder.gather_args(); @@ -330,20 +317,20 @@ pub(super) fn gather_moves<'tcx>( builder.finalize() } -impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { +impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { fn gather_args(&mut self) { for arg in self.body.args_iter() { - let path = self.data.rev_lookup.find_local(arg); + if let Some(path) = self.data.rev_lookup.find_local(arg) { + let init = self.data.inits.push(Init { + path, + kind: InitKind::Deep, + location: InitLocation::Argument(arg), + }); - let init = self.data.inits.push(Init { - path, - kind: InitKind::Deep, - location: InitLocation::Argument(arg), - }); + debug!("gather_args: adding init {:?} of {:?} for argument {:?}", init, path, arg); - debug!("gather_args: adding init {:?} of {:?} for argument {:?}", init, path, arg); - - self.data.init_path_map[path].push(init); + self.data.init_path_map[path].push(init); + } } } @@ -358,12 +345,12 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { } } -struct Gatherer<'b, 'a, 'tcx> { - builder: &'b mut MoveDataBuilder<'a, 'tcx>, +struct Gatherer<'b, 'a, 'tcx, F> { + builder: &'b mut MoveDataBuilder<'a, 'tcx, F>, loc: Location, } -impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { +impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { fn gather_statement(&mut self, stmt: &Statement<'tcx>) { match &stmt.kind { StatementKind::Assign(box (place, Rvalue::CopyForDeref(reffed))) => { @@ -546,13 +533,12 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { let base_place = Place { local: place.local, projection: self.builder.tcx.mk_place_elems(base) }; let base_path = match self.move_path_for(base_place) { - Ok(path) => path, - Err(MoveError::UnionMove { path }) => { + MovePathResult::Path(path) => path, + MovePathResult::Union(path) => { self.record_move(place, path); return; } - Err(error @ MoveError::IllegalMove { .. }) => { - self.builder.errors.push((base_place, error)); + MovePathResult::Error => { return; } }; @@ -572,10 +558,10 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { } } else { match self.move_path_for(place) { - Ok(path) | Err(MoveError::UnionMove { path }) => self.record_move(place, path), - Err(error @ MoveError::IllegalMove { .. }) => { - self.builder.errors.push((place, error)); + MovePathResult::Path(path) | MovePathResult::Union(path) => { + self.record_move(place, path) } + MovePathResult::Error => {} }; } } diff --git a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs index 0c7aa6676ec..7ab1a9ed069 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs @@ -1,4 +1,3 @@ -use crate::move_paths::builder::MoveDat; use crate::un_derefer::UnDerefer; use rustc_data_structures::fx::FxHashMap; use rustc_index::{IndexSlice, IndexVec}; @@ -291,7 +290,7 @@ impl Init { /// Tables mapping from a place to its MovePathIndex. #[derive(Debug)] pub struct MovePathLookup<'tcx> { - locals: IndexVec<Local, MovePathIndex>, + locals: IndexVec<Local, Option<MovePathIndex>>, /// projections are made from a base-place and a projection /// elem. The base-place will have a unique MovePathIndex; we use @@ -318,7 +317,9 @@ impl<'tcx> MovePathLookup<'tcx> { // unknown place, but will rather return the nearest available // parent. pub fn find(&self, place: PlaceRef<'tcx>) -> LookupResult { - let mut result = self.find_local(place.local); + let Some(mut result) = self.find_local(place.local) else { + return LookupResult::Parent(None); + }; for (_, elem) in self.un_derefer.iter_projections(place) { if let Some(&subpath) = self.projections.get(&(result, elem.lift())) { @@ -332,7 +333,7 @@ impl<'tcx> MovePathLookup<'tcx> { } #[inline] - pub fn find_local(&self, local: Local) -> MovePathIndex { + pub fn find_local(&self, local: Local) -> Option<MovePathIndex> { self.locals[local] } @@ -340,46 +341,8 @@ impl<'tcx> MovePathLookup<'tcx> { /// `MovePathIndex`es. pub fn iter_locals_enumerated( &self, - ) -> impl DoubleEndedIterator<Item = (Local, MovePathIndex)> + ExactSizeIterator + '_ { - self.locals.iter_enumerated().map(|(l, &idx)| (l, idx)) - } -} - -#[derive(Debug)] -pub struct IllegalMoveOrigin<'tcx> { - pub location: Location, - pub kind: IllegalMoveOriginKind<'tcx>, -} - -#[derive(Debug)] -pub enum IllegalMoveOriginKind<'tcx> { - /// Illegal move due to attempt to move from behind a reference. - BorrowedContent { - /// The place the reference refers to: if erroneous code was trying to - /// move from `(*x).f` this will be `*x`. - target_place: Place<'tcx>, - }, - - /// Illegal move due to attempt to move from field of an ADT that - /// implements `Drop`. Rust maintains invariant that all `Drop` - /// ADT's remain fully-initialized so that user-defined destructor - /// can safely read from all of the ADT's fields. - InteriorOfTypeWithDestructor { container_ty: Ty<'tcx> }, - - /// Illegal move due to attempt to move out of a slice or array. - InteriorOfSliceOrArray { ty: Ty<'tcx>, is_index: bool }, -} - -#[derive(Debug)] -pub enum MoveError<'tcx> { - IllegalMove { cannot_move_out_of: IllegalMoveOrigin<'tcx> }, - UnionMove { path: MovePathIndex }, -} - -impl<'tcx> MoveError<'tcx> { - fn cannot_move_out_of(location: Location, kind: IllegalMoveOriginKind<'tcx>) -> Self { - let origin = IllegalMoveOrigin { location, kind }; - MoveError::IllegalMove { cannot_move_out_of: origin } + ) -> impl DoubleEndedIterator<Item = (Local, MovePathIndex)> + '_ { + self.locals.iter_enumerated().filter_map(|(l, &idx)| Some((l, idx?))) } } @@ -388,8 +351,9 @@ impl<'tcx> MoveData<'tcx> { body: &Body<'tcx>, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, - ) -> MoveDat<'tcx> { - builder::gather_moves(body, tcx, param_env) + filter: impl Fn(Ty<'tcx>) -> bool, + ) -> MoveData<'tcx> { + builder::gather_moves(body, tcx, param_env, filter) } /// For the move path `mpi`, returns the root local variable (if any) that starts the path. diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs index 1ebb59b3a63..d3dce641ba1 100644 --- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs +++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs @@ -34,7 +34,7 @@ impl<'tcx> MirPass<'tcx> for SanityCheck { } let param_env = tcx.param_env(def_id); - let move_data = MoveData::gather_moves(body, tcx, param_env).unwrap(); + let move_data = MoveData::gather_moves(&body, tcx, param_env, |_| true); let mdpe = MoveDataParamEnv { move_data, param_env }; if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_init).is_some() { diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index 04108aeedf6..025d2ddfd4f 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -463,7 +463,19 @@ impl<V: Clone> Clone for State<V> { } } -impl<V: Clone + HasTop + HasBottom> State<V> { +impl<V: Clone> State<V> { + pub fn new(init: V, map: &Map) -> State<V> { + let values = IndexVec::from_elem_n(init, map.value_count); + State(StateData::Reachable(values)) + } + + pub fn all(&self, f: impl Fn(&V) -> bool) -> bool { + match self.0 { + StateData::Unreachable => true, + StateData::Reachable(ref values) => values.iter().all(f), + } + } + pub fn is_reachable(&self) -> bool { matches!(&self.0, StateData::Reachable(_)) } @@ -472,7 +484,10 @@ impl<V: Clone + HasTop + HasBottom> State<V> { self.0 = StateData::Unreachable; } - pub fn flood_all(&mut self) { + pub fn flood_all(&mut self) + where + V: HasTop, + { self.flood_all_with(V::TOP) } @@ -481,28 +496,52 @@ impl<V: Clone + HasTop + HasBottom> State<V> { values.raw.fill(value); } + /// Assign `value` to all places that are contained in `place` or may alias one. pub fn flood_with(&mut self, place: PlaceRef<'_>, map: &Map, value: V) { - let StateData::Reachable(values) = &mut self.0 else { return }; - map.for_each_aliasing_place(place, None, &mut |vi| { - values[vi] = value.clone(); - }); + self.flood_with_tail_elem(place, None, map, value) } - pub fn flood(&mut self, place: PlaceRef<'_>, map: &Map) { + /// Assign `TOP` to all places that are contained in `place` or may alias one. + pub fn flood(&mut self, place: PlaceRef<'_>, map: &Map) + where + V: HasTop, + { self.flood_with(place, map, V::TOP) } + /// Assign `value` to the discriminant of `place` and all places that may alias it. pub fn flood_discr_with(&mut self, place: PlaceRef<'_>, map: &Map, value: V) { - let StateData::Reachable(values) = &mut self.0 else { return }; - map.for_each_aliasing_place(place, Some(TrackElem::Discriminant), &mut |vi| { - values[vi] = value.clone(); - }); + self.flood_with_tail_elem(place, Some(TrackElem::Discriminant), map, value) } - pub fn flood_discr(&mut self, place: PlaceRef<'_>, map: &Map) { + /// Assign `TOP` to the discriminant of `place` and all places that may alias it. + pub fn flood_discr(&mut self, place: PlaceRef<'_>, map: &Map) + where + V: HasTop, + { self.flood_discr_with(place, map, V::TOP) } + /// This method is the most general version of the `flood_*` method. + /// + /// Assign `value` on the given place and all places that may alias it. In particular, when + /// the given place has a variant downcast, we invoke the function on all the other variants. + /// + /// `tail_elem` allows to support discriminants that are not a place in MIR, but that we track + /// as such. + pub fn flood_with_tail_elem( + &mut self, + place: PlaceRef<'_>, + tail_elem: Option<TrackElem>, + map: &Map, + value: V, + ) { + let StateData::Reachable(values) = &mut self.0 else { return }; + map.for_each_aliasing_place(place, tail_elem, &mut |vi| { + values[vi] = value.clone(); + }); + } + /// Low-level method that assigns to a place. /// This does nothing if the place is not tracked. /// @@ -553,7 +592,10 @@ impl<V: Clone + HasTop + HasBottom> State<V> { } /// Helper method to interpret `target = result`. - pub fn assign(&mut self, target: PlaceRef<'_>, result: ValueOrPlace<V>, map: &Map) { + pub fn assign(&mut self, target: PlaceRef<'_>, result: ValueOrPlace<V>, map: &Map) + where + V: HasTop, + { self.flood(target, map); if let Some(target) = map.find(target) { self.insert_idx(target, result, map); @@ -561,36 +603,93 @@ impl<V: Clone + HasTop + HasBottom> State<V> { } /// Helper method for assignments to a discriminant. - pub fn assign_discr(&mut self, target: PlaceRef<'_>, result: ValueOrPlace<V>, map: &Map) { + pub fn assign_discr(&mut self, target: PlaceRef<'_>, result: ValueOrPlace<V>, map: &Map) + where + V: HasTop, + { self.flood_discr(target, map); if let Some(target) = map.find_discr(target) { self.insert_idx(target, result, map); } } + /// Retrieve the value stored for a place, or `None` if it is not tracked. + pub fn try_get(&self, place: PlaceRef<'_>, map: &Map) -> Option<V> { + let place = map.find(place)?; + self.try_get_idx(place, map) + } + + /// Retrieve the discriminant stored for a place, or `None` if it is not tracked. + pub fn try_get_discr(&self, place: PlaceRef<'_>, map: &Map) -> Option<V> { + let place = map.find_discr(place)?; + self.try_get_idx(place, map) + } + + /// Retrieve the slice length stored for a place, or `None` if it is not tracked. + pub fn try_get_len(&self, place: PlaceRef<'_>, map: &Map) -> Option<V> { + let place = map.find_len(place)?; + self.try_get_idx(place, map) + } + + /// Retrieve the value stored for a place index, or `None` if it is not tracked. + pub fn try_get_idx(&self, place: PlaceIndex, map: &Map) -> Option<V> { + match &self.0 { + StateData::Reachable(values) => { + map.places[place].value_index.map(|v| values[v].clone()) + } + StateData::Unreachable => None, + } + } + /// Retrieve the value stored for a place, or ⊤ if it is not tracked. - pub fn get(&self, place: PlaceRef<'_>, map: &Map) -> V { - map.find(place).map(|place| self.get_idx(place, map)).unwrap_or(V::TOP) + /// + /// This method returns ⊥ if the place is tracked and the state is unreachable. + pub fn get(&self, place: PlaceRef<'_>, map: &Map) -> V + where + V: HasBottom + HasTop, + { + match &self.0 { + StateData::Reachable(_) => self.try_get(place, map).unwrap_or(V::TOP), + // Because this is unreachable, we can return any value we want. + StateData::Unreachable => V::BOTTOM, + } } /// Retrieve the value stored for a place, or ⊤ if it is not tracked. - pub fn get_discr(&self, place: PlaceRef<'_>, map: &Map) -> V { - match map.find_discr(place) { - Some(place) => self.get_idx(place, map), - None => V::TOP, + /// + /// This method returns ⊥ the current state is unreachable. + pub fn get_discr(&self, place: PlaceRef<'_>, map: &Map) -> V + where + V: HasBottom + HasTop, + { + match &self.0 { + StateData::Reachable(_) => self.try_get_discr(place, map).unwrap_or(V::TOP), + // Because this is unreachable, we can return any value we want. + StateData::Unreachable => V::BOTTOM, } } /// Retrieve the value stored for a place, or ⊤ if it is not tracked. - pub fn get_len(&self, place: PlaceRef<'_>, map: &Map) -> V { - match map.find_len(place) { - Some(place) => self.get_idx(place, map), - None => V::TOP, + /// + /// This method returns ⊥ the current state is unreachable. + pub fn get_len(&self, place: PlaceRef<'_>, map: &Map) -> V + where + V: HasBottom + HasTop, + { + match &self.0 { + StateData::Reachable(_) => self.try_get_len(place, map).unwrap_or(V::TOP), + // Because this is unreachable, we can return any value we want. + StateData::Unreachable => V::BOTTOM, } } /// Retrieve the value stored for a place index, or ⊤ if it is not tracked. - pub fn get_idx(&self, place: PlaceIndex, map: &Map) -> V { + /// + /// This method returns ⊥ the current state is unreachable. + pub fn get_idx(&self, place: PlaceIndex, map: &Map) -> V + where + V: HasBottom + HasTop, + { match &self.0 { StateData::Reachable(values) => { map.places[place].value_index.map(|v| values[v].clone()).unwrap_or(V::TOP) diff --git a/compiler/rustc_mir_transform/Cargo.toml b/compiler/rustc_mir_transform/Cargo.toml index f1198d9bfd3..9ec0bb4ab94 100644 --- a/compiler/rustc_mir_transform/Cargo.toml +++ b/compiler/rustc_mir_transform/Cargo.toml @@ -11,6 +11,7 @@ smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } tracing = "0.1" either = "1" rustc_ast = { path = "../rustc_ast" } +rustc_arena = { path = "../rustc_arena" } rustc_attr = { path = "../rustc_attr" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_mir_transform/src/cost_checker.rs b/compiler/rustc_mir_transform/src/cost_checker.rs new file mode 100644 index 00000000000..9bb26693cb2 --- /dev/null +++ b/compiler/rustc_mir_transform/src/cost_checker.rs @@ -0,0 +1,98 @@ +use rustc_middle::mir::visit::*; +use rustc_middle::mir::*; +use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt}; + +const INSTR_COST: usize = 5; +const CALL_PENALTY: usize = 25; +const LANDINGPAD_PENALTY: usize = 50; +const RESUME_PENALTY: usize = 45; + +/// Verify that the callee body is compatible with the caller. +#[derive(Clone)] +pub(crate) struct CostChecker<'b, 'tcx> { + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + cost: usize, + callee_body: &'b Body<'tcx>, + instance: Option<ty::Instance<'tcx>>, +} + +impl<'b, 'tcx> CostChecker<'b, 'tcx> { + pub fn new( + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + instance: Option<ty::Instance<'tcx>>, + callee_body: &'b Body<'tcx>, + ) -> CostChecker<'b, 'tcx> { + CostChecker { tcx, param_env, callee_body, instance, cost: 0 } + } + + pub fn cost(&self) -> usize { + self.cost + } + + fn instantiate_ty(&self, v: Ty<'tcx>) -> Ty<'tcx> { + if let Some(instance) = self.instance { + instance.instantiate_mir(self.tcx, ty::EarlyBinder::bind(&v)) + } else { + v + } + } +} + +impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { + fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) { + // Don't count StorageLive/StorageDead in the inlining cost. + match statement.kind { + StatementKind::StorageLive(_) + | StatementKind::StorageDead(_) + | StatementKind::Deinit(_) + | StatementKind::Nop => {} + _ => self.cost += INSTR_COST, + } + } + + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, _: Location) { + let tcx = self.tcx; + match terminator.kind { + TerminatorKind::Drop { ref place, unwind, .. } => { + // If the place doesn't actually need dropping, treat it like a regular goto. + let ty = self.instantiate_ty(place.ty(self.callee_body, tcx).ty); + if ty.needs_drop(tcx, self.param_env) { + self.cost += CALL_PENALTY; + if let UnwindAction::Cleanup(_) = unwind { + self.cost += LANDINGPAD_PENALTY; + } + } else { + self.cost += INSTR_COST; + } + } + TerminatorKind::Call { func: Operand::Constant(ref f), unwind, .. } => { + let fn_ty = self.instantiate_ty(f.const_.ty()); + self.cost += if let ty::FnDef(def_id, _) = *fn_ty.kind() && tcx.is_intrinsic(def_id) { + // Don't give intrinsics the extra penalty for calls + INSTR_COST + } else { + CALL_PENALTY + }; + if let UnwindAction::Cleanup(_) = unwind { + self.cost += LANDINGPAD_PENALTY; + } + } + TerminatorKind::Assert { unwind, .. } => { + self.cost += CALL_PENALTY; + if let UnwindAction::Cleanup(_) = unwind { + self.cost += LANDINGPAD_PENALTY; + } + } + TerminatorKind::UnwindResume => self.cost += RESUME_PENALTY, + TerminatorKind::InlineAsm { unwind, .. } => { + self.cost += INSTR_COST; + if let UnwindAction::Cleanup(_) = unwind { + self.cost += LANDINGPAD_PENALTY; + } + } + _ => self.cost += INSTR_COST, + } + } +} diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs index d18fdaaf22f..59156b2427c 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drops.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs @@ -9,9 +9,9 @@ use rustc_mir_dataflow::elaborate_drops::{elaborate_drop, DropFlagState, Unwind} use rustc_mir_dataflow::elaborate_drops::{DropElaborator, DropFlagMode, DropStyle}; use rustc_mir_dataflow::impls::{MaybeInitializedPlaces, MaybeUninitializedPlaces}; use rustc_mir_dataflow::move_paths::{LookupResult, MoveData, MovePathIndex}; +use rustc_mir_dataflow::on_all_children_bits; use rustc_mir_dataflow::on_lookup_result_bits; use rustc_mir_dataflow::MoveDataParamEnv; -use rustc_mir_dataflow::{on_all_children_bits, on_all_drop_children_bits}; use rustc_mir_dataflow::{Analysis, ResultsCursor}; use rustc_span::Span; use rustc_target::abi::{FieldIdx, VariantIdx}; @@ -54,16 +54,10 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops { let def_id = body.source.def_id(); let param_env = tcx.param_env_reveal_all_normalized(def_id); - let move_data = match MoveData::gather_moves(body, tcx, param_env) { - Ok(move_data) => move_data, - Err((move_data, _)) => { - tcx.sess.delay_span_bug( - body.span, - "No `move_errors` should be allowed in MIR borrowck", - ); - move_data - } - }; + // For types that do not need dropping, the behaviour is trivial. So we only need to track + // init/uninit for types that do need dropping. + let move_data = + MoveData::gather_moves(&body, tcx, param_env, |ty| ty.needs_drop(tcx, param_env)); let elaborate_patch = { let env = MoveDataParamEnv { move_data, param_env }; @@ -178,13 +172,19 @@ impl<'a, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, '_, 'tcx> { let mut some_live = false; let mut some_dead = false; let mut children_count = 0; - on_all_drop_children_bits(self.tcx(), self.body(), self.ctxt.env, path, |child| { - let (live, dead) = self.ctxt.init_data.maybe_live_dead(child); - debug!("elaborate_drop: state({:?}) = {:?}", child, (live, dead)); - some_live |= live; - some_dead |= dead; - children_count += 1; - }); + on_all_children_bits( + self.tcx(), + self.body(), + self.ctxt.move_data(), + path, + |child| { + let (live, dead) = self.ctxt.init_data.maybe_live_dead(child); + debug!("elaborate_drop: state({:?}) = {:?}", child, (live, dead)); + some_live |= live; + some_dead |= dead; + children_count += 1; + }, + ); ((some_live, some_dead), children_count != 1) } }; @@ -296,26 +296,36 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { fn collect_drop_flags(&mut self) { for (bb, data) in self.body.basic_blocks.iter_enumerated() { let terminator = data.terminator(); - let place = match terminator.kind { - TerminatorKind::Drop { ref place, .. } => place, - _ => continue, - }; - - self.init_data.seek_before(self.body.terminator_loc(bb)); + let TerminatorKind::Drop { ref place, .. } = terminator.kind else { continue }; let path = self.move_data().rev_lookup.find(place.as_ref()); debug!("collect_drop_flags: {:?}, place {:?} ({:?})", bb, place, path); - let path = match path { - LookupResult::Exact(e) => e, - LookupResult::Parent(None) => continue, + match path { + LookupResult::Exact(path) => { + self.init_data.seek_before(self.body.terminator_loc(bb)); + on_all_children_bits(self.tcx, self.body, self.move_data(), path, |child| { + let (maybe_live, maybe_dead) = self.init_data.maybe_live_dead(child); + debug!( + "collect_drop_flags: collecting {:?} from {:?}@{:?} - {:?}", + child, + place, + path, + (maybe_live, maybe_dead) + ); + if maybe_live && maybe_dead { + self.create_drop_flag(child, terminator.source_info.span) + } + }); + } + LookupResult::Parent(None) => {} LookupResult::Parent(Some(parent)) => { - let (_maybe_live, maybe_dead) = self.init_data.maybe_live_dead(parent); - if self.body.local_decls[place.local].is_deref_temp() { continue; } + self.init_data.seek_before(self.body.terminator_loc(bb)); + let (_maybe_live, maybe_dead) = self.init_data.maybe_live_dead(parent); if maybe_dead { self.tcx.sess.delay_span_bug( terminator.source_info.span, @@ -324,80 +334,74 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { ), ); } - continue; } }; - - on_all_drop_children_bits(self.tcx, self.body, self.env, path, |child| { - let (maybe_live, maybe_dead) = self.init_data.maybe_live_dead(child); - debug!( - "collect_drop_flags: collecting {:?} from {:?}@{:?} - {:?}", - child, - place, - path, - (maybe_live, maybe_dead) - ); - if maybe_live && maybe_dead { - self.create_drop_flag(child, terminator.source_info.span) - } - }); } } fn elaborate_drops(&mut self) { + // This function should mirror what `collect_drop_flags` does. for (bb, data) in self.body.basic_blocks.iter_enumerated() { - let loc = Location { block: bb, statement_index: data.statements.len() }; let terminator = data.terminator(); + let TerminatorKind::Drop { place, target, unwind, replace } = terminator.kind else { + continue; + }; - match terminator.kind { - TerminatorKind::Drop { place, target, unwind, replace } => { - self.init_data.seek_before(loc); - match self.move_data().rev_lookup.find(place.as_ref()) { - LookupResult::Exact(path) => { - let unwind = if data.is_cleanup { - Unwind::InCleanup - } else { - match unwind { - UnwindAction::Cleanup(cleanup) => Unwind::To(cleanup), - UnwindAction::Continue => Unwind::To(self.patch.resume_block()), - UnwindAction::Unreachable => { - Unwind::To(self.patch.unreachable_cleanup_block()) - } - UnwindAction::Terminate(reason) => { - debug_assert_ne!( - reason, - UnwindTerminateReason::InCleanup, - "we are not in a cleanup block, InCleanup reason should be impossible" - ); - Unwind::To(self.patch.terminate_block(reason)) - } - } - }; - elaborate_drop( - &mut Elaborator { ctxt: self }, - terminator.source_info, - place, - path, - target, - unwind, - bb, - ) + // This place does not need dropping. It does not have an associated move-path, so the + // match below will conservatively keep an unconditional drop. As that drop is useless, + // just remove it here and now. + if !place + .ty(&self.body.local_decls, self.tcx) + .ty + .needs_drop(self.tcx, self.env.param_env) + { + self.patch.patch_terminator(bb, TerminatorKind::Goto { target }); + continue; + } + + let path = self.move_data().rev_lookup.find(place.as_ref()); + match path { + LookupResult::Exact(path) => { + let unwind = match unwind { + _ if data.is_cleanup => Unwind::InCleanup, + UnwindAction::Cleanup(cleanup) => Unwind::To(cleanup), + UnwindAction::Continue => Unwind::To(self.patch.resume_block()), + UnwindAction::Unreachable => { + Unwind::To(self.patch.unreachable_cleanup_block()) } - LookupResult::Parent(..) => { - if !replace { - self.tcx.sess.delay_span_bug( - terminator.source_info.span, - format!("drop of untracked value {bb:?}"), - ); - } - // A drop and replace behind a pointer/array/whatever. - // The borrow checker requires that these locations are initialized before the assignment, - // so we just leave an unconditional drop. - assert!(!data.is_cleanup); + UnwindAction::Terminate(reason) => { + debug_assert_ne!( + reason, + UnwindTerminateReason::InCleanup, + "we are not in a cleanup block, InCleanup reason should be impossible" + ); + Unwind::To(self.patch.terminate_block(reason)) } + }; + self.init_data.seek_before(self.body.terminator_loc(bb)); + elaborate_drop( + &mut Elaborator { ctxt: self }, + terminator.source_info, + place, + path, + target, + unwind, + bb, + ) + } + LookupResult::Parent(None) => {} + LookupResult::Parent(Some(_)) => { + if !replace { + self.tcx.sess.delay_span_bug( + terminator.source_info.span, + format!("drop of untracked value {bb:?}"), + ); } + // A drop and replace behind a pointer/array/whatever. + // The borrow checker requires that these locations are initialized before the assignment, + // so we just leave an unconditional drop. + assert!(!data.is_cleanup); } - _ => continue, } } } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 757b2aeca7b..8b33e00c63c 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -14,6 +14,7 @@ use rustc_session::config::OptLevel; use rustc_target::abi::FieldIdx; use rustc_target::spec::abi::Abi; +use crate::cost_checker::CostChecker; use crate::simplify::{remove_dead_blocks, CfgSimplifier}; use crate::util; use crate::MirPass; @@ -22,11 +23,6 @@ use std::ops::{Range, RangeFrom}; pub(crate) mod cycle; -const INSTR_COST: usize = 5; -const CALL_PENALTY: usize = 25; -const LANDINGPAD_PENALTY: usize = 50; -const RESUME_PENALTY: usize = 45; - const TOP_DOWN_DEPTH_LIMIT: usize = 5; pub struct Inline; @@ -479,13 +475,8 @@ impl<'tcx> Inliner<'tcx> { // FIXME: Give a bonus to functions with only a single caller - let mut checker = CostChecker { - tcx: self.tcx, - param_env: self.param_env, - instance: callsite.callee, - callee_body, - cost: 0, - }; + let mut checker = + CostChecker::new(self.tcx, self.param_env, Some(callsite.callee), callee_body); // Traverse the MIR manually so we can account for the effects of inlining on the CFG. let mut work_list = vec![START_BLOCK]; @@ -530,7 +521,7 @@ impl<'tcx> Inliner<'tcx> { // That attribute is often applied to very large functions that exceed LLVM's (very // generous) inlining threshold. Such functions are very poor MIR inlining candidates. // Always inlining #[inline(always)] functions in MIR, on net, slows down the compiler. - let cost = checker.cost; + let cost = checker.cost(); if cost <= threshold { debug!("INLINING {:?} [cost={} <= threshold={}]", callsite, cost, threshold); Ok(()) @@ -803,81 +794,6 @@ impl<'tcx> Inliner<'tcx> { } } -/// Verify that the callee body is compatible with the caller. -/// -/// This visitor mostly computes the inlining cost, -/// but also needs to verify that types match because of normalization failure. -struct CostChecker<'b, 'tcx> { - tcx: TyCtxt<'tcx>, - param_env: ParamEnv<'tcx>, - cost: usize, - callee_body: &'b Body<'tcx>, - instance: ty::Instance<'tcx>, -} - -impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { - fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) { - // Don't count StorageLive/StorageDead in the inlining cost. - match statement.kind { - StatementKind::StorageLive(_) - | StatementKind::StorageDead(_) - | StatementKind::Deinit(_) - | StatementKind::Nop => {} - _ => self.cost += INSTR_COST, - } - } - - fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, _: Location) { - let tcx = self.tcx; - match terminator.kind { - TerminatorKind::Drop { ref place, unwind, .. } => { - // If the place doesn't actually need dropping, treat it like a regular goto. - let ty = self.instance.instantiate_mir( - tcx, - ty::EarlyBinder::bind(&place.ty(self.callee_body, tcx).ty), - ); - if ty.needs_drop(tcx, self.param_env) { - self.cost += CALL_PENALTY; - if let UnwindAction::Cleanup(_) = unwind { - self.cost += LANDINGPAD_PENALTY; - } - } else { - self.cost += INSTR_COST; - } - } - TerminatorKind::Call { func: Operand::Constant(ref f), unwind, .. } => { - let fn_ty = - self.instance.instantiate_mir(tcx, ty::EarlyBinder::bind(&f.const_.ty())); - self.cost += if let ty::FnDef(def_id, _) = *fn_ty.kind() - && tcx.is_intrinsic(def_id) - { - // Don't give intrinsics the extra penalty for calls - INSTR_COST - } else { - CALL_PENALTY - }; - if let UnwindAction::Cleanup(_) = unwind { - self.cost += LANDINGPAD_PENALTY; - } - } - TerminatorKind::Assert { unwind, .. } => { - self.cost += CALL_PENALTY; - if let UnwindAction::Cleanup(_) = unwind { - self.cost += LANDINGPAD_PENALTY; - } - } - TerminatorKind::UnwindResume => self.cost += RESUME_PENALTY, - TerminatorKind::InlineAsm { unwind, .. } => { - self.cost += INSTR_COST; - if let UnwindAction::Cleanup(_) = unwind { - self.cost += LANDINGPAD_PENALTY; - } - } - _ => self.cost += INSTR_COST, - } - } -} - /** * Integrator. * diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs new file mode 100644 index 00000000000..7b918be4474 --- /dev/null +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -0,0 +1,759 @@ +//! A jump threading optimization. +//! +//! This optimization seeks to replace join-then-switch control flow patterns by straight jumps +//! X = 0 X = 0 +//! ------------\ /-------- ------------ +//! X = 1 X----X SwitchInt(X) => X = 1 +//! ------------/ \-------- ------------ +//! +//! +//! We proceed by walking the cfg backwards starting from each `SwitchInt` terminator, +//! looking for assignments that will turn the `SwitchInt` into a simple `Goto`. +//! +//! The algorithm maintains a set of replacement conditions: +//! - `conditions[place]` contains `Condition { value, polarity: Eq, target }` +//! if assigning `value` to `place` turns the `SwitchInt` into `Goto { target }`. +//! - `conditions[place]` contains `Condition { value, polarity: Ne, target }` +//! if assigning anything different from `value` to `place` turns the `SwitchInt` +//! into `Goto { target }`. +//! +//! In this file, we denote as `place ?= value` the existence of a replacement condition +//! on `place` with given `value`, irrespective of the polarity and target of that +//! replacement condition. +//! +//! We then walk the CFG backwards transforming the set of conditions. +//! When we find a fulfilling assignment, we record a `ThreadingOpportunity`. +//! All `ThreadingOpportunity`s are applied to the body, by duplicating blocks if required. +//! +//! The optimization search can be very heavy, as it performs a DFS on MIR starting from +//! each `SwitchInt` terminator. To manage the complexity, we: +//! - bound the maximum depth by a constant `MAX_BACKTRACK`; +//! - we only traverse `Goto` terminators. +//! +//! We try to avoid creating irreducible control-flow by not threading through a loop header. +//! +//! Likewise, applying the optimisation can create a lot of new MIR, so we bound the instruction +//! cost by `MAX_COST`. + +use rustc_arena::DroplessArena; +use rustc_data_structures::fx::FxHashSet; +use rustc_index::bit_set::BitSet; +use rustc_index::IndexVec; +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::*; +use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt}; +use rustc_mir_dataflow::value_analysis::{Map, PlaceIndex, State, TrackElem}; + +use crate::cost_checker::CostChecker; +use crate::MirPass; + +pub struct JumpThreading; + +const MAX_BACKTRACK: usize = 5; +const MAX_COST: usize = 100; +const MAX_PLACES: usize = 100; + +impl<'tcx> MirPass<'tcx> for JumpThreading { + fn is_enabled(&self, sess: &rustc_session::Session) -> bool { + sess.mir_opt_level() >= 4 + } + + #[instrument(skip_all level = "debug")] + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + let def_id = body.source.def_id(); + debug!(?def_id); + + let param_env = tcx.param_env_reveal_all_normalized(def_id); + let map = Map::new(tcx, body, Some(MAX_PLACES)); + let loop_headers = loop_headers(body); + + let arena = DroplessArena::default(); + let mut finder = TOFinder { + tcx, + param_env, + body, + arena: &arena, + map: &map, + loop_headers: &loop_headers, + opportunities: Vec::new(), + }; + + for (bb, bbdata) in body.basic_blocks.iter_enumerated() { + debug!(?bb, term = ?bbdata.terminator()); + if bbdata.is_cleanup || loop_headers.contains(bb) { + continue; + } + let Some((discr, targets)) = bbdata.terminator().kind.as_switch() else { continue }; + let Some(discr) = discr.place() else { continue }; + debug!(?discr, ?bb); + + let discr_ty = discr.ty(body, tcx).ty; + let Ok(discr_layout) = tcx.layout_of(param_env.and(discr_ty)) else { continue }; + + let Some(discr) = finder.map.find(discr.as_ref()) else { continue }; + debug!(?discr); + + let cost = CostChecker::new(tcx, param_env, None, body); + + let mut state = State::new(ConditionSet::default(), &finder.map); + + let conds = if let Some((value, then, else_)) = targets.as_static_if() { + let Some(value) = ScalarInt::try_from_uint(value, discr_layout.size) else { + continue; + }; + arena.alloc_from_iter([ + Condition { value, polarity: Polarity::Eq, target: then }, + Condition { value, polarity: Polarity::Ne, target: else_ }, + ]) + } else { + arena.alloc_from_iter(targets.iter().filter_map(|(value, target)| { + let value = ScalarInt::try_from_uint(value, discr_layout.size)?; + Some(Condition { value, polarity: Polarity::Eq, target }) + })) + }; + let conds = ConditionSet(conds); + state.insert_value_idx(discr, conds, &finder.map); + + finder.find_opportunity(bb, state, cost, 0); + } + + let opportunities = finder.opportunities; + debug!(?opportunities); + if opportunities.is_empty() { + return; + } + + // Verify that we do not thread through a loop header. + for to in opportunities.iter() { + assert!(to.chain.iter().all(|&block| !loop_headers.contains(block))); + } + OpportunitySet::new(body, opportunities).apply(body); + } +} + +#[derive(Debug)] +struct ThreadingOpportunity { + /// The list of `BasicBlock`s from the one that found the opportunity to the `SwitchInt`. + chain: Vec<BasicBlock>, + /// The `SwitchInt` will be replaced by `Goto { target }`. + target: BasicBlock, +} + +struct TOFinder<'tcx, 'a> { + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + body: &'a Body<'tcx>, + map: &'a Map, + loop_headers: &'a BitSet<BasicBlock>, + /// We use an arena to avoid cloning the slices when cloning `state`. + arena: &'a DroplessArena, + opportunities: Vec<ThreadingOpportunity>, +} + +/// Represent the following statement. If we can prove that the current local is equal/not-equal +/// to `value`, jump to `target`. +#[derive(Copy, Clone, Debug)] +struct Condition { + value: ScalarInt, + polarity: Polarity, + target: BasicBlock, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +enum Polarity { + Ne, + Eq, +} + +impl Condition { + fn matches(&self, value: ScalarInt) -> bool { + (self.value == value) == (self.polarity == Polarity::Eq) + } + + fn inv(mut self) -> Self { + self.polarity = match self.polarity { + Polarity::Eq => Polarity::Ne, + Polarity::Ne => Polarity::Eq, + }; + self + } +} + +#[derive(Copy, Clone, Debug, Default)] +struct ConditionSet<'a>(&'a [Condition]); + +impl<'a> ConditionSet<'a> { + fn iter(self) -> impl Iterator<Item = Condition> + 'a { + self.0.iter().copied() + } + + fn iter_matches(self, value: ScalarInt) -> impl Iterator<Item = Condition> + 'a { + self.iter().filter(move |c| c.matches(value)) + } + + fn map(self, arena: &'a DroplessArena, f: impl Fn(Condition) -> Condition) -> ConditionSet<'a> { + ConditionSet(arena.alloc_from_iter(self.iter().map(f))) + } +} + +impl<'tcx, 'a> TOFinder<'tcx, 'a> { + fn is_empty(&self, state: &State<ConditionSet<'a>>) -> bool { + state.all(|cs| cs.0.is_empty()) + } + + /// Recursion entry point to find threading opportunities. + #[instrument(level = "trace", skip(self, cost), ret)] + fn find_opportunity( + &mut self, + bb: BasicBlock, + mut state: State<ConditionSet<'a>>, + mut cost: CostChecker<'_, 'tcx>, + depth: usize, + ) { + // Do not thread through loop headers. + if self.loop_headers.contains(bb) { + return; + } + + debug!(cost = ?cost.cost()); + for (statement_index, stmt) in + self.body.basic_blocks[bb].statements.iter().enumerate().rev() + { + if self.is_empty(&state) { + return; + } + + cost.visit_statement(stmt, Location { block: bb, statement_index }); + if cost.cost() > MAX_COST { + return; + } + + // Attempt to turn the `current_condition` on `lhs` into a condition on another place. + self.process_statement(bb, stmt, &mut state); + + // When a statement mutates a place, assignments to that place that happen + // above the mutation cannot fulfill a condition. + // _1 = 5 // Whatever happens here, it won't change the result of a `SwitchInt`. + // _1 = 6 + if let Some((lhs, tail)) = self.mutated_statement(stmt) { + state.flood_with_tail_elem(lhs.as_ref(), tail, self.map, ConditionSet::default()); + } + } + + if self.is_empty(&state) || depth >= MAX_BACKTRACK { + return; + } + + let last_non_rec = self.opportunities.len(); + + let predecessors = &self.body.basic_blocks.predecessors()[bb]; + if let &[pred] = &predecessors[..] && bb != START_BLOCK { + let term = self.body.basic_blocks[pred].terminator(); + match term.kind { + TerminatorKind::SwitchInt { ref discr, ref targets } => { + self.process_switch_int(discr, targets, bb, &mut state); + self.find_opportunity(pred, state, cost, depth + 1); + } + _ => self.recurse_through_terminator(pred, &state, &cost, depth), + } + } else { + for &pred in predecessors { + self.recurse_through_terminator(pred, &state, &cost, depth); + } + } + + let new_tos = &mut self.opportunities[last_non_rec..]; + debug!(?new_tos); + + // Try to deduplicate threading opportunities. + if new_tos.len() > 1 + && new_tos.len() == predecessors.len() + && predecessors + .iter() + .zip(new_tos.iter()) + .all(|(&pred, to)| to.chain == &[pred] && to.target == new_tos[0].target) + { + // All predecessors have a threading opportunity, and they all point to the same block. + debug!(?new_tos, "dedup"); + let first = &mut new_tos[0]; + *first = ThreadingOpportunity { chain: vec![bb], target: first.target }; + self.opportunities.truncate(last_non_rec + 1); + return; + } + + for op in self.opportunities[last_non_rec..].iter_mut() { + op.chain.push(bb); + } + } + + /// Extract the mutated place from a statement. + /// + /// This method returns the `Place` so we can flood the state in case of a partial assignment. + /// (_1 as Ok).0 = _5; + /// (_1 as Err).0 = _6; + /// We want to ensure that a `SwitchInt((_1 as Ok).0)` does not see the first assignment, as + /// the value may have been mangled by the second assignment. + /// + /// In case we assign to a discriminant, we return `Some(TrackElem::Discriminant)`, so we can + /// stop at flooding the discriminant, and preserve the variant fields. + /// (_1 as Some).0 = _6; + /// SetDiscriminant(_1, 1); + /// switchInt((_1 as Some).0) + #[instrument(level = "trace", skip(self), ret)] + fn mutated_statement( + &self, + stmt: &Statement<'tcx>, + ) -> Option<(Place<'tcx>, Option<TrackElem>)> { + match stmt.kind { + StatementKind::Assign(box (place, _)) + | StatementKind::Deinit(box place) => Some((place, None)), + StatementKind::SetDiscriminant { box place, variant_index: _ } => { + Some((place, Some(TrackElem::Discriminant))) + } + StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => { + Some((Place::from(local), None)) + } + StatementKind::Retag(..) + | StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(..)) + // copy_nonoverlapping takes pointers and mutated the pointed-to value. + | StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(..)) + | StatementKind::AscribeUserType(..) + | StatementKind::Coverage(..) + | StatementKind::FakeRead(..) + | StatementKind::ConstEvalCounter + | StatementKind::PlaceMention(..) + | StatementKind::Nop => None, + } + } + + #[instrument(level = "trace", skip(self))] + fn process_operand( + &mut self, + bb: BasicBlock, + lhs: PlaceIndex, + rhs: &Operand<'tcx>, + state: &mut State<ConditionSet<'a>>, + ) -> Option<!> { + let register_opportunity = |c: Condition| { + debug!(?bb, ?c.target, "register"); + self.opportunities.push(ThreadingOpportunity { chain: vec![bb], target: c.target }) + }; + + match rhs { + // If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`. + Operand::Constant(constant) => { + let conditions = state.try_get_idx(lhs, self.map)?; + let constant = + constant.const_.normalize(self.tcx, self.param_env).try_to_scalar_int()?; + conditions.iter_matches(constant).for_each(register_opportunity); + } + // Transfer the conditions on the copied rhs. + Operand::Move(rhs) | Operand::Copy(rhs) => { + let rhs = self.map.find(rhs.as_ref())?; + state.insert_place_idx(rhs, lhs, self.map); + } + } + + None + } + + #[instrument(level = "trace", skip(self))] + fn process_statement( + &mut self, + bb: BasicBlock, + stmt: &Statement<'tcx>, + state: &mut State<ConditionSet<'a>>, + ) -> Option<!> { + let register_opportunity = |c: Condition| { + debug!(?bb, ?c.target, "register"); + self.opportunities.push(ThreadingOpportunity { chain: vec![bb], target: c.target }) + }; + + // Below, `lhs` is the return value of `mutated_statement`, + // the place to which `conditions` apply. + + let discriminant_for_variant = |enum_ty: Ty<'tcx>, variant_index| { + let discr = enum_ty.discriminant_for_variant(self.tcx, variant_index)?; + let discr_layout = self.tcx.layout_of(self.param_env.and(discr.ty)).ok()?; + let scalar = ScalarInt::try_from_uint(discr.val, discr_layout.size)?; + Some(Operand::const_from_scalar( + self.tcx, + discr.ty, + scalar.into(), + rustc_span::DUMMY_SP, + )) + }; + + match &stmt.kind { + // If we expect `discriminant(place) ?= A`, + // we have an opportunity if `variant_index ?= A`. + StatementKind::SetDiscriminant { box place, variant_index } => { + let discr_target = self.map.find_discr(place.as_ref())?; + let enum_ty = place.ty(self.body, self.tcx).ty; + let discr = discriminant_for_variant(enum_ty, *variant_index)?; + self.process_operand(bb, discr_target, &discr, state)?; + } + // If we expect `lhs ?= true`, we have an opportunity if we assume `lhs == true`. + StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume( + Operand::Copy(place) | Operand::Move(place), + )) => { + let conditions = state.try_get(place.as_ref(), self.map)?; + conditions.iter_matches(ScalarInt::TRUE).for_each(register_opportunity); + } + StatementKind::Assign(box (lhs_place, rhs)) => { + if let Some(lhs) = self.map.find(lhs_place.as_ref()) { + match rhs { + Rvalue::Use(operand) => self.process_operand(bb, lhs, operand, state)?, + // Transfer the conditions on the copy rhs. + Rvalue::CopyForDeref(rhs) => { + self.process_operand(bb, lhs, &Operand::Copy(*rhs), state)? + } + Rvalue::Discriminant(rhs) => { + let rhs = self.map.find_discr(rhs.as_ref())?; + state.insert_place_idx(rhs, lhs, self.map); + } + // If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`. + Rvalue::Aggregate(box ref kind, ref operands) => { + let agg_ty = lhs_place.ty(self.body, self.tcx).ty; + let lhs = match kind { + // Do not support unions. + AggregateKind::Adt(.., Some(_)) => return None, + AggregateKind::Adt(_, variant_index, ..) if agg_ty.is_enum() => { + if let Some(discr_target) = self.map.apply(lhs, TrackElem::Discriminant) + && let Some(discr_value) = discriminant_for_variant(agg_ty, *variant_index) + { + self.process_operand(bb, discr_target, &discr_value, state); + } + self.map.apply(lhs, TrackElem::Variant(*variant_index))? + } + _ => lhs, + }; + for (field_index, operand) in operands.iter_enumerated() { + if let Some(field) = + self.map.apply(lhs, TrackElem::Field(field_index)) + { + self.process_operand(bb, field, operand, state); + } + } + } + // Transfer the conditions on the copy rhs, after inversing polarity. + Rvalue::UnaryOp(UnOp::Not, Operand::Move(place) | Operand::Copy(place)) => { + let conditions = state.try_get_idx(lhs, self.map)?; + let place = self.map.find(place.as_ref())?; + let conds = conditions.map(self.arena, Condition::inv); + state.insert_value_idx(place, conds, self.map); + } + // We expect `lhs ?= A`. We found `lhs = Eq(rhs, B)`. + // Create a condition on `rhs ?= B`. + Rvalue::BinaryOp( + op, + box ( + Operand::Move(place) | Operand::Copy(place), + Operand::Constant(value), + ) + | box ( + Operand::Constant(value), + Operand::Move(place) | Operand::Copy(place), + ), + ) => { + let conditions = state.try_get_idx(lhs, self.map)?; + let place = self.map.find(place.as_ref())?; + let equals = match op { + BinOp::Eq => ScalarInt::TRUE, + BinOp::Ne => ScalarInt::FALSE, + _ => return None, + }; + let value = value + .const_ + .normalize(self.tcx, self.param_env) + .try_to_scalar_int()?; + let conds = conditions.map(self.arena, |c| Condition { + value, + polarity: if c.matches(equals) { + Polarity::Eq + } else { + Polarity::Ne + }, + ..c + }); + state.insert_value_idx(place, conds, self.map); + } + + _ => {} + } + } + } + _ => {} + } + + None + } + + #[instrument(level = "trace", skip(self, cost))] + fn recurse_through_terminator( + &mut self, + bb: BasicBlock, + state: &State<ConditionSet<'a>>, + cost: &CostChecker<'_, 'tcx>, + depth: usize, + ) { + let register_opportunity = |c: Condition| { + debug!(?bb, ?c.target, "register"); + self.opportunities.push(ThreadingOpportunity { chain: vec![bb], target: c.target }) + }; + + let term = self.body.basic_blocks[bb].terminator(); + let place_to_flood = match term.kind { + // We come from a target, so those are not possible. + TerminatorKind::UnwindResume + | TerminatorKind::UnwindTerminate(_) + | TerminatorKind::Return + | TerminatorKind::Unreachable + | TerminatorKind::CoroutineDrop => bug!("{term:?} has no terminators"), + // Disallowed during optimizations. + TerminatorKind::FalseEdge { .. } + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::Yield { .. } => bug!("{term:?} invalid"), + // Cannot reason about inline asm. + TerminatorKind::InlineAsm { .. } => return, + // `SwitchInt` is handled specially. + TerminatorKind::SwitchInt { .. } => return, + // We can recurse, no thing particular to do. + TerminatorKind::Goto { .. } => None, + // Flood the overwritten place, and progress through. + TerminatorKind::Drop { place: destination, .. } + | TerminatorKind::Call { destination, .. } => Some(destination), + // Treat as an `assume(cond == expected)`. + TerminatorKind::Assert { ref cond, expected, .. } => { + if let Some(place) = cond.place() + && let Some(conditions) = state.try_get(place.as_ref(), self.map) + { + let expected = if expected { ScalarInt::TRUE } else { ScalarInt::FALSE }; + conditions.iter_matches(expected).for_each(register_opportunity); + } + None + } + }; + + // We can recurse through this terminator. + let mut state = state.clone(); + if let Some(place_to_flood) = place_to_flood { + state.flood_with(place_to_flood.as_ref(), self.map, ConditionSet::default()); + } + self.find_opportunity(bb, state, cost.clone(), depth + 1); + } + + #[instrument(level = "trace", skip(self))] + fn process_switch_int( + &mut self, + discr: &Operand<'tcx>, + targets: &SwitchTargets, + target_bb: BasicBlock, + state: &mut State<ConditionSet<'a>>, + ) -> Option<!> { + debug_assert_ne!(target_bb, START_BLOCK); + debug_assert_eq!(self.body.basic_blocks.predecessors()[target_bb].len(), 1); + + let discr = discr.place()?; + let discr_ty = discr.ty(self.body, self.tcx).ty; + let discr_layout = self.tcx.layout_of(self.param_env.and(discr_ty)).ok()?; + let conditions = state.try_get(discr.as_ref(), self.map)?; + + if let Some((value, _)) = targets.iter().find(|&(_, target)| target == target_bb) { + let value = ScalarInt::try_from_uint(value, discr_layout.size)?; + debug_assert_eq!(targets.iter().filter(|&(_, target)| target == target_bb).count(), 1); + + // We are inside `target_bb`. Since we have a single predecessor, we know we passed + // through the `SwitchInt` before arriving here. Therefore, we know that + // `discr == value`. If one condition can be fulfilled by `discr == value`, + // that's an opportunity. + for c in conditions.iter_matches(value) { + debug!(?target_bb, ?c.target, "register"); + self.opportunities.push(ThreadingOpportunity { chain: vec![], target: c.target }); + } + } else if let Some((value, _, else_bb)) = targets.as_static_if() + && target_bb == else_bb + { + let value = ScalarInt::try_from_uint(value, discr_layout.size)?; + + // We only know that `discr != value`. That's much weaker information than + // the equality we had in the previous arm. All we can conclude is that + // the replacement condition `discr != value` can be threaded, and nothing else. + for c in conditions.iter() { + if c.value == value && c.polarity == Polarity::Ne { + debug!(?target_bb, ?c.target, "register"); + self.opportunities + .push(ThreadingOpportunity { chain: vec![], target: c.target }); + } + } + } + + None + } +} + +struct OpportunitySet { + opportunities: Vec<ThreadingOpportunity>, + /// For each bb, give the TOs in which it appears. The pair corresponds to the index + /// in `opportunities` and the index in `ThreadingOpportunity::chain`. + involving_tos: IndexVec<BasicBlock, Vec<(usize, usize)>>, + /// Cache the number of predecessors for each block, as we clear the basic block cache.. + predecessors: IndexVec<BasicBlock, usize>, +} + +impl OpportunitySet { + fn new(body: &Body<'_>, opportunities: Vec<ThreadingOpportunity>) -> OpportunitySet { + let mut involving_tos = IndexVec::from_elem(Vec::new(), &body.basic_blocks); + for (index, to) in opportunities.iter().enumerate() { + for (ibb, &bb) in to.chain.iter().enumerate() { + involving_tos[bb].push((index, ibb)); + } + involving_tos[to.target].push((index, to.chain.len())); + } + let predecessors = predecessor_count(body); + OpportunitySet { opportunities, involving_tos, predecessors } + } + + /// Apply the opportunities on the graph. + fn apply(&mut self, body: &mut Body<'_>) { + for i in 0..self.opportunities.len() { + self.apply_once(i, body); + } + } + + #[instrument(level = "trace", skip(self, body))] + fn apply_once(&mut self, index: usize, body: &mut Body<'_>) { + debug!(?self.predecessors); + debug!(?self.involving_tos); + + // Check that `predecessors` satisfies its invariant. + debug_assert_eq!(self.predecessors, predecessor_count(body)); + + // Remove the TO from the vector to allow modifying the other ones later. + let op = &mut self.opportunities[index]; + debug!(?op); + let op_chain = std::mem::take(&mut op.chain); + let op_target = op.target; + debug_assert_eq!(op_chain.len(), op_chain.iter().collect::<FxHashSet<_>>().len()); + + let Some((current, chain)) = op_chain.split_first() else { return }; + let basic_blocks = body.basic_blocks.as_mut(); + + // Invariant: the control-flow is well-formed at the end of each iteration. + let mut current = *current; + for &succ in chain { + debug!(?current, ?succ); + + // `succ` must be a successor of `current`. If it is not, this means this TO is not + // satisfiable and a previous TO erased this edge, so we bail out. + if basic_blocks[current].terminator().successors().find(|s| *s == succ).is_none() { + debug!("impossible"); + return; + } + + // Fast path: `succ` is only used once, so we can reuse it directly. + if self.predecessors[succ] == 1 { + debug!("single"); + current = succ; + continue; + } + + let new_succ = basic_blocks.push(basic_blocks[succ].clone()); + debug!(?new_succ); + + // Replace `succ` by `new_succ` where it appears. + let mut num_edges = 0; + for s in basic_blocks[current].terminator_mut().successors_mut() { + if *s == succ { + *s = new_succ; + num_edges += 1; + } + } + + // Update predecessors with the new block. + let _new_succ = self.predecessors.push(num_edges); + debug_assert_eq!(new_succ, _new_succ); + self.predecessors[succ] -= num_edges; + self.update_predecessor_count(basic_blocks[new_succ].terminator(), Update::Incr); + + // Replace the `current -> succ` edge by `current -> new_succ` in all the following + // TOs. This is necessary to avoid trying to thread through a non-existing edge. We + // use `involving_tos` here to avoid traversing the full set of TOs on each iteration. + let mut new_involved = Vec::new(); + for &(to_index, in_to_index) in &self.involving_tos[current] { + // That TO has already been applied, do nothing. + if to_index <= index { + continue; + } + + let other_to = &mut self.opportunities[to_index]; + if other_to.chain.get(in_to_index) != Some(¤t) { + continue; + } + let s = other_to.chain.get_mut(in_to_index + 1).unwrap_or(&mut other_to.target); + if *s == succ { + // `other_to` references the `current -> succ` edge, so replace `succ`. + *s = new_succ; + new_involved.push((to_index, in_to_index + 1)); + } + } + + // The TOs that we just updated now reference `new_succ`. Update `involving_tos` + // in case we need to duplicate an edge starting at `new_succ` later. + let _new_succ = self.involving_tos.push(new_involved); + debug_assert_eq!(new_succ, _new_succ); + + current = new_succ; + } + + let current = &mut basic_blocks[current]; + self.update_predecessor_count(current.terminator(), Update::Decr); + current.terminator_mut().kind = TerminatorKind::Goto { target: op_target }; + self.predecessors[op_target] += 1; + } + + fn update_predecessor_count(&mut self, terminator: &Terminator<'_>, incr: Update) { + match incr { + Update::Incr => { + for s in terminator.successors() { + self.predecessors[s] += 1; + } + } + Update::Decr => { + for s in terminator.successors() { + self.predecessors[s] -= 1; + } + } + } + } +} + +fn predecessor_count(body: &Body<'_>) -> IndexVec<BasicBlock, usize> { + let mut predecessors: IndexVec<_, _> = + body.basic_blocks.predecessors().iter().map(|ps| ps.len()).collect(); + predecessors[START_BLOCK] += 1; // Account for the implicit entry edge. + predecessors +} + +enum Update { + Incr, + Decr, +} + +/// Compute the set of loop headers in the given body. We define a loop header as a block which has +/// at least a predecessor which it dominates. This definition is only correct for reducible CFGs. +/// But if the CFG is already irreducible, there is no point in trying much harder. +/// is already irreducible. +fn loop_headers(body: &Body<'_>) -> BitSet<BasicBlock> { + let mut loop_headers = BitSet::new_empty(body.basic_blocks.len()); + let dominators = body.basic_blocks.dominators(); + // Only visit reachable blocks. + for (bb, bbdata) in traversal::preorder(body) { + for succ in bbdata.terminator().successors() { + if dominators.dominates(succ, bb) { + loop_headers.insert(succ); + } + } + } + loop_headers +} diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 6c0aa51795b..9aaa54110bd 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -62,6 +62,7 @@ mod const_prop; mod const_prop_lint; mod copy_prop; mod coroutine; +mod cost_checker; mod coverage; mod cross_crate_inline; mod ctfe_limit; @@ -81,6 +82,7 @@ mod function_item_references; mod gvn; pub mod inline; mod instsimplify; +mod jump_threading; mod large_enums; mod lower_intrinsics; mod lower_slice_len; @@ -571,6 +573,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &dataflow_const_prop::DataflowConstProp, &const_debuginfo::ConstDebugInfo, &o1(simplify_branches::SimplifyConstCondition::AfterConstProp), + &jump_threading::JumpThreading, &early_otherwise_branch::EarlyOtherwiseBranch, &simplify_comparison_integral::SimplifyComparisonIntegral, &dead_store_elimination::DeadStoreElimination, diff --git a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs index 26384974798..87fee2410ec 100644 --- a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs +++ b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs @@ -24,11 +24,8 @@ pub struct RemoveUninitDrops; impl<'tcx> MirPass<'tcx> for RemoveUninitDrops { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let param_env = tcx.param_env(body.source.def_id()); - let Ok(move_data) = MoveData::gather_moves(body, tcx, param_env) else { - // We could continue if there are move errors, but there's not much point since our - // init data isn't complete. - return; - }; + let move_data = + MoveData::gather_moves(&body, tcx, param_env, |ty| ty.needs_drop(tcx, param_env)); let mdpe = MoveDataParamEnv { move_data, param_env }; let mut maybe_inits = MaybeInitializedPlaces::new(tcx, body, &mdpe) diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index c7e92d7b2b2..5ce21e0bbc6 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -332,7 +332,7 @@ impl<D: Deps> DepGraphData<D> { /// - If you need 3+ arguments, use a tuple for the /// `arg` parameter. /// - /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/incremental-compilation.html + /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/queries/incremental-compilation.html #[inline(always)] pub fn with_task<Ctxt: HasDepContext<Deps = D>, A: Debug, R>( &self, diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 7dbbd4c34ea..352bf98d01f 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -59,7 +59,6 @@ struct UnusedImportCheckVisitor<'a, 'b, 'tcx> { base_use_tree: Option<&'a ast::UseTree>, base_id: ast::NodeId, item_span: Span, - base_use_is_pub: bool, } struct ExternCrateToLint { @@ -146,7 +145,6 @@ impl<'a, 'b, 'tcx> Visitor<'a> for UnusedImportCheckVisitor<'a, 'b, 'tcx> { // because this means that they were generated in some fashion by the // compiler and we don't need to consider them. ast::ItemKind::Use(..) if item.span.is_dummy() => return, - ast::ItemKind::Use(..) => self.base_use_is_pub = item.vis.kind.is_pub(), ast::ItemKind::ExternCrate(orig_name) => { self.extern_crate_items.push(ExternCrateToLint { id: item.id, @@ -173,7 +171,7 @@ impl<'a, 'b, 'tcx> Visitor<'a> for UnusedImportCheckVisitor<'a, 'b, 'tcx> { self.base_use_tree = Some(use_tree); } - if self.base_use_is_pub { + if self.r.effective_visibilities.is_exported(self.r.local_def_id(id)) { self.check_import_as_underscore(use_tree, id); return; } @@ -332,7 +330,6 @@ impl Resolver<'_, '_> { base_use_tree: None, base_id: ast::DUMMY_NODE_ID, item_span: DUMMY_SP, - base_use_is_pub: false, }; visit::walk_crate(&mut visitor, krate); diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 2e991b4c0ad..462e7e85c65 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -3463,9 +3463,10 @@ impl DumpMonoStatsFormat { /// `-Zpolonius` values, enabling the borrow checker polonius analysis, and which version: legacy, /// or future prototype. -#[derive(Clone, Copy, PartialEq, Hash, Debug)] +#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)] pub enum Polonius { /// The default value: disabled. + #[default] Off, /// Legacy version, using datalog and the `polonius-engine` crate. Historical value for `-Zpolonius`. @@ -3475,12 +3476,6 @@ pub enum Polonius { Next, } -impl Default for Polonius { - fn default() -> Self { - Polonius::Off - } -} - impl Polonius { /// Returns whether the legacy version of polonius is enabled pub fn is_legacy_enabled(&self) -> bool { diff --git a/compiler/rustc_smir/Cargo.toml b/compiler/rustc_smir/Cargo.toml index 2b77044d6bf..41c7d3a8594 100644 --- a/compiler/rustc_smir/Cargo.toml +++ b/compiler/rustc_smir/Cargo.toml @@ -5,9 +5,7 @@ edition = "2021" [dependencies] rustc_data_structures = { path = "../rustc_data_structures" } -rustc_driver = { path = "../rustc_driver" } rustc_hir = { path = "../rustc_hir" } -rustc_interface = { path = "../rustc_interface" } rustc_middle = { path = "../rustc_middle" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index b1ea5e898b8..f7e519570fa 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -3,22 +3,18 @@ //! For that, we define APIs that will temporarily be public to 3P that exposes rustc internal APIs //! until stable MIR is complete. -use crate::rustc_internal; use crate::rustc_smir::Tables; use rustc_data_structures::fx; use rustc_data_structures::fx::FxIndexMap; -use rustc_driver::{Callbacks, Compilation, RunCompiler}; -use rustc_interface::{interface, Queries}; use rustc_middle::mir::interpret::AllocId; use rustc_middle::ty; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::{CrateNum, DefId}; use rustc_span::Span; use stable_mir::ty::IndexedVal; -use stable_mir::CompilerError; use std::fmt::Debug; use std::hash::Hash; -use std::ops::{ControlFlow, Index}; +use std::ops::Index; mod internal; @@ -143,63 +139,81 @@ pub fn run(tcx: TyCtxt<'_>, f: impl FnOnce()) { ); } -pub struct StableMir<B = (), C = ()> -where - B: Send, - C: Send, -{ - args: Vec<String>, - callback: fn(TyCtxt<'_>) -> ControlFlow<B, C>, - result: Option<ControlFlow<B, C>>, -} +#[macro_export] +macro_rules! run { + ($args:expr, $callback:expr) => { + run!($args, tcx, $callback) + }; + ($args:expr, $tcx:ident, $callback:expr) => {{ + use rustc_driver::{Callbacks, Compilation, RunCompiler}; + use rustc_interface::{interface, Queries}; + use stable_mir::CompilerError; + use std::ops::ControlFlow; + + pub struct StableMir<B = (), C = ()> + where + B: Send, + C: Send, + { + args: Vec<String>, + callback: fn(TyCtxt<'_>) -> ControlFlow<B, C>, + result: Option<ControlFlow<B, C>>, + } -impl<B, C> StableMir<B, C> -where - B: Send, - C: Send, -{ - /// Creates a new `StableMir` instance, with given test_function and arguments. - pub fn new(args: Vec<String>, callback: fn(TyCtxt<'_>) -> ControlFlow<B, C>) -> Self { - StableMir { args, callback, result: None } - } - - /// Runs the compiler against given target and tests it with `test_function` - pub fn run(&mut self) -> Result<C, CompilerError<B>> { - let compiler_result = - rustc_driver::catch_fatal_errors(|| RunCompiler::new(&self.args.clone(), self).run()); - match (compiler_result, self.result.take()) { - (Ok(Ok(())), Some(ControlFlow::Continue(value))) => Ok(value), - (Ok(Ok(())), Some(ControlFlow::Break(value))) => Err(CompilerError::Interrupted(value)), - (Ok(Ok(_)), None) => Err(CompilerError::Skipped), - (Ok(Err(_)), _) => Err(CompilerError::CompilationFailed), - (Err(_), _) => Err(CompilerError::ICE), + impl<B, C> StableMir<B, C> + where + B: Send, + C: Send, + { + /// Creates a new `StableMir` instance, with given test_function and arguments. + pub fn new(args: Vec<String>, callback: fn(TyCtxt<'_>) -> ControlFlow<B, C>) -> Self { + StableMir { args, callback, result: None } + } + + /// Runs the compiler against given target and tests it with `test_function` + pub fn run(&mut self) -> Result<C, CompilerError<B>> { + let compiler_result = rustc_driver::catch_fatal_errors(|| { + RunCompiler::new(&self.args.clone(), self).run() + }); + match (compiler_result, self.result.take()) { + (Ok(Ok(())), Some(ControlFlow::Continue(value))) => Ok(value), + (Ok(Ok(())), Some(ControlFlow::Break(value))) => { + Err(CompilerError::Interrupted(value)) + } + (Ok(Ok(_)), None) => Err(CompilerError::Skipped), + (Ok(Err(_)), _) => Err(CompilerError::CompilationFailed), + (Err(_), _) => Err(CompilerError::ICE), + } + } } - } -} -impl<B, C> Callbacks for StableMir<B, C> -where - B: Send, - C: Send, -{ - /// Called after analysis. Return value instructs the compiler whether to - /// continue the compilation afterwards (defaults to `Compilation::Continue`) - fn after_analysis<'tcx>( - &mut self, - _compiler: &interface::Compiler, - queries: &'tcx Queries<'tcx>, - ) -> Compilation { - queries.global_ctxt().unwrap().enter(|tcx| { - rustc_internal::run(tcx, || { - self.result = Some((self.callback)(tcx)); - }); - if self.result.as_ref().is_some_and(|val| val.is_continue()) { - Compilation::Continue - } else { - Compilation::Stop + impl<B, C> Callbacks for StableMir<B, C> + where + B: Send, + C: Send, + { + /// Called after analysis. Return value instructs the compiler whether to + /// continue the compilation afterwards (defaults to `Compilation::Continue`) + fn after_analysis<'tcx>( + &mut self, + _compiler: &interface::Compiler, + queries: &'tcx Queries<'tcx>, + ) -> Compilation { + queries.global_ctxt().unwrap().enter(|tcx| { + rustc_internal::run(tcx, || { + self.result = Some((self.callback)(tcx)); + }); + if self.result.as_ref().is_some_and(|val| val.is_continue()) { + Compilation::Continue + } else { + Compilation::Stop + } + }) } - }) - } + } + + StableMir::new($args, |$tcx| $callback).run() + }}; } /// Simmilar to rustc's `FxIndexMap`, `IndexMap` with extra diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 8bb64aa5f12..1a94b7e98fd 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -2247,7 +2247,7 @@ where /// Useful type to use with `Result<>` indicate that an error has already /// been reported to the user, so no need to continue checking. -#[derive(Clone, Copy, Debug, Encodable, Decodable, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] #[derive(HashStable_Generic)] pub struct ErrorGuaranteed(()); @@ -2259,3 +2259,20 @@ impl ErrorGuaranteed { ErrorGuaranteed(()) } } + +impl<E: rustc_serialize::Encoder> Encodable<E> for ErrorGuaranteed { + #[inline] + fn encode(&self, _e: &mut E) { + panic!( + "should never serialize an `ErrorGuaranteed`, as we do not write metadata or incremental caches in case errors occurred" + ) + } +} +impl<D: rustc_serialize::Decoder> Decodable<D> for ErrorGuaranteed { + #[inline] + fn decode(_d: &mut D) -> ErrorGuaranteed { + panic!( + "`ErrorGuaranteed` should never have been serialized to metadata or incremental caches" + ) + } +} diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index 5f22b89ea49..53925eeaaa0 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -199,16 +199,16 @@ struct SymbolPrinter<'tcx> { // `PrettyPrinter` aka pretty printing of e.g. types in paths, // symbol names should have their own printing machinery. -impl<'tcx> Printer<'tcx> for &mut SymbolPrinter<'tcx> { +impl<'tcx> Printer<'tcx> for SymbolPrinter<'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } - fn print_region(self, _region: ty::Region<'_>) -> Result<Self, PrintError> { - Ok(self) + fn print_region(&mut self, _region: ty::Region<'_>) -> Result<(), PrintError> { + Ok(()) } - fn print_type(mut self, ty: Ty<'tcx>) -> Result<Self, PrintError> { + fn print_type(&mut self, ty: Ty<'tcx>) -> Result<(), PrintError> { match *ty.kind() { // Print all nominal types as paths (unlike `pretty_print_type`). ty::FnDef(def_id, args) @@ -220,17 +220,17 @@ impl<'tcx> Printer<'tcx> for &mut SymbolPrinter<'tcx> { // -Zverbose flag, so we cannot reuse it here. ty::Array(ty, size) => { self.write_str("[")?; - self = self.print_type(ty)?; + self.print_type(ty)?; self.write_str("; ")?; if let Some(size) = size.try_to_target_usize(self.tcx()) { write!(self, "{size}")? } else if let ty::ConstKind::Param(param) = size.kind() { - self = param.print(self)? + param.print(self)? } else { self.write_str("_")? } self.write_str("]")?; - Ok(self) + Ok(()) } ty::Alias(ty::Inherent, _) => panic!("unexpected inherent projection"), @@ -240,21 +240,21 @@ impl<'tcx> Printer<'tcx> for &mut SymbolPrinter<'tcx> { } fn print_dyn_existential( - mut self, + &mut self, predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { let mut first = true; for p in predicates { if !first { write!(self, "+")?; } first = false; - self = p.print(self)?; + p.print(self)?; } - Ok(self) + Ok(()) } - fn print_const(self, ct: ty::Const<'tcx>) -> Result<Self, PrintError> { + fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError> { // only print integers match (ct.kind(), ct.ty().kind()) { (ty::ConstKind::Value(ty::ValTree::Leaf(scalar)), ty::Int(_) | ty::Uint(_)) => { @@ -269,18 +269,18 @@ impl<'tcx> Printer<'tcx> for &mut SymbolPrinter<'tcx> { } _ => self.write_str("_")?, } - Ok(self) + Ok(()) } - fn path_crate(self, cnum: CrateNum) -> Result<Self, PrintError> { + fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> { self.write_str(self.tcx.crate_name(cnum).as_str())?; - Ok(self) + Ok(()) } fn path_qualified( - self, + &mut self, self_ty: Ty<'tcx>, trait_ref: Option<ty::TraitRef<'tcx>>, - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { // Similar to `pretty_path_qualified`, but for the other // types that are printed as paths (see `print_type` above). match self_ty.kind() { @@ -295,15 +295,15 @@ impl<'tcx> Printer<'tcx> for &mut SymbolPrinter<'tcx> { } fn path_append_impl( - self, - print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>, + &mut self, + print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, _disambiguated_data: &DisambiguatedDefPathData, self_ty: Ty<'tcx>, trait_ref: Option<ty::TraitRef<'tcx>>, - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { self.pretty_path_append_impl( - |mut cx| { - cx = print_prefix(cx)?; + |cx| { + print_prefix(cx)?; if cx.keep_within_component { // HACK(eddyb) print the path similarly to how `FmtPrinter` prints it. @@ -312,22 +312,22 @@ impl<'tcx> Printer<'tcx> for &mut SymbolPrinter<'tcx> { cx.path.finalize_pending_component(); } - Ok(cx) + Ok(()) }, self_ty, trait_ref, ) } fn path_append( - mut self, - print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>, + &mut self, + print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, disambiguated_data: &DisambiguatedDefPathData, - ) -> Result<Self, PrintError> { - self = print_prefix(self)?; + ) -> Result<(), PrintError> { + print_prefix(self)?; // Skip `::{{extern}}` blocks and `::{{constructor}}` on tuple/unit structs. if let DefPathData::ForeignMod | DefPathData::Ctor = disambiguated_data.data { - return Ok(self); + return Ok(()); } if self.keep_within_component { @@ -339,14 +339,14 @@ impl<'tcx> Printer<'tcx> for &mut SymbolPrinter<'tcx> { write!(self, "{}", disambiguated_data.data)?; - Ok(self) + Ok(()) } fn path_generic_args( - mut self, - print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>, + &mut self, + print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, args: &[GenericArg<'tcx>], - ) -> Result<Self, PrintError> { - self = print_prefix(self)?; + ) -> Result<(), PrintError> { + print_prefix(self)?; let args = args.iter().cloned().filter(|arg| !matches!(arg.unpack(), GenericArgKind::Lifetime(_))); @@ -354,42 +354,42 @@ impl<'tcx> Printer<'tcx> for &mut SymbolPrinter<'tcx> { if args.clone().next().is_some() { self.generic_delimiters(|cx| cx.comma_sep(args)) } else { - Ok(self) + Ok(()) } } } -impl<'tcx> PrettyPrinter<'tcx> for &mut SymbolPrinter<'tcx> { +impl<'tcx> PrettyPrinter<'tcx> for SymbolPrinter<'tcx> { fn should_print_region(&self, _region: ty::Region<'_>) -> bool { false } - fn comma_sep<T>(mut self, mut elems: impl Iterator<Item = T>) -> Result<Self, PrintError> + fn comma_sep<T>(&mut self, mut elems: impl Iterator<Item = T>) -> Result<(), PrintError> where T: Print<'tcx, Self>, { if let Some(first) = elems.next() { - self = first.print(self)?; + first.print(self)?; for elem in elems { self.write_str(",")?; - self = elem.print(self)?; + elem.print(self)?; } } - Ok(self) + Ok(()) } fn generic_delimiters( - mut self, - f: impl FnOnce(Self) -> Result<Self, PrintError>, - ) -> Result<Self, PrintError> { + &mut self, + f: impl FnOnce(&mut Self) -> Result<(), PrintError>, + ) -> Result<(), PrintError> { write!(self, "<")?; let kept_within_component = mem::replace(&mut self.keep_within_component, true); - self = f(self)?; + f(self)?; self.keep_within_component = kept_within_component; write!(self, ">")?; - Ok(self) + Ok(()) } } diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 89b8b1518b0..ad3d291dfa0 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -30,7 +30,7 @@ pub(super) fn mangle<'tcx>( let args = tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), instance.args); let prefix = "_R"; - let mut cx = &mut SymbolMangler { + let mut cx: SymbolMangler<'_> = SymbolMangler { tcx, start_offset: prefix.len(), paths: FxHashMap::default(), @@ -49,13 +49,13 @@ pub(super) fn mangle<'tcx>( _ => None, }; - cx = if let Some(shim_kind) = shim_kind { + if let Some(shim_kind) = shim_kind { cx.path_append_ns(|cx| cx.print_def_path(def_id, args), 'S', 0, shim_kind).unwrap() } else { cx.print_def_path(def_id, args).unwrap() }; if let Some(instantiating_crate) = instantiating_crate { - cx = cx.print_def_path(instantiating_crate.as_def_id(), &[]).unwrap(); + cx.print_def_path(instantiating_crate.as_def_id(), &[]).unwrap(); } std::mem::take(&mut cx.out) } @@ -65,7 +65,7 @@ pub(super) fn mangle_typeid_for_trait_ref<'tcx>( trait_ref: ty::PolyExistentialTraitRef<'tcx>, ) -> String { // FIXME(flip1995): See comment in `mangle_typeid_for_fnabi`. - let mut cx = &mut SymbolMangler { + let mut cx = SymbolMangler { tcx, start_offset: 0, paths: FxHashMap::default(), @@ -74,7 +74,7 @@ pub(super) fn mangle_typeid_for_trait_ref<'tcx>( binders: vec![], out: String::new(), }; - cx = cx.print_def_path(trait_ref.def_id(), &[]).unwrap(); + cx.print_def_path(trait_ref.def_id(), &[]).unwrap(); std::mem::take(&mut cx.out) } @@ -179,32 +179,32 @@ impl<'tcx> SymbolMangler<'tcx> { self.push(ident); } - fn path_append_ns<'a>( - mut self: &'a mut Self, - print_prefix: impl FnOnce(&'a mut Self) -> Result<&'a mut Self, PrintError>, + fn path_append_ns( + &mut self, + print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, ns: char, disambiguator: u64, name: &str, - ) -> Result<&'a mut Self, PrintError> { + ) -> Result<(), PrintError> { self.push("N"); self.out.push(ns); - self = print_prefix(self)?; + print_prefix(self)?; self.push_disambiguator(disambiguator as u64); self.push_ident(name); - Ok(self) + Ok(()) } - fn print_backref(&mut self, i: usize) -> Result<&mut Self, PrintError> { + fn print_backref(&mut self, i: usize) -> Result<(), PrintError> { self.push("B"); self.push_integer_62((i - self.start_offset) as u64); - Ok(self) + Ok(()) } - fn in_binder<'a, T>( - mut self: &'a mut Self, + fn in_binder<T>( + &mut self, value: &ty::Binder<'tcx, T>, - print_value: impl FnOnce(&'a mut Self, &T) -> Result<&'a mut Self, PrintError>, - ) -> Result<&'a mut Self, PrintError> + print_value: impl FnOnce(&mut Self, &T) -> Result<(), PrintError>, + ) -> Result<(), PrintError> where T: TypeVisitable<TyCtxt<'tcx>>, { @@ -222,45 +222,45 @@ impl<'tcx> SymbolMangler<'tcx> { lifetime_depths.end += lifetimes; self.binders.push(BinderLevel { lifetime_depths }); - self = print_value(self, value.as_ref().skip_binder())?; + print_value(self, value.as_ref().skip_binder())?; self.binders.pop(); - Ok(self) + Ok(()) } } -impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { +impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } fn print_def_path( - mut self, + &mut self, def_id: DefId, args: &'tcx [GenericArg<'tcx>], - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { if let Some(&i) = self.paths.get(&(def_id, args)) { return self.print_backref(i); } let start = self.out.len(); - self = self.default_print_def_path(def_id, args)?; + self.default_print_def_path(def_id, args)?; // Only cache paths that do not refer to an enclosing // binder (which would change depending on context). if !args.iter().any(|k| k.has_escaping_bound_vars()) { self.paths.insert((def_id, args), start); } - Ok(self) + Ok(()) } fn print_impl_path( - mut self, + &mut self, impl_def_id: DefId, args: &'tcx [GenericArg<'tcx>], mut self_ty: Ty<'tcx>, mut impl_trait_ref: Option<ty::TraitRef<'tcx>>, - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { let key = self.tcx.def_key(impl_def_id); let parent_def_id = DefId { index: key.parent.unwrap(), ..impl_def_id }; @@ -288,7 +288,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { // Encode impl generic params if the substitutions contain parameters (implying // polymorphization is enabled) and this isn't an inherent impl. if impl_trait_ref.is_some() && args.iter().any(|a| a.has_non_region_param()) { - self = self.path_generic_args( + self.path_generic_args( |this| { this.path_append_ns( |cx| cx.print_def_path(parent_def_id, &[]), @@ -301,19 +301,19 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { )?; } else { self.push_disambiguator(key.disambiguated_data.disambiguator as u64); - self = self.print_def_path(parent_def_id, &[])?; + self.print_def_path(parent_def_id, &[])?; } - self = self_ty.print(self)?; + self_ty.print(self)?; if let Some(trait_ref) = impl_trait_ref { - self = self.print_def_path(trait_ref.def_id, trait_ref.args)?; + self.print_def_path(trait_ref.def_id, trait_ref.args)?; } - Ok(self) + Ok(()) } - fn print_region(self, region: ty::Region<'_>) -> Result<Self, PrintError> { + fn print_region(&mut self, region: ty::Region<'_>) -> Result<(), PrintError> { let i = match *region { // Erased lifetimes use the index 0, for a // shorter mangling of `L_`. @@ -332,10 +332,10 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { }; self.push("L"); self.push_integer_62(i as u64); - Ok(self) + Ok(()) } - fn print_type(mut self, ty: Ty<'tcx>) -> Result<Self, PrintError> { + fn print_type(&mut self, ty: Ty<'tcx>) -> Result<(), PrintError> { // Basic types, never cached (single-character). let basic_type = match ty.kind() { ty::Bool => "b", @@ -365,7 +365,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { }; if !basic_type.is_empty() { self.push(basic_type); - return Ok(self); + return Ok(()); } if let Some(&i) = self.types.get(&ty) { @@ -391,9 +391,9 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { hir::Mutability::Mut => "Q", }); if !r.is_erased() { - self = r.print(self)?; + r.print(self)?; } - self = ty.print(self)?; + ty.print(self)?; } ty::RawPtr(mt) => { @@ -401,23 +401,23 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { hir::Mutability::Not => "P", hir::Mutability::Mut => "O", }); - self = mt.ty.print(self)?; + mt.ty.print(self)?; } ty::Array(ty, len) => { self.push("A"); - self = ty.print(self)?; - self = self.print_const(len)?; + ty.print(self)?; + self.print_const(len)?; } ty::Slice(ty) => { self.push("S"); - self = ty.print(self)?; + ty.print(self)?; } ty::Tuple(tys) => { self.push("T"); for ty in tys.iter() { - self = ty.print(self)?; + ty.print(self)?; } self.push("E"); } @@ -428,15 +428,15 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { | ty::Alias(ty::Projection | ty::Opaque, ty::AliasTy { def_id, args, .. }) | ty::Closure(def_id, args) | ty::Coroutine(def_id, args, _) => { - self = self.print_def_path(def_id, args)?; + self.print_def_path(def_id, args)?; } ty::Foreign(def_id) => { - self = self.print_def_path(def_id, &[])?; + self.print_def_path(def_id, &[])?; } ty::FnPtr(sig) => { self.push("F"); - self = self.in_binder(&sig, |mut cx, sig| { + self.in_binder(&sig, |cx, sig| { if sig.unsafety == hir::Unsafety::Unsafe { cx.push("U"); } @@ -454,7 +454,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { } } for &ty in sig.inputs() { - cx = ty.print(cx)?; + ty.print(cx)?; } if sig.c_variadic { cx.push("v"); @@ -470,8 +470,8 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { // FIXME(dyn-star): need to update v0 mangling docs ty::DynStar => "D*", }); - self = self.print_dyn_existential(predicates)?; - self = r.print(self)?; + self.print_dyn_existential(predicates)?; + r.print(self)?; } ty::Alias(ty::Inherent, _) => bug!("symbol_names: unexpected inherent projection"), @@ -484,13 +484,13 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { if !ty.has_escaping_bound_vars() { self.types.insert(ty, start); } - Ok(self) + Ok(()) } fn print_dyn_existential( - mut self, + &mut self, predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { // Okay, so this is a bit tricky. Imagine we have a trait object like // `dyn for<'a> Foo<'a, Bar = &'a ()>`. When we mangle this, the // output looks really close to the syntax, where the `Bar = &'a ()` bit @@ -517,7 +517,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { // [<Trait> [{<Projection>}]] [{<Auto>}] // Since any predicates after the first one shouldn't change the binders, // just put them all in the binders of the first. - self = self.in_binder(&predicates[0], |mut cx, _| { + self.in_binder(&predicates[0], |cx, _| { for predicate in predicates.iter() { // It would be nice to be able to validate bound vars here, but // projections can actually include bound vars from super traits @@ -528,30 +528,30 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { // Use a type that can't appear in defaults of type parameters. let dummy_self = Ty::new_fresh(cx.tcx, 0); let trait_ref = trait_ref.with_self_ty(cx.tcx, dummy_self); - cx = cx.print_def_path(trait_ref.def_id, trait_ref.args)?; + cx.print_def_path(trait_ref.def_id, trait_ref.args)?; } ty::ExistentialPredicate::Projection(projection) => { let name = cx.tcx.associated_item(projection.def_id).name; cx.push("p"); cx.push_ident(name.as_str()); - cx = match projection.term.unpack() { + match projection.term.unpack() { ty::TermKind::Ty(ty) => ty.print(cx), ty::TermKind::Const(c) => c.print(cx), }?; } ty::ExistentialPredicate::AutoTrait(def_id) => { - cx = cx.print_def_path(*def_id, &[])?; + cx.print_def_path(*def_id, &[])?; } } } - Ok(cx) + Ok(()) })?; self.push("E"); - Ok(self) + Ok(()) } - fn print_const(mut self, ct: ty::Const<'tcx>) -> Result<Self, PrintError> { + fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError> { // We only mangle a typed value if the const can be evaluated. let ct = ct.normalize(self.tcx, ty::ParamEnv::reveal_all()); match ct.kind() { @@ -570,12 +570,13 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { | ty::ConstKind::Error(_) => { // Never cached (single-character). self.push("p"); - return Ok(self); + return Ok(()); } } if let Some(&i) = self.consts.get(&ct) { - return self.print_backref(i); + self.print_backref(i)?; + return Ok(()); } let start = self.out.len(); @@ -583,7 +584,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { match ty.kind() { ty::Uint(_) | ty::Int(_) | ty::Bool | ty::Char => { - self = ty.print(self)?; + ty.print(self)?; let mut bits = ct.eval_bits(self.tcx, ty::ParamEnv::reveal_all()); @@ -645,7 +646,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { .ty; // FIXME(const_generics): add an assert that we only do this for valtrees. let dereferenced_const = self.tcx.mk_ct_from_kind(ct.kind(), pointee_ty); - self = dereferenced_const.print(self)?; + dereferenced_const.print(self)?; } } } @@ -654,22 +655,22 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { let contents = self.tcx.destructure_const(ct); let fields = contents.fields.iter().copied(); - let print_field_list = |mut this: Self| { + let print_field_list = |this: &mut Self| { for field in fields.clone() { - this = field.print(this)?; + field.print(this)?; } this.push("E"); - Ok(this) + Ok(()) }; match *ct.ty().kind() { ty::Array(..) | ty::Slice(_) => { self.push("A"); - self = print_field_list(self)?; + print_field_list(self)?; } ty::Tuple(..) => { self.push("T"); - self = print_field_list(self)?; + print_field_list(self)?; } ty::Adt(def, args) => { let variant_idx = @@ -677,7 +678,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { let variant_def = &def.variant(variant_idx); self.push("V"); - self = self.print_def_path(variant_def.def_id, args)?; + self.print_def_path(variant_def.def_id, args)?; match variant_def.ctor_kind() { Some(CtorKind::Const) => { @@ -685,7 +686,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { } Some(CtorKind::Fn) => { self.push("T"); - self = print_field_list(self)?; + print_field_list(self)?; } None => { self.push("S"); @@ -701,7 +702,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { ); self.push_ident(field_name.unwrap_or(kw::Empty).as_str()); - self = field.print(self)?; + field.print(self)?; } self.push("E"); } @@ -720,47 +721,47 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { if !ct.has_escaping_bound_vars() { self.consts.insert(ct, start); } - Ok(self) + Ok(()) } - fn path_crate(self, cnum: CrateNum) -> Result<Self, PrintError> { + fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> { self.push("C"); let stable_crate_id = self.tcx.def_path_hash(cnum.as_def_id()).stable_crate_id(); self.push_disambiguator(stable_crate_id.as_u64()); let name = self.tcx.crate_name(cnum); self.push_ident(name.as_str()); - Ok(self) + Ok(()) } fn path_qualified( - mut self, + &mut self, self_ty: Ty<'tcx>, trait_ref: Option<ty::TraitRef<'tcx>>, - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { assert!(trait_ref.is_some()); let trait_ref = trait_ref.unwrap(); self.push("Y"); - self = self_ty.print(self)?; + self_ty.print(self)?; self.print_def_path(trait_ref.def_id, trait_ref.args) } fn path_append_impl( - self, - _: impl FnOnce(Self) -> Result<Self, PrintError>, + &mut self, + _: impl FnOnce(&mut Self) -> Result<(), PrintError>, _: &DisambiguatedDefPathData, _: Ty<'tcx>, _: Option<ty::TraitRef<'tcx>>, - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { // Inlined into `print_impl_path` unreachable!() } fn path_append( - self, - print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>, + &mut self, + print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, disambiguated_data: &DisambiguatedDefPathData, - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { let ns = match disambiguated_data.data { // Extern block segments can be skipped, names from extern blocks // are effectively living in their parent modules. @@ -797,10 +798,10 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { } fn path_generic_args( - mut self, - print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>, + &mut self, + print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, args: &[GenericArg<'tcx>], - ) -> Result<Self, PrintError> { + ) -> Result<(), PrintError> { // Don't print any regions if they're all erased. let print_regions = args.iter().any(|arg| match arg.unpack() { GenericArgKind::Lifetime(r) => !r.is_erased(), @@ -816,23 +817,23 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { } self.push("I"); - self = print_prefix(self)?; + print_prefix(self)?; for arg in args { match arg.unpack() { GenericArgKind::Lifetime(lt) => { - self = lt.print(self)?; + lt.print(self)?; } GenericArgKind::Type(ty) => { - self = ty.print(self)?; + ty.print(self)?; } GenericArgKind::Const(c) => { self.push("K"); - self = c.print(self)?; + c.print(self)?; } } } self.push("E"); - Ok(self) + Ok(()) } } diff --git a/compiler/rustc_target/src/spec/csky_unknown_linux_gnuabiv2.rs b/compiler/rustc_target/src/spec/csky_unknown_linux_gnuabiv2.rs index 7d03dd26f5d..93becd70869 100644 --- a/compiler/rustc_target/src/spec/csky_unknown_linux_gnuabiv2.rs +++ b/compiler/rustc_target/src/spec/csky_unknown_linux_gnuabiv2.rs @@ -12,7 +12,7 @@ pub fn target() -> Target { options: TargetOptions { abi: "abiv2".into(), features: "+2e3,+3e7,+7e10,+cache,+dsp1e2,+dspe60,+e1,+e2,+edsp,+elrw,+hard-tp,+high-registers,+hwdiv,+mp,+mp1e2,+nvic,+trust".into(), - late_link_args_static: TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-l:libatomic.a"]), + late_link_args: TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-l:libatomic.a"]), max_atomic_width: Some(32), ..super::linux_gnu_base::opts() }, diff --git a/compiler/rustc_target/src/spec/csky_unknown_linux_gnuabiv2hf.rs b/compiler/rustc_target/src/spec/csky_unknown_linux_gnuabiv2hf.rs new file mode 100644 index 00000000000..74518834160 --- /dev/null +++ b/compiler/rustc_target/src/spec/csky_unknown_linux_gnuabiv2hf.rs @@ -0,0 +1,21 @@ +use crate::spec::{Cc, LinkerFlavor, Lld, Target, TargetOptions}; + +// This target is for glibc Linux on Csky + +pub fn target() -> Target { + Target { + //https://github.com/llvm/llvm-project/blob/8b76aea8d8b1b71f6220bc2845abc749f18a19b7/clang/lib/Basic/Targets/CSKY.h + llvm_target: "csky-unknown-linux-gnuabiv2".into(), + pointer_width: 32, + data_layout: "e-m:e-S32-p:32:32-i32:32:32-i64:32:32-f32:32:32-f64:32:32-v64:32:32-v128:32:32-a:0:32-Fi32-n32".into(), + arch: "csky".into(), + options: TargetOptions { + abi: "abiv2hf".into(), + cpu: "ck860fv".into(), + features: "+hard-float,+hard-float-abi,+2e3,+3e7,+7e10,+cache,+dsp1e2,+dspe60,+e1,+e2,+edsp,+elrw,+hard-tp,+high-registers,+hwdiv,+mp,+mp1e2,+nvic,+trust".into(), + late_link_args: TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-l:libatomic.a", "-mhard-float"]), + max_atomic_width: Some(32), + ..super::linux_gnu_base::opts() + }, + } +} diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 16f70cf43b3..f1c7513d885 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1344,6 +1344,7 @@ supported_targets! { ("loongarch64-unknown-linux-gnu", loongarch64_unknown_linux_gnu), ("m68k-unknown-linux-gnu", m68k_unknown_linux_gnu), ("csky-unknown-linux-gnuabiv2", csky_unknown_linux_gnuabiv2), + ("csky-unknown-linux-gnuabiv2hf", csky_unknown_linux_gnuabiv2hf), ("mips-unknown-linux-gnu", mips_unknown_linux_gnu), ("mips64-unknown-linux-gnuabi64", mips64_unknown_linux_gnuabi64), ("mips64el-unknown-linux-gnuabi64", mips64el_unknown_linux_gnuabi64), diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 989c1310b76..79b09db2268 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -20,7 +20,6 @@ use std::ops::ControlFlow; pub use self::infer_ctxt_ext::*; pub use self::type_err_ctxt_ext::*; -pub use rustc_infer::traits::error_reporting::*; // When outputting impl candidates, prefer showing those that are more similar. // diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index b382474213e..1245b4a7756 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -246,14 +246,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if pred_str.len() > 50 { // We don't need to save the type to a file, we will be talking about this type already // in a separate note when we explain the obligation, so it will be available that way. - pred_str = predicate - .print(FmtPrinter::new_with_limit( - self.tcx, - Namespace::TypeNS, - rustc_session::Limit(6), - )) - .unwrap() - .into_buffer(); + let mut cx: FmtPrinter<'_, '_> = + FmtPrinter::new_with_limit(self.tcx, Namespace::TypeNS, rustc_session::Limit(6)); + predicate.print(&mut cx).unwrap(); + pred_str = cx.into_buffer(); } let mut err = struct_span_err!( self.tcx.sess, @@ -1408,17 +1404,15 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { self.maybe_detailed_projection_msg(predicate, normalized_term, expected_term) }) .unwrap_or_else(|| { - with_forced_trimmed_paths!(format!( - "type mismatch resolving `{}`", - self.resolve_vars_if_possible(predicate) - .print(FmtPrinter::new_with_limit( - self.tcx, - Namespace::TypeNS, - rustc_session::Limit(10), - )) - .unwrap() - .into_buffer() - )) + let mut cx = FmtPrinter::new_with_limit( + self.tcx, + Namespace::TypeNS, + rustc_session::Limit(10), + ); + with_forced_trimmed_paths!(format!("type mismatch resolving `{}`", { + self.resolve_vars_if_possible(predicate).print(&mut cx).unwrap(); + cx.into_buffer() + })) }); let mut diag = struct_span_err!(self.tcx.sess, obligation.cause.span, E0271, "{msg}"); @@ -1463,14 +1457,15 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ty.span, with_forced_trimmed_paths!(Cow::from(format!( "type mismatch resolving `{}`", - self.resolve_vars_if_possible(predicate) - .print(FmtPrinter::new_with_limit( + { + let mut cx = FmtPrinter::new_with_limit( self.tcx, Namespace::TypeNS, rustc_session::Limit(5), - )) - .unwrap() - .into_buffer() + ); + self.resolve_vars_if_possible(predicate).print(&mut cx).unwrap(); + cx.into_buffer() + } ))), )), _ => None, diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index ab81d77a268..71007e1f0e0 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -41,11 +41,6 @@ use std::ops::ControlFlow; pub(crate) use self::project::{needs_normalization, BoundVarReplacer, PlaceholderReplacer}; -pub use self::FulfillmentErrorCode::*; -pub use self::ImplSource::*; -pub use self::ObligationCauseCode::*; -pub use self::SelectionError::*; - pub use self::coherence::{add_placeholder_note, orphan_check, overlapping_impls}; pub use self::coherence::{OrphanCheckErr, OverlapResult}; pub use self::engine::{ObligationCtxt, TraitEngineExt}; diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index 47ed4e20edc..67681fb6ec1 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -9,7 +9,7 @@ use rustc_middle::ty::{self, ImplSubject, ToPredicate, Ty, TyCtxt, TypeVisitable use rustc_span::Span; use smallvec::SmallVec; -pub use rustc_infer::traits::{self, util::*}; +pub use rustc_infer::traits::util::*; /////////////////////////////////////////////////////////////////////////// // `TraitAliasExpander` iterator diff --git a/config.example.toml b/config.example.toml index 4f44121410e..66fa91d4bad 100644 --- a/config.example.toml +++ b/config.example.toml @@ -30,7 +30,7 @@ # # If `change-id` does not match the version that is currently running, # `x.py` will prompt you to update it and check the related PR for more details. -change-id = 115898 +change-id = 116998 # ============================================================================= # Tweaking how LLVM is compiled @@ -377,6 +377,9 @@ change-id = 115898 # this is not intended to be used during local development. #metrics = false +# Specify the location of the Android NDK. Used when targeting Android. +#android-ndk = "/path/to/android-ndk-r25b" + # ============================================================================= # General install configuration options # ============================================================================= @@ -756,12 +759,6 @@ change-id = 115898 # it must link to `libgcc_eh.a` to get a working output, and this option have no effect. #llvm-libunwind = 'no' if Linux, 'in-tree' if Fuchsia -# If this target is for Android, this option will be required to specify where -# the NDK for the target lives. This is used to find the C compiler to link and -# build native code. -# See `src/bootstrap/cc_detect.rs` for details. -#android-ndk = <none> (path) - # Build the sanitizer runtimes for this target. # This option will override the same option under [build] section. #sanitizers = build.sanitizers (bool) diff --git a/library/core/src/arch.rs b/library/core/src/arch.rs index fc2a5b89c14..8817ec0777b 100644 --- a/library/core/src/arch.rs +++ b/library/core/src/arch.rs @@ -1,5 +1,6 @@ #![doc = include_str!("../../stdarch/crates/core_arch/src/core_arch_docs.md")] +#[allow(unused_imports)] #[stable(feature = "simd_arch", since = "1.27.0")] pub use crate::core_arch::arch::*; diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 79601c8c19d..280d2219b9e 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -120,8 +120,6 @@ #![deny(unsafe_op_in_unsafe_fn)] #![deny(fuzzy_provenance_casts)] -extern crate test; - mod alloc; mod any; mod array; diff --git a/library/core/tests/num/flt2dec/mod.rs b/library/core/tests/num/flt2dec/mod.rs index 30843cc3dd7..83e2707b57e 100644 --- a/library/core/tests/num/flt2dec/mod.rs +++ b/library/core/tests/num/flt2dec/mod.rs @@ -8,8 +8,6 @@ use core::num::flt2dec::{ }; use core::num::fmt::{Formatted, Part}; -pub use test::Bencher; - mod estimator; mod strategy { mod dragon; diff --git a/library/portable-simd/crates/core_simd/src/mod.rs b/library/portable-simd/crates/core_simd/src/mod.rs index f9891a3b7c1..19426769858 100644 --- a/library/portable-simd/crates/core_simd/src/mod.rs +++ b/library/portable-simd/crates/core_simd/src/mod.rs @@ -35,6 +35,5 @@ pub mod simd { pub use crate::core_simd::masks::*; pub use crate::core_simd::ord::*; pub use crate::core_simd::swizzle::*; - pub use crate::core_simd::swizzle_dyn::*; pub use crate::core_simd::vector::*; } diff --git a/library/std/src/sys/common/mod.rs b/library/std/src/sys/common/mod.rs index 2b8782ddf44..b35c5d30b41 100644 --- a/library/std/src/sys/common/mod.rs +++ b/library/std/src/sys/common/mod.rs @@ -12,6 +12,7 @@ pub mod alloc; pub mod small_c_string; +#[allow(unused_imports)] pub mod thread_local; #[cfg(test)] diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index 01d8217342c..da05620fd16 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -241,6 +241,7 @@ pub unsafe fn cleanup() { #[cfg(target_os = "android")] pub use crate::sys::android::signal; +#[allow(unused_imports)] #[cfg(not(target_os = "android"))] pub use libc::signal; diff --git a/library/std/src/sys/unix/process/mod.rs b/library/std/src/sys/unix/process/mod.rs index 947ef4c8aef..074f0a105e3 100644 --- a/library/std/src/sys/unix/process/mod.rs +++ b/library/std/src/sys/unix/process/mod.rs @@ -1,7 +1,6 @@ pub use self::process_common::{Command, CommandArgs, ExitCode, Stdio, StdioPipes}; pub use self::process_inner::{ExitStatus, ExitStatusError, Process}; pub use crate::ffi::OsString as EnvKey; -pub use crate::sys_common::process::CommandEnvs; #[cfg_attr(any(target_os = "espidf", target_os = "horizon"), allow(unused))] mod process_common; diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs index 1ca11a7f9d7..bac32d9e60e 100644 --- a/library/std/src/sys/unix/process/process_common.rs +++ b/library/std/src/sys/unix/process/process_common.rs @@ -75,6 +75,7 @@ cfg_if::cfg_if! { return 0; } } else { + #[allow(unused_imports)] pub use libc::{sigemptyset, sigaddset}; } } diff --git a/library/std/src/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs index fbf158f56fc..bdf725fbe5a 100644 --- a/library/std/src/sys/unix/rand.rs +++ b/library/std/src/sys/unix/rand.rs @@ -62,18 +62,15 @@ mod imp { unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), libc::GRND_NONBLOCK) } } - #[cfg(any(target_os = "espidf", target_os = "horizon"))] + #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "freebsd"))] fn getrandom(buf: &mut [u8]) -> libc::ssize_t { - unsafe { libc::getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) } - } - - #[cfg(target_os = "freebsd")] - fn getrandom(buf: &mut [u8]) -> libc::ssize_t { - // FIXME: using the above when libary std's libc is updated + #[cfg(not(target_os = "freebsd"))] + use libc::getrandom; + #[cfg(target_os = "freebsd")] extern "C" { fn getrandom( - buffer: *mut libc::c_void, - length: libc::size_t, + buf: *mut libc::c_void, + buflen: libc::size_t, flags: libc::c_uint, ) -> libc::ssize_t; } @@ -236,6 +233,7 @@ mod imp { } } +// FIXME: once the 10.x release becomes the minimum, this can be dropped for simplification. #[cfg(target_os = "netbsd")] mod imp { use crate::ptr; diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index 6ded683aade..22f2b1007ef 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -1066,6 +1066,14 @@ impl DirBuilder { } pub fn readdir(p: &Path) -> io::Result<ReadDir> { + // We push a `*` to the end of the path which cause the empty path to be + // treated as the current directory. So, for consistency with other platforms, + // we explicitly error on the empty path. + if p.as_os_str().is_empty() { + // Return an error code consistent with other ways of opening files. + // E.g. fs::metadata or File::open. + return Err(io::Error::from_raw_os_error(c::ERROR_PATH_NOT_FOUND as i32)); + } let root = p.to_path_buf(); let star = p.join("*"); let path = maybe_verbatim(&star)?; diff --git a/src/bootstrap/bootstrap_test.py b/src/bootstrap/bootstrap_test.py index fefd6b18739..7f16cac7890 100644 --- a/src/bootstrap/bootstrap_test.py +++ b/src/bootstrap/bootstrap_test.py @@ -103,7 +103,7 @@ class GenerateAndParseConfig(unittest.TestCase): """Test that we can serialize and deserialize a config.toml file""" def test_no_args(self): build = serialize_and_parse([]) - self.assertEqual(build.get_toml("change-id"), '115898') + self.assertEqual(build.get_toml("change-id"), '116998') self.assertEqual(build.get_toml("profile"), 'dist') self.assertIsNone(build.get_toml("llvm.download-ci-llvm")) diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index f469dbea6db..bfef3e67240 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -97,20 +97,7 @@ v("llvm-root", None, "set LLVM root") v("llvm-config", None, "set path to llvm-config") v("llvm-filecheck", None, "set path to LLVM's FileCheck utility") v("python", "build.python", "set path to python") -v("android-cross-path", "target.arm-linux-androideabi.android-ndk", - "Android NDK standalone path (deprecated)") -v("i686-linux-android-ndk", "target.i686-linux-android.android-ndk", - "i686-linux-android NDK standalone path") -v("arm-linux-androideabi-ndk", "target.arm-linux-androideabi.android-ndk", - "arm-linux-androideabi NDK standalone path") -v("armv7-linux-androideabi-ndk", "target.armv7-linux-androideabi.android-ndk", - "armv7-linux-androideabi NDK standalone path") -v("thumbv7neon-linux-androideabi-ndk", "target.thumbv7neon-linux-androideabi.android-ndk", - "thumbv7neon-linux-androideabi NDK standalone path") -v("aarch64-linux-android-ndk", "target.aarch64-linux-android.android-ndk", - "aarch64-linux-android NDK standalone path") -v("x86_64-linux-android-ndk", "target.x86_64-linux-android.android-ndk", - "x86_64-linux-android NDK standalone path") +v("android-ndk", "build.android-ndk", "set path to Android NDK") v("musl-root", "target.x86_64-unknown-linux-musl.musl-root", "MUSL root installation directory (deprecated)") v("musl-root-x86_64", "target.x86_64-unknown-linux-musl.musl-root", diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 7ca1bbad968..5b5334b0a55 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -21,7 +21,6 @@ use std::str::FromStr; use crate::core::build_steps::compile::CODEGEN_BACKEND_PREFIX; use crate::core::config::flags::{Color, Flags, Warnings}; use crate::utils::cache::{Interned, INTERNER}; -use crate::utils::cc_detect::{ndk_compiler, Language}; use crate::utils::channel::{self, GitInfo}; use crate::utils::helpers::{exe, output, t}; use build_helper::exit; @@ -142,6 +141,7 @@ pub struct Config { pub color: Color, pub patch_binaries_for_nix: Option<bool>, pub stage0_metadata: Stage0Metadata, + pub android_ndk: Option<PathBuf>, pub stdout_is_tty: bool, pub stderr_is_tty: bool, @@ -521,7 +521,6 @@ pub struct Target { pub ranlib: Option<PathBuf>, pub default_linker: Option<PathBuf>, pub linker: Option<PathBuf>, - pub ndk: Option<PathBuf>, pub sanitizers: Option<bool>, pub profiler: Option<StringOrBool>, pub rpath: Option<bool>, @@ -799,6 +798,7 @@ define_config! { patch_binaries_for_nix: Option<bool> = "patch-binaries-for-nix", // NOTE: only parsed by bootstrap.py, `--feature build-metrics` enables metrics unconditionally metrics: Option<bool> = "metrics", + android_ndk: Option<PathBuf> = "android-ndk", } } @@ -1039,7 +1039,6 @@ define_config! { llvm_has_rust_patches: Option<bool> = "llvm-has-rust-patches", llvm_filecheck: Option<String> = "llvm-filecheck", llvm_libunwind: Option<String> = "llvm-libunwind", - android_ndk: Option<String> = "android-ndk", sanitizers: Option<bool> = "sanitizers", profiler: Option<StringOrBool> = "profiler", rpath: Option<bool> = "rpath", @@ -1320,6 +1319,7 @@ impl Config { config.python = build.python.map(PathBuf::from); config.reuse = build.reuse.map(PathBuf::from); config.submodules = build.submodules; + config.android_ndk = build.android_ndk; set(&mut config.low_priority, build.low_priority); set(&mut config.compiler_docs, build.compiler_docs); set(&mut config.library_docs_private_items, build.library_docs_private_items); @@ -1600,18 +1600,11 @@ impl Config { .llvm_libunwind .as_ref() .map(|v| v.parse().expect("failed to parse rust.llvm-libunwind")); - if let Some(ref s) = cfg.android_ndk { - target.ndk = Some(config.src.join(s)); - } if let Some(s) = cfg.no_std { target.no_std = s; } - target.cc = cfg.cc.map(PathBuf::from).or_else(|| { - target.ndk.as_ref().map(|ndk| ndk_compiler(Language::C, &triple, ndk)) - }); - target.cxx = cfg.cxx.map(PathBuf::from).or_else(|| { - target.ndk.as_ref().map(|ndk| ndk_compiler(Language::CPlusPlus, &triple, ndk)) - }); + target.cc = cfg.cc.map(PathBuf::from); + target.cxx = cfg.cxx.map(PathBuf::from); target.ar = cfg.ar.map(PathBuf::from); target.ranlib = cfg.ranlib.map(PathBuf::from); target.linker = cfg.linker.map(PathBuf::from); diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 97c743074af..705c27bb922 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -78,7 +78,7 @@ const LLD_FILE_NAMES: &[&str] = &["ld.lld", "ld64.lld", "lld-link", "wasm-ld"]; /// /// If you make any major changes (such as adding new values or changing default values), please /// ensure that the associated PR ID is added to the end of this list. -pub const CONFIG_CHANGE_HISTORY: &[usize] = &[115898]; +pub const CONFIG_CHANGE_HISTORY: &[usize] = &[115898, 116998]; /// Extra --check-cfg to add when building /// (Mode restriction, config name, config values (if any)) diff --git a/src/bootstrap/src/utils/cc_detect.rs b/src/bootstrap/src/utils/cc_detect.rs index 3d3f93f7720..52b36ce75f3 100644 --- a/src/bootstrap/src/utils/cc_detect.rs +++ b/src/bootstrap/src/utils/cc_detect.rs @@ -26,7 +26,7 @@ use std::path::{Path, PathBuf}; use std::process::Command; use std::{env, iter}; -use crate::core::config::{Target, TargetSelection}; +use crate::core::config::TargetSelection; use crate::utils::helpers::output; use crate::{Build, CLang, GitRepo}; @@ -107,10 +107,11 @@ pub fn find(build: &Build) { pub fn find_target(build: &Build, target: TargetSelection) { let mut cfg = new_cc_build(build, target); let config = build.config.target_config.get(&target); - if let Some(cc) = config.and_then(|c| c.cc.as_ref()) { + if let Some(cc) = config + .and_then(|c| c.cc.clone()) + .or_else(|| default_compiler(&mut cfg, Language::C, target, build)) + { cfg.compiler(cc); - } else { - set_compiler(&mut cfg, Language::C, target, config, build); } let compiler = cfg.get_compiler(); @@ -127,12 +128,12 @@ pub fn find_target(build: &Build, target: TargetSelection) { // We'll need one anyways if the target triple is also a host triple let mut cfg = new_cc_build(build, target); cfg.cpp(true); - let cxx_configured = if let Some(cxx) = config.and_then(|c| c.cxx.as_ref()) { + let cxx_configured = if let Some(cxx) = config + .and_then(|c| c.cxx.clone()) + .or_else(|| default_compiler(&mut cfg, Language::CPlusPlus, target, build)) + { cfg.compiler(cxx); true - } else if build.hosts.contains(&target) || build.build == target { - set_compiler(&mut cfg, Language::CPlusPlus, target, config, build); - true } else { // Use an auto-detected compiler (or one configured via `CXX_target_triple` env vars). cfg.try_get_compiler().is_ok() @@ -161,22 +162,21 @@ pub fn find_target(build: &Build, target: TargetSelection) { } } -fn set_compiler( +fn default_compiler( cfg: &mut cc::Build, compiler: Language, target: TargetSelection, - config: Option<&Target>, build: &Build, -) { +) -> Option<PathBuf> { match &*target.triple { // When compiling for android we may have the NDK configured in the // config.toml in which case we look there. Otherwise the default // compiler already takes into account the triple in question. - t if t.contains("android") => { - if let Some(ndk) = config.and_then(|c| c.ndk.as_ref()) { - cfg.compiler(ndk_compiler(compiler, &*target.triple, ndk)); - } - } + t if t.contains("android") => build + .config + .android_ndk + .as_ref() + .map(|ndk| ndk_compiler(compiler, &*target.triple, ndk)), // The default gcc version from OpenBSD may be too old, try using egcc, // which is a gcc version from ports, if this is the case. @@ -184,45 +184,48 @@ fn set_compiler( let c = cfg.get_compiler(); let gnu_compiler = compiler.gcc(); if !c.path().ends_with(gnu_compiler) { - return; + return None; } let output = output(c.to_command().arg("--version")); - let i = match output.find(" 4.") { - Some(i) => i, - None => return, - }; + let i = output.find(" 4.")?; match output[i + 3..].chars().next().unwrap() { '0'..='6' => {} - _ => return, + _ => return None, } let alternative = format!("e{gnu_compiler}"); if Command::new(&alternative).output().is_ok() { - cfg.compiler(alternative); + Some(PathBuf::from(alternative)) + } else { + None } } - "mips-unknown-linux-musl" => { + "mips-unknown-linux-musl" if compiler == Language::C => { if cfg.get_compiler().path().to_str() == Some("gcc") { - cfg.compiler("mips-linux-musl-gcc"); + Some(PathBuf::from("mips-linux-musl-gcc")) + } else { + None } } - "mipsel-unknown-linux-musl" => { + "mipsel-unknown-linux-musl" if compiler == Language::C => { if cfg.get_compiler().path().to_str() == Some("gcc") { - cfg.compiler("mipsel-linux-musl-gcc"); + Some(PathBuf::from("mipsel-linux-musl-gcc")) + } else { + None } } - t if t.contains("musl") => { + t if t.contains("musl") && compiler == Language::C => { if let Some(root) = build.musl_root(target) { let guess = root.join("bin/musl-gcc"); - if guess.exists() { - cfg.compiler(guess); - } + if guess.exists() { Some(guess) } else { None } + } else { + None } } - _ => {} + _ => None, } } @@ -243,10 +246,22 @@ pub(crate) fn ndk_compiler(compiler: Language, triple: &str, ndk: &Path) -> Path let api_level = if triple.contains("aarch64") || triple.contains("x86_64") { "21" } else { "19" }; let compiler = format!("{}{}-{}", triple_translated, api_level, compiler.clang()); - ndk.join("bin").join(compiler) + let host_tag = if cfg!(target_os = "macos") { + // The NDK uses universal binaries, so this is correct even on ARM. + "darwin-x86_64" + } else if cfg!(target_os = "windows") { + "windows-x86_64" + } else { + // NDK r25b only has official releases for macOS, Windows and Linux. + // Try the Linux directory everywhere else, on the assumption that the OS has an + // emulation layer that can cope (e.g. BSDs). + "linux-x86_64" + }; + ndk.join("toolchains").join("llvm").join("prebuilt").join(host_tag).join("bin").join(compiler) } /// The target programming language for a native compiler. +#[derive(PartialEq)] pub(crate) enum Language { /// The compiler is targeting C. C, diff --git a/src/ci/docker/host-x86_64/arm-android/Dockerfile b/src/ci/docker/host-x86_64/arm-android/Dockerfile index db11700af28..abca06fb9fb 100644 --- a/src/ci/docker/host-x86_64/arm-android/Dockerfile +++ b/src/ci/docker/host-x86_64/arm-android/Dockerfile @@ -30,7 +30,7 @@ ENV PATH=$PATH:/android/sdk/platform-tools ENV TARGETS=arm-linux-androideabi -ENV RUST_CONFIGURE_ARGS --arm-linux-androideabi-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/ +ENV RUST_CONFIGURE_ARGS --android-ndk=/android/ndk/ ENV SCRIPT python3 ../x.py --stage 2 test --host='' --target $TARGETS diff --git a/src/ci/docker/host-x86_64/dist-android/Dockerfile b/src/ci/docker/host-x86_64/dist-android/Dockerfile index b09b6edb01a..20b72b377ca 100644 --- a/src/ci/docker/host-x86_64/dist-android/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-android/Dockerfile @@ -19,12 +19,7 @@ ENV TARGETS=$TARGETS,x86_64-linux-android ENV RUST_CONFIGURE_ARGS \ --enable-extended \ --enable-profiler \ - --arm-linux-androideabi-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/ \ - --armv7-linux-androideabi-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/ \ - --thumbv7neon-linux-androideabi-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/ \ - --i686-linux-android-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/ \ - --aarch64-linux-android-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/ \ - --x86_64-linux-android-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/ \ + --android-ndk=/android/ndk/ \ --disable-docs ENV SCRIPT python3 ../x.py dist --host='' --target $TARGETS diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 5c663386446..8b4d673d831 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -33,7 +33,7 @@ - [\*-esp-espidf](platform-support/esp-idf.md) - [\*-unknown-fuchsia](platform-support/fuchsia.md) - [\*-kmc-solid_\*](platform-support/kmc-solid.md) - - [csky-unknown-linux-gnuabiv2](platform-support/csky-unknown-linux-gnuabiv2.md) + - [csky-unknown-linux-gnuabiv2\*](platform-support/csky-unknown-linux-gnuabiv2.md) - [loongarch\*-unknown-linux-\*](platform-support/loongarch-linux.md) - [loongarch\*-unknown-none\*](platform-support/loongarch-none.md) - [m68k-unknown-linux-gnu](platform-support/m68k-unknown-linux-gnu.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index db834600b9c..4e55d360430 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -259,7 +259,8 @@ target | std | host | notes `avr-unknown-gnu-atmega328` | * | | AVR. Requires `-Z build-std=core` `bpfeb-unknown-none` | * | | BPF (big endian) `bpfel-unknown-none` | * | | BPF (little endian) -`csky-unknown-linux-gnuabiv2` | ✓ | | C-SKY abiv2 Linux(little endian) +`csky-unknown-linux-gnuabiv2` | ✓ | | C-SKY abiv2 Linux (little endian) +`csky-unknown-linux-gnuabiv2hf` | ✓ | | C-SKY abiv2 Linux, hardfloat (little endian) `hexagon-unknown-linux-musl` | ? | | `i386-apple-ios` | ✓ | | 32-bit x86 iOS [^x86_32-floats-return-ABI] [`i586-pc-nto-qnx700`](platform-support/nto-qnx.md) | * | | 32-bit x86 QNX Neutrino 7.0 RTOS [^x86_32-floats-return-ABI] diff --git a/src/doc/rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md b/src/doc/rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md index e73598be0d9..57c717c182d 100644 --- a/src/doc/rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md +++ b/src/doc/rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md @@ -4,7 +4,14 @@ This target supports [C-SKY](https://github.com/c-sky) CPUs with `abi` v2 and `glibc`. +target | std | host | notes +-------|:---:|:----:|------- +`csky-unknown-linux-gnuabiv2` | ✓ | | C-SKY abiv2 Linux (little endian) +`csky-unknown-linux-gnuabiv2hf` | ✓ | | C-SKY abiv2 Linux, hardfloat (little endian) + +Reference: https://c-sky.github.io/ + https://gitlab.com/c-sky/ ## Target maintainers @@ -13,7 +20,6 @@ https://gitlab.com/c-sky/ ## Requirements - ## Building the target ### Get a C toolchain @@ -28,13 +34,17 @@ The target can be built by enabling it for a `rustc` build, by placing the follo ```toml [build] -target = ["x86_64-unknown-linux-gnu", "csky-unknown-linux-gnuabiv2"] +target = ["x86_64-unknown-linux-gnu", "csky-unknown-linux-gnuabiv2", "csky-unknown-linux-gnuabiv2hf"] stage = 2 [target.csky-unknown-linux-gnuabiv2] # ADJUST THIS PATH TO POINT AT YOUR TOOLCHAIN cc = "${TOOLCHAIN_PATH}/bin/csky-linux-gnuabiv2-gcc" +[target.csky-unknown-linux-gnuabiv2hf] +# ADJUST THIS PATH TO POINT AT YOUR TOOLCHAIN +cc = "${TOOLCHAIN_PATH}/bin/csky-linux-gnuabiv2-gcc" + ### Build ```sh diff --git a/src/etc/gdb_lookup.py b/src/etc/gdb_lookup.py index 8171cb4e9a6..f3ac9c10978 100644 --- a/src/etc/gdb_lookup.py +++ b/src/etc/gdb_lookup.py @@ -1,4 +1,5 @@ import gdb +import gdb.printing import re from gdb_providers import * @@ -9,7 +10,7 @@ _gdb_version_matched = re.search('([0-9]+)\\.([0-9]+)', gdb.VERSION) gdb_version = [int(num) for num in _gdb_version_matched.groups()] if _gdb_version_matched else [] def register_printers(objfile): - objfile.pretty_printers.append(lookup) + objfile.pretty_printers.append(printer) # BACKCOMPAT: rust 1.35 @@ -38,58 +39,80 @@ def check_enum_discriminant(valobj): return True -def lookup(valobj): - rust_type = classify_rust_type(valobj.type) - - if rust_type == RustType.ENUM: - # use enum provider only for GDB <7.12 - if gdb_version[0] < 7 or (gdb_version[0] == 7 and gdb_version[1] < 12): - if check_enum_discriminant(valobj): - return EnumProvider(valobj) - - if rust_type == RustType.STD_STRING: - return StdStringProvider(valobj) - if rust_type == RustType.STD_OS_STRING: - return StdOsStringProvider(valobj) - if rust_type == RustType.STD_STR: - return StdStrProvider(valobj) - if rust_type == RustType.STD_SLICE: - return StdSliceProvider(valobj) - if rust_type == RustType.STD_VEC: - return StdVecProvider(valobj) - if rust_type == RustType.STD_VEC_DEQUE: - return StdVecDequeProvider(valobj) - if rust_type == RustType.STD_BTREE_SET: - return StdBTreeSetProvider(valobj) - if rust_type == RustType.STD_BTREE_MAP: - return StdBTreeMapProvider(valobj) - if rust_type == RustType.STD_HASH_MAP: - if is_hashbrown_hashmap(valobj): - return StdHashMapProvider(valobj) - else: - return StdOldHashMapProvider(valobj) - if rust_type == RustType.STD_HASH_SET: - hash_map = valobj[valobj.type.fields()[0]] - if is_hashbrown_hashmap(hash_map): - return StdHashMapProvider(valobj, show_values=False) - else: - return StdOldHashMapProvider(hash_map, show_values=False) - - if rust_type == RustType.STD_RC: - return StdRcProvider(valobj) - if rust_type == RustType.STD_ARC: - return StdRcProvider(valobj, is_atomic=True) - - if rust_type == RustType.STD_CELL: - return StdCellProvider(valobj) - if rust_type == RustType.STD_REF: - return StdRefProvider(valobj) - if rust_type == RustType.STD_REF_MUT: - return StdRefProvider(valobj) - if rust_type == RustType.STD_REF_CELL: - return StdRefCellProvider(valobj) - - if rust_type == RustType.STD_NONZERO_NUMBER: - return StdNonZeroNumberProvider(valobj) - +# Helper for enum printing that checks the discriminant. Only used in +# older gdb. +def enum_provider(valobj): + if check_enum_discriminant(valobj): + return EnumProvider(valobj) return None + + +# Helper to handle both old and new hash maps. +def hashmap_provider(valobj): + if is_hashbrown_hashmap(valobj): + return StdHashMapProvider(valobj) + else: + return StdOldHashMapProvider(valobj) + + +# Helper to handle both old and new hash sets. +def hashset_provider(valobj): + hash_map = valobj[valobj.type.fields()[0]] + if is_hashbrown_hashmap(hash_map): + return StdHashMapProvider(valobj, show_values=False) + else: + return StdOldHashMapProvider(hash_map, show_values=False) + + +class PrintByRustType(gdb.printing.SubPrettyPrinter): + def __init__(self, name, provider): + super(PrintByRustType, self).__init__(name) + self.provider = provider + + def __call__(self, val): + if self.enabled: + return self.provider(val) + return None + + +class RustPrettyPrinter(gdb.printing.PrettyPrinter): + def __init__(self, name): + super(RustPrettyPrinter, self).__init__(name, []) + self.type_map = {} + + def add(self, rust_type, provider): + # Just use the rust_type as the name. + printer = PrintByRustType(rust_type, provider) + self.type_map[rust_type] = printer + self.subprinters.append(printer) + + def __call__(self, valobj): + rust_type = classify_rust_type(valobj.type) + if rust_type in self.type_map: + return self.type_map[rust_type](valobj) + return None + + +printer = RustPrettyPrinter("rust") +# use enum provider only for GDB <7.12 +if gdb_version[0] < 7 or (gdb_version[0] == 7 and gdb_version[1] < 12): + printer.add(RustType.ENUM, enum_provider) +printer.add(RustType.STD_STRING, StdStringProvider) +printer.add(RustType.STD_OS_STRING, StdOsStringProvider) +printer.add(RustType.STD_STR, StdStrProvider) +printer.add(RustType.STD_SLICE, StdSliceProvider) +printer.add(RustType.STD_VEC, StdVecProvider) +printer.add(RustType.STD_VEC_DEQUE, StdVecDequeProvider) +printer.add(RustType.STD_BTREE_SET, StdBTreeSetProvider) +printer.add(RustType.STD_BTREE_MAP, StdBTreeMapProvider) +printer.add(RustType.STD_HASH_MAP, hashmap_provider) +printer.add(RustType.STD_HASH_SET, hashset_provider) +printer.add(RustType.STD_RC, StdRcProvider) +printer.add(RustType.STD_ARC, lambda valobj: StdRcProvider(valobj, is_atomic=True)) + +printer.add(RustType.STD_CELL, StdCellProvider) +printer.add(RustType.STD_REF, StdRefProvider) +printer.add(RustType.STD_REF_MUT, StdRefProvider) +printer.add(RustType.STD_REF_CELL, StdRefCellProvider) + +printer.add(RustType.STD_NONZERO_NUMBER, StdNonZeroNumberProvider) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index e2b4cc50dd5..9efdcd60117 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -1107,6 +1107,7 @@ so that we can apply CSS-filters to change the arrow color in themes */ white-space: pre-wrap; border-radius: 3px; display: inline; + vertical-align: baseline; } .stab.portability > code { diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index 5153dd26f4a..b768722acf8 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -102,6 +102,7 @@ static TARGETS: &[&str] = &[ "loongarch64-unknown-none-softfloat", "m68k-unknown-linux-gnu", "csky-unknown-linux-gnuabiv2", + "csky-unknown-linux-gnuabiv2hf", "mips-unknown-linux-gnu", "mips-unknown-linux-musl", "mips64-unknown-linux-gnuabi64", diff --git a/src/tools/clippy/tests/ui/crashes/ice-6252.stderr b/src/tools/clippy/tests/ui/crashes/ice-6252.stderr index f929bec9583..30be9dde73c 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-6252.stderr +++ b/src/tools/clippy/tests/ui/crashes/ice-6252.stderr @@ -24,16 +24,6 @@ help: you might be missing a type parameter LL | impl<N, M, VAL> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {} | +++++ -error[E0046]: not all trait items implemented, missing: `VAL` - --> $DIR/ice-6252.rs:11:1 - | -LL | const VAL: T; - | ------------ `VAL` from trait -... -LL | impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `VAL` in implementation - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0046, E0412. -For more information about an error, try `rustc --explain E0046`. +For more information about this error, try `rustc --explain E0412`. diff --git a/src/tools/clippy/tests/ui/enum_glob_use.fixed b/src/tools/clippy/tests/ui/enum_glob_use.fixed index 9044e80268d..3c0db9beb1a 100644 --- a/src/tools/clippy/tests/ui/enum_glob_use.fixed +++ b/src/tools/clippy/tests/ui/enum_glob_use.fixed @@ -19,6 +19,7 @@ mod in_fn_test { } mod blurg { + #[allow(unused_imports)] pub use std::cmp::Ordering::*; // ok, re-export } diff --git a/src/tools/clippy/tests/ui/enum_glob_use.rs b/src/tools/clippy/tests/ui/enum_glob_use.rs index 4f157a97cbc..2538477f797 100644 --- a/src/tools/clippy/tests/ui/enum_glob_use.rs +++ b/src/tools/clippy/tests/ui/enum_glob_use.rs @@ -19,6 +19,7 @@ mod in_fn_test { } mod blurg { + #[allow(unused_imports)] pub use std::cmp::Ordering::*; // ok, re-export } diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 8ce00e00b57..ac270a1f0ba 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2470,7 +2470,7 @@ impl<'test> TestCx<'test> { } CoverageMap => { rustc.arg("-Cinstrument-coverage"); - // These tests only compile to MIR, so they don't need the + // These tests only compile to LLVM IR, so they don't need the // profiler runtime to be present. rustc.arg("-Zno-profiler-runtime"); // Coverage mappings are sensitive to MIR optimizations, and diff --git a/src/tools/miri/tests/utils/mod.rs b/src/tools/miri/tests/utils/mod.rs index 593f82910c6..7b7dc231a50 100644 --- a/src/tools/miri/tests/utils/mod.rs +++ b/src/tools/miri/tests/utils/mod.rs @@ -1,4 +1,5 @@ #![allow(dead_code)] +#![allow(unused_imports)] #[macro_use] mod macros; diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind.rs index 0483adc776f..3ca6bd4cb11 100644 --- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind.rs +++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind.rs @@ -4,7 +4,7 @@ mod generated; #[allow(unreachable_pub)] -pub use self::generated::{SyntaxKind, T}; +pub use self::generated::SyntaxKind; impl From<u16> for SyntaxKind { #[inline] diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs index db5278f89d5..4b589037672 100644 --- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs +++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs @@ -497,4 +497,3 @@ impl SyntaxKind { } #[macro_export] macro_rules ! T { [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [do] => { $ crate :: SyntaxKind :: DO_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [existential] => { $ crate :: SyntaxKind :: EXISTENTIAL_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW } ; [asm] => { $ crate :: SyntaxKind :: ASM_KW } ; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; } -pub use T; diff --git a/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs b/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs index 56227fce9b5..dc6c96343d6 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs @@ -450,7 +450,6 @@ fn generate_syntax_kinds(grammar: KindsSrc<'_>) -> String { [ident] => { $crate::SyntaxKind::IDENT }; [shebang] => { $crate::SyntaxKind::SHEBANG }; } - pub use T; }; sourcegen::add_preamble("sourcegen_ast", sourcegen::reformat(ast.to_string())) diff --git a/src/tools/rustfmt/.github/workflows/check_diff.yml b/src/tools/rustfmt/.github/workflows/check_diff.yml index 8bfb5834519..2f2beb76915 100644 --- a/src/tools/rustfmt/.github/workflows/check_diff.yml +++ b/src/tools/rustfmt/.github/workflows/check_diff.yml @@ -30,4 +30,4 @@ jobs: rustup target add x86_64-unknown-linux-gnu - name: check diff - run: bash ${GITHUB_WORKSPACE}/ci/check_diff.sh ${{ github.event.inputs.clone_url }} ${{ github.event.inputs.branch_name }} ${{ github.event.inputs.commit_hash }} ${{ github.event.inputs.rustfmt_configs }} + run: bash ${GITHUB_WORKSPACE}/ci/check_diff.sh ${{ github.event.inputs.clone_url }} ${{ github.event.inputs.branch_name }} ${{ github.event.inputs.commit_hash || github.event.inputs.branch_name }} ${{ github.event.inputs.rustfmt_configs }} diff --git a/src/tools/rustfmt/CHANGELOG.md b/src/tools/rustfmt/CHANGELOG.md index fbcd0a57f4e..ec4c682d2c4 100644 --- a/src/tools/rustfmt/CHANGELOG.md +++ b/src/tools/rustfmt/CHANGELOG.md @@ -3,6 +3,82 @@ ## [Unreleased] +## [1.7.0] 2023-10-22 + +### Fixed + +- Sometimes when `format_code_in_doc_comments=true` was set some line comments were converted to block comments [#5533](https://github.com/rust-lang/rustfmt/issues/5533) +- rustfmt will no longer remove the braces in match arms when the block has a labeled [#5676](https://github.com/rust-lang/rustfmt/issues/5676) + ```rust + fn main() { + match true { + true => 'a: { + break 'a + } + _ => (), + } + } + ``` +- Calling methods on float literals ending in `.` will now be wrapped in parenthesis. e.g. `0. .to_string()` will be formatted as `(0.).to_string()` [#5791](https://github.com/rust-lang/rustfmt/issues/5791) +- Prevent ICE when formatting empty `macro_rules!` branch [#5730](https://github.com/rust-lang/rustfmt/issues/5730) + ```rust + macro_rules! statement { + () => {;}; + } + ``` +- Prevent ICE when formatting `vec!{}` [#5735](https://github.com/rust-lang/rustfmt/issues/5735) +- Prevent internal trailing whitespace error when formatting an empty `macro_rules!` defintion e.g. `macro_rules! foo {}` [#5882](https://github.com/rust-lang/rustfmt/issues/5882) +- Formatting doc comment lines that start with `.` or `)` won't be treated as ordered markdown lists because `.` or `)` must be preceded by a number to start an ordered markdown list [#5835](https://github.com/rust-lang/rustfmt/pull/5835) +- Add parenthesis around closures when they're used as method receives, don't have a block body, and end with `.` [#4808](https://github.com/rust-lang/rustfmt/issues/4808) + ```rust + fn main() { + || (10.).method(); + (|| ..).method(); + (|| 1..).method(); + } + ``` +- Prevent removing `for<T>` when using the [`#![feature(non_lifetime_binders)]`](https://github.com/rust-lang/rust/issues/108185) [#5721](https://github.com/rust-lang/rustfmt/issues/5721) + ```rust + #![feature(non_lifetime_binders)] + #![allow(incomplete_features)] + + trait Other<U: ?Sized> {} + + trait Trait<U> + where + for<T> U: Other<T> {} + ``` +- Fix various issues with comments in imports [#5852](https://github.com/rust-lang/rustfmt/issues/5852) [#4708](https://github.com/rust-lang/rustfmt/issues/4708) [#3984](https://github.com/rust-lang/rustfmt/issues/3984) +- When setting `version = Two` newlines between where clause bounds will be removed [#5655](https://github.com/rust-lang/rustfmt/issues/5655) + ```rust + fn foo<T>(_: T) + where + T: std::fmt::Debug, + T: std::fmt::Display, + { + } + ``` +- Improve formatting of `let-else` statements that have leading attributes When setting `version = Two` [#5901](https://github.com/rust-lang/rustfmt/issues/5901) +- Prevent comment duplication in expressions wrapped in parenthesis. [#5871](https://github.com/rust-lang/rustfmt/issues/5871) +- Adjust the span derivation used when rewriting const generics. The incorrect span derivation lead to invalid code after reformatting. [#5935](https://github.com/rust-lang/rustfmt/issues/5935) + + +### Changed + +- rustfmt no longer removes explicit `Rust` ABIs. e.g `extern "Rust" fn im_a_rust_fn() {}` [#5701](https://github.com/rust-lang/rustfmt/issues/5701) +- Setting `trailing_semicolon = false` will only remove trailing `;` on the last expression in a block [#5797](https://github.com/rust-lang/rustfmt/issues/5797) +- Update the format of `cargo help fmt` to be more consistent with other standard commands [#5908](https://github.com/rust-lang/rustfmt/pull/5908) + +### Added + +- Users can now set `skip_macro_invocations` in `rustfmt.toml` [#5816](https://github.com/rust-lang/rustfmt/issues/5816) +- Adds initial support for formatting `let-chains`. **`let-chains` are still a nightly feature and their formatting is subject to change** [#5910](https://github.com/rust-lang/rustfmt/pull/5910). Formatting was implemented following the rules outlined in [rust-lang/rust#110568](https://github.com/rust-lang/rust/pull/110568) + +### Misc + +- Support the experimental `dyn*` syntax, enabled by `#![feature(dyn_star)]` [#5542](https://github.com/rust-lang/rustfmt/issues/5542) +- Replace `unicode_categories` dependency with `unicode-properties` [#5864](https://github.com/rust-lang/rustfmt/pull/5864) + ## [1.6.0] 2023-07-02 ### Added @@ -10,7 +86,7 @@ - Support for formatting let-else statements [#5690] - New config option, `single_line_let_else_max_width`, that allows users to configure the maximum length of single line `let-else` statements. `let-else` statements that otherwise meet the requirements to be formatted on a single line will have their divergent`else` block formatted over multiple lines if they exceed this length [#5684] -[#5690]: (https://github.com/rust-lang/rustfmt/pulls/5690) +[#5690]: https://github.com/rust-lang/rustfmt/pull/5690 [#5684]: https://github.com/rust-lang/rustfmt/issues/5684 ## [1.5.3] 2023-06-20 @@ -19,7 +95,7 @@ - When formatting doc comments with `wrap_comments = true` rustfmt will no longer wrap markdown tables [#4210](https://github.com/rust-lang/rustfmt/issues/4210) - Properly handle wrapping comments that include a numbered list in markdown [#5416](https://github.com/rust-lang/rustfmt/issues/5416) -- Properly handle markdown sublists that utilize a `+` [#4041](https://github.com/rust-lang/rustfmt/issues/4210) +- Properly handle markdown sublists that utilize a `+` [#4041](https://github.com/rust-lang/rustfmt/issues/4041) - rustfmt will no longer use shorthand initialization when rewriting a tuple struct even when `use_field_init_shorthand = true` as this leads to code that could no longer compile. Take the following struct as an example `struct MyStruct(u64);`. rustfmt will no longer format `MyStruct { 0: 0 }` as `MyStruct { 0 }` [#5488](https://github.com/rust-lang/rustfmt/issues/5488) - rustfmt no longer panics when formatting an empty code block in a doc comment with `format_code_in_doc_comments = true` [#5234](https://github.com/rust-lang/rustfmt/issues/5234). For example: diff --git a/src/tools/rustfmt/CODE_OF_CONDUCT.md b/src/tools/rustfmt/CODE_OF_CONDUCT.md index d70b2b52aca..2acddfeefdf 100644 --- a/src/tools/rustfmt/CODE_OF_CONDUCT.md +++ b/src/tools/rustfmt/CODE_OF_CONDUCT.md @@ -11,7 +11,7 @@ A version of this document [can be found online](https://www.rust-lang.org/condu * Please be kind and courteous. There's no need to be mean or rude. * Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer. * Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works. -* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We interpret the term "harassment" as including the definition in the <a href="http://citizencodeofconduct.org/">Citizen Code of Conduct</a>; if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups. +* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We interpret the term "harassment" as including the definition in the <a href="https://github.com/stumpsyn/policies/blob/master/citizen_code_of_conduct.md/">Citizen Code of Conduct</a>; if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups. * Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact one of the channel ops or any of the [Rust moderation team][mod_team] immediately. Whether you're a regular contributor or a newcomer, we care about making this community a safe place for you and we've got your back. * Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome. diff --git a/src/tools/rustfmt/Cargo.lock b/src/tools/rustfmt/Cargo.lock index bd28df7a757..8fcefa97489 100644 --- a/src/tools/rustfmt/Cargo.lock +++ b/src/tools/rustfmt/Cargo.lock @@ -23,42 +23,50 @@ dependencies = [ [[package]] name = "anstream" -version = "0.2.6" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "342258dd14006105c2b75ab1bd7543a03bdf0cfc94383303ac212a04939dff6f" +checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c" dependencies = [ "anstyle", "anstyle-parse", + "anstyle-query", "anstyle-wincon", - "concolor-override", - "concolor-query", - "is-terminal", + "colorchoice", "utf8parse", ] [[package]] name = "anstyle" -version = "0.3.5" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23ea9e81bd02e310c216d080f6223c179012256e5151c41db88d12c88a1684d2" +checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46" [[package]] name = "anstyle-parse" -version = "0.1.1" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7d1bb534e9efed14f3e5f44e7dd1a4f709384023a4165199a4241e18dff0116" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" dependencies = [ "utf8parse", ] [[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] name = "anstyle-wincon" -version = "0.2.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3127af6145b149f3287bb9a0d10ad9c5692dba8c53ad48285e5bec4063834fa" +checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" dependencies = [ "anstyle", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -90,11 +98,11 @@ dependencies = [ [[package]] name = "bytecount" -version = "0.6.2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e" +checksum = "ad152d03a2c813c80bb94fedbf3a3f02b28f793e39e7c214c8a0bcc196343de7" dependencies = [ - "packed_simd_2", + "packed_simd", ] [[package]] @@ -130,12 +138,6 @@ dependencies = [ ] [[package]] -name = "cc" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" - -[[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -143,33 +145,41 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.2.1" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046ae530c528f252094e4a77886ee1374437744b2bff1497aa898bbddbbb29b3" +checksum = "6a13b88d2c62ff462f88e4a121f17a82c1af05693a2f192b5c38d14de73c19f6" dependencies = [ "clap_builder", "clap_derive", - "once_cell", +] + +[[package]] +name = "clap-cargo" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "383f21342a464d4af96e9a4cad22a0b4f2880d4a5b3bbf5c9654dd1d9a224ee4" +dependencies = [ + "anstyle", + "clap", ] [[package]] name = "clap_builder" -version = "4.2.1" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "223163f58c9a40c3b0a43e1c4b50a9ce09f007ea2cb1ec258a687945b4b7929f" +checksum = "2bb9faaa7c2ef94b2743a21f5a29e6f0010dff4caa69ac8e9d6cf8b6fa74da08" dependencies = [ "anstream", "anstyle", - "bitflags", "clap_lex", "strsim", ] [[package]] name = "clap_derive" -version = "4.2.0" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" +checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" dependencies = [ "heck", "proc-macro2", @@ -179,24 +189,15 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" +checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" [[package]] -name = "concolor-override" +name = "colorchoice" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a855d4a1978dc52fb0536a04d384c2c0c1aa273597f08b77c8c4d3b2eec6037f" - -[[package]] -name = "concolor-query" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d11d52c3d7ca2e6d0040212be9e4dbbcd78b6447f535b6b561f449427944cf" -dependencies = [ - "windows-sys 0.45.0", -] +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "crossbeam-utils" @@ -262,40 +263,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] -name = "env_logger" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" -dependencies = [ - "humantime", - "is-terminal", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "errno" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -347,18 +314,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" [[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] name = "ignore" version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -387,29 +342,6 @@ dependencies = [ ] [[package]] -name = "io-lifetimes" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" -dependencies = [ - "hermit-abi", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "is-terminal" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" -dependencies = [ - "hermit-abi", - "io-lifetimes", - "rustix", - "windows-sys 0.48.0", -] - -[[package]] name = "itertools" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -438,15 +370,9 @@ checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" [[package]] name = "libm" -version = "0.1.4" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" - -[[package]] -name = "linux-raw-sys" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "log" @@ -458,28 +384,69 @@ dependencies = [ ] [[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata", +] + +[[package]] name = "memchr" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" [[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] name = "once_cell" version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] -name = "packed_simd_2" -version = "0.3.7" +name = "overload" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defdcfef86dcc44ad208f71d9ff4ce28df6537a4e0d6b0e8e845cb8ca10059a6" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "packed_simd" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f9f08af0c877571712e2e3e686ad79efad9657dbf0f7c3c8ba943ff6c38932d" dependencies = [ "cfg-if", - "libm", + "num-traits", ] [[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] name = "proc-macro2" version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -519,9 +486,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.5.5" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" dependencies = [ "aho-corasick", "memchr", @@ -529,10 +496,19 @@ dependencies = [ ] [[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax", +] + +[[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "rustfmt-config_proc_macro" @@ -545,21 +521,20 @@ dependencies = [ [[package]] name = "rustfmt-nightly" -version = "1.6.0" +version = "1.7.0" dependencies = [ "annotate-snippets", "anyhow", "bytecount", "cargo_metadata", "clap", + "clap-cargo", "diff", "dirs", - "env_logger", "getopts", "ignore", "itertools", "lazy_static", - "log", "regex", "rustfmt-config_proc_macro", "serde", @@ -567,23 +542,11 @@ dependencies = [ "term", "thiserror", "toml", + "tracing", + "tracing-subscriber", + "unicode-properties", "unicode-segmentation", "unicode-width", - "unicode_categories", -] - -[[package]] -name = "rustix" -version = "0.37.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77" -dependencies = [ - "bitflags", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys", - "windows-sys 0.48.0", ] [[package]] @@ -657,6 +620,21 @@ dependencies = [ ] [[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -685,15 +663,6 @@ dependencies = [ ] [[package]] -name = "termcolor" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" -dependencies = [ - "winapi-util", -] - -[[package]] name = "thiserror" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -757,28 +726,90 @@ dependencies = [ ] [[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] name = "unicode-ident" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] -name = "unicode-segmentation" -version = "1.9.0" +name = "unicode-properties" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" +checksum = "c7f91c8b21fbbaa18853c3d0801c78f4fc94cdb976699bb03e832e75f7fd22f0" [[package]] -name = "unicode-width" -version = "0.1.9" +name = "unicode-segmentation" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] -name = "unicode_categories" -version = "0.1.1" +name = "unicode-width" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "utf8parse" @@ -787,6 +818,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] name = "walkdir" version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -836,35 +873,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - -[[package]] -name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.0", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets", ] [[package]] @@ -873,95 +886,53 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - -[[package]] -name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" diff --git a/src/tools/rustfmt/Cargo.toml b/src/tools/rustfmt/Cargo.toml index 8c312f47a28..00e0ed37a84 100644 --- a/src/tools/rustfmt/Cargo.toml +++ b/src/tools/rustfmt/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "rustfmt-nightly" -version = "1.6.0" +version = "1.7.0" description = "Tool to find and fix Rust formatting issues" repository = "https://github.com/rust-lang/rustfmt" readme = "README.md" -license = "Apache-2.0/MIT" +license = "Apache-2.0 OR MIT" build = "build.rs" categories = ["development-tools"] edition = "2021" @@ -35,26 +35,27 @@ generic-simd = ["bytecount/generic-simd"] [dependencies] annotate-snippets = { version = "0.9", features = ["color"] } anyhow = "1.0" -bytecount = "0.6" +bytecount = "0.6.4" cargo_metadata = "0.15.4" -clap = { version = "4.2.1", features = ["derive"] } +clap = { version = "4.4.2", features = ["derive"] } +clap-cargo = "0.12.0" diff = "0.1" dirs = "4.0" -env_logger = "0.10.0" getopts = "0.2" ignore = "0.4" itertools = "0.10" lazy_static = "1.4" -log = "0.4" -regex = "1.5" +regex = "1.7" serde = { version = "1.0.160", features = ["derive"] } serde_json = "1.0" term = "0.7" thiserror = "1.0.40" toml = "0.7.4" +tracing = "0.1.37" +tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } unicode-segmentation = "1.9" unicode-width = "0.1" -unicode_categories = "0.1" +unicode-properties = { version = "0.1", default-features = false, features = ["general-category"] } rustfmt-config_proc_macro = { version = "0.3", path = "config_proc_macro" } diff --git a/src/tools/rustfmt/Contributing.md b/src/tools/rustfmt/Contributing.md index b986a887c92..69a2c76369f 100644 --- a/src/tools/rustfmt/Contributing.md +++ b/src/tools/rustfmt/Contributing.md @@ -95,10 +95,18 @@ wish there weren't. You can leave `FIXME`s, preferably with an issue number. You may want to run a version of rustfmt from source code as part of a test or hacking on the rustfmt codebase. It's strongly discouraged to install a version -of rustfmt from source. Instead, run it using `cargo run`, and `--manifest-path`. +of rustfmt from source. + +To run `rustfmt` on a file: + +``` +cargo run --bin rustfmt -- path/to/file.rs +``` + +If you want to test modified `cargo-fmt`, or run `rustfmt` on the whole project (You may need to build rustfmt first): ``` -cargo run --bin cargo-fmt -- --manifest-path path/to/project/you/want2test/Cargo.toml +RUSTFMT="./target/debug/rustfmt" cargo run --bin cargo-fmt -- --manifest-path path/to/project/you/want2test/Cargo.toml ``` ### Version-gate formatting changes diff --git a/src/tools/rustfmt/README.md b/src/tools/rustfmt/README.md index c05184fbb04..b68a942e463 100644 --- a/src/tools/rustfmt/README.md +++ b/src/tools/rustfmt/README.md @@ -229,4 +229,4 @@ See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT) for details. [rust]: https://github.com/rust-lang/rust [fmt rfcs]: https://github.com/rust-dev-tools/fmt-rfcs -[style guide]: https://github.com/rust-dev-tools/fmt-rfcs/blob/master/guide/guide.md +[style guide]: https://doc.rust-lang.org/nightly/style-guide/ diff --git a/src/tools/rustfmt/ci/build_and_test.bat b/src/tools/rustfmt/ci/build_and_test.bat index 69dae1fff7b..16608a4aaa7 100755 --- a/src/tools/rustfmt/ci/build_and_test.bat +++ b/src/tools/rustfmt/ci/build_and_test.bat @@ -6,7 +6,11 @@ rustc -Vv || exit /b 1 cargo -V || exit /b 1 :: Build and test main crate -cargo build --locked || exit /b 1 +if "%CFG_RELEASE_CHANNEL%"=="nightly" ( + cargo build --locked --all-features || exit /b 1 +) else ( + cargo build --locked || exit /b 1 +) cargo test || exit /b 1 :: Build and test other crates diff --git a/src/tools/rustfmt/ci/build_and_test.sh b/src/tools/rustfmt/ci/build_and_test.sh index 94991853263..207da362fd6 100755 --- a/src/tools/rustfmt/ci/build_and_test.sh +++ b/src/tools/rustfmt/ci/build_and_test.sh @@ -10,7 +10,11 @@ rustc -Vv cargo -V # Build and test main crate -cargo build --locked +if [ "$CFG_RELEASE_CHANNEL" == "nightly" ]; then + cargo build --locked --all-features +else + cargo build --locked +fi cargo test # Build and test other crates diff --git a/src/tools/rustfmt/ci/check_diff.sh b/src/tools/rustfmt/ci/check_diff.sh index 062c2dd8673..50c58b1f492 100755 --- a/src/tools/rustfmt/ci/check_diff.sh +++ b/src/tools/rustfmt/ci/check_diff.sh @@ -1,5 +1,10 @@ #!/bin/bash +set -e + +# https://github.com/rust-lang/rustfmt/issues/5675 +export LD_LIBRARY_PATH=$(rustc --print sysroot)/lib:$LD_LIBRARY_PATH + function print_usage() { echo "usage check_diff REMOTE_REPO FEATURE_BRANCH [COMMIT_HASH] [OPTIONAL_RUSTFMT_CONFIGS]" } @@ -110,7 +115,7 @@ function compile_rustfmt() { git fetch feature $FEATURE_BRANCH cargo build --release --bin rustfmt && cp target/release/rustfmt $1/rustfmt - if [ -z "$OPTIONAL_COMMIT_HASH" ]; then + if [ -z "$OPTIONAL_COMMIT_HASH" ] || [ "$FEATURE_BRANCH" = "$OPTIONAL_COMMIT_HASH" ]; then git switch $FEATURE_BRANCH else git switch $OPTIONAL_COMMIT_HASH --detach @@ -140,9 +145,15 @@ function check_repo() { init_submodules $SUBMODULES fi + + # rustfmt --check returns 1 if a diff was found + # Also check_diff returns 1 if there was a diff between master rustfmt and the feature branch + # so we want to ignore the exit status check + set +e check_diff $REPO_NAME # append the status of running `check_diff` to the STATUSES array STATUSES+=($?) + set -e echo "removing tmp_dir $tmp_dir" rm -rf $tmp_dir diff --git a/src/tools/rustfmt/config_proc_macro/Cargo.toml b/src/tools/rustfmt/config_proc_macro/Cargo.toml index 34e8c237f55..eda8a7fce81 100644 --- a/src/tools/rustfmt/config_proc_macro/Cargo.toml +++ b/src/tools/rustfmt/config_proc_macro/Cargo.toml @@ -3,7 +3,7 @@ name = "rustfmt-config_proc_macro" version = "0.3.0" edition = "2018" description = "A collection of procedural macros for rustfmt" -license = "Apache-2.0/MIT" +license = "Apache-2.0 OR MIT" categories = ["development-tools::procedural-macro-helpers"] repository = "https://github.com/rust-lang/rustfmt" diff --git a/src/tools/rustfmt/docs/index.html b/src/tools/rustfmt/docs/index.html index 4fa932d4c76..ee0339bc50d 100644 --- a/src/tools/rustfmt/docs/index.html +++ b/src/tools/rustfmt/docs/index.html @@ -87,7 +87,7 @@ </article> </div> <script> - const RusfmtTagsUrl = 'https://api.github.com/repos/rust-lang/rustfmt/tags'; + const RustfmtTagsUrl = 'https://api.github.com/repos/rust-lang/rustfmt/tags'; const RustfmtLatestUrl = 'https://api.github.com/repos/rust-lang/rustfmt/releases/latest'; const UrlHash = window.location.hash.replace(/^#/, ''); const queryParams = new URLSearchParams(window.location.search); @@ -190,7 +190,7 @@ created: async function() { let tags; try { - tags = (await axios.get(RusfmtTagsUrl)).data; + tags = (await axios.get(RustfmtTagsUrl)).data; } catch(e) { this.handleReqFailure(e); return; @@ -230,7 +230,7 @@ `<p>The rate limit will be reset at ${resetDate}.</p>`; } else { this.aboutHtml = - `<p>Ecountered an error when fetching documentation data:</p>` + + `<p>Encountered an error when fetching documentation data:</p>` + `<pre><code>${e.response.data}</code></pre>` + `<p>We would appreciate <a href="https://github.com/rust-lang/rustfmt/issues/new?template=bug_report.md">a bug report</a>.` + `<p>Try refreshing the page.</p>`; diff --git a/src/tools/rustfmt/rust-toolchain b/src/tools/rustfmt/rust-toolchain index 33ff8b03da2..0057e2f370a 100644 --- a/src/tools/rustfmt/rust-toolchain +++ b/src/tools/rustfmt/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-07-01" +channel = "nightly-2023-10-22" components = ["llvm-tools", "rustc-dev"] diff --git a/src/tools/rustfmt/src/attr.rs b/src/tools/rustfmt/src/attr.rs index 22e45082a9f..4d83547d664 100644 --- a/src/tools/rustfmt/src/attr.rs +++ b/src/tools/rustfmt/src/attr.rs @@ -308,7 +308,7 @@ impl Rewrite for ast::MetaItem { // See #2479 for example. let value = rewrite_literal(context, lit.as_token_lit(), lit.span, lit_shape) .unwrap_or_else(|| context.snippet(lit.span).to_owned()); - format!("{} = {}", path, value) + format!("{path} = {value}") } }) } @@ -342,7 +342,7 @@ impl Rewrite for ast::Attribute { let literal_str = literal.as_str(); let doc_comment_formatter = DocCommentFormatter::new(literal_str, comment_style); - let doc_comment = format!("{}", doc_comment_formatter); + let doc_comment = format!("{doc_comment_formatter}"); return rewrite_doc_comment( &doc_comment, shape.comment(context.config), @@ -406,9 +406,9 @@ impl Rewrite for [ast::Attribute] { 0, )?; let comment = if comment.is_empty() { - format!("\n{}", mlb) + format!("\n{mlb}") } else { - format!("{}{}\n{}", mla, comment, mlb) + format!("{mla}{comment}\n{mlb}") }; result.push_str(&comment); result.push_str(&shape.indent.to_string(context.config)); diff --git a/src/tools/rustfmt/src/attr/doc_comment.rs b/src/tools/rustfmt/src/attr/doc_comment.rs index 25c8158df8c..f55201839b5 100644 --- a/src/tools/rustfmt/src/attr/doc_comment.rs +++ b/src/tools/rustfmt/src/attr/doc_comment.rs @@ -20,15 +20,15 @@ impl Display for DocCommentFormatter<'_> { // Handle `#[doc = ""]`. if lines.peek().is_none() { - return write!(formatter, "{}", opener); + return write!(formatter, "{opener}"); } while let Some(line) = lines.next() { let is_last_line = lines.peek().is_none(); if is_last_line { - write!(formatter, "{}{}", opener, line)?; + write!(formatter, "{opener}{line}")?; } else { - writeln!(formatter, "{}{}", opener, line)?; + writeln!(formatter, "{opener}{line}")?; } } Ok(()) diff --git a/src/tools/rustfmt/src/bin/main.rs b/src/tools/rustfmt/src/bin/main.rs index 03b75c1b041..6f564083656 100644 --- a/src/tools/rustfmt/src/bin/main.rs +++ b/src/tools/rustfmt/src/bin/main.rs @@ -6,6 +6,7 @@ use io::Error as IoError; use thiserror::Error; use rustfmt_nightly as rustfmt; +use tracing_subscriber::EnvFilter; use std::collections::HashMap; use std::env; @@ -29,13 +30,15 @@ extern crate rustc_driver; fn main() { rustc_driver::install_ice_hook(BUG_REPORT_URL, |_| ()); - env_logger::Builder::from_env("RUSTFMT_LOG").init(); + tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_env("RUSTFMT_LOG")) + .init(); let opts = make_opts(); let exit_code = match execute(&opts) { Ok(code) => code, Err(e) => { - eprintln!("{:#}", e); + eprintln!("{e:#}"); 1 } }; @@ -281,7 +284,7 @@ fn format_string(input: String, options: GetOptsOptions) -> Result<i32> { for f in config.file_lines().files() { match *f { FileName::Stdin => {} - _ => eprintln!("Warning: Extra file listed in file_lines option '{}'", f), + _ => eprintln!("Warning: Extra file listed in file_lines option '{f}'"), } } @@ -377,7 +380,7 @@ fn format_and_emit_report<T: Write>(session: &mut Session<'_, T>, input: Input) } } Err(msg) => { - eprintln!("Error writing files: {}", msg); + eprintln!("Error writing files: {msg}"); session.add_operational_error(); } } @@ -400,12 +403,9 @@ fn print_usage_to_stdout(opts: &Options, reason: &str) { let sep = if reason.is_empty() { String::new() } else { - format!("{}\n\n", reason) + format!("{reason}\n\n") }; - let msg = format!( - "{}Format Rust code\n\nusage: rustfmt [options] <file>...", - sep - ); + let msg = format!("{sep}Format Rust code\n\nusage: rustfmt [options] <file>..."); println!("{}", opts.usage(&msg)); } @@ -419,7 +419,7 @@ are 1-based and inclusive of both end points. Specifying an empty array will result in no files being formatted. For example, ``` -rustfmt --file-lines '[ +rustfmt src/lib.rs src/foo.rs --file-lines '[ {{\"file\":\"src/lib.rs\",\"range\":[7,13]}}, {{\"file\":\"src/lib.rs\",\"range\":[21,29]}}, {{\"file\":\"src/foo.rs\",\"range\":[10,11]}}, @@ -439,7 +439,7 @@ fn print_version() { include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt")) ); - println!("rustfmt {}", version_info); + println!("rustfmt {version_info}"); } fn determine_operation(matches: &Matches) -> Result<Operation, OperationError> { @@ -644,9 +644,9 @@ impl GetOptsOptions { match *f { FileName::Real(ref f) if files.contains(f) => {} FileName::Real(_) => { - eprintln!("Warning: Extra file listed in file_lines option '{}'", f) + eprintln!("Warning: Extra file listed in file_lines option '{f}'") } - FileName::Stdin => eprintln!("Warning: Not a file '{}'", f), + FileName::Stdin => eprintln!("Warning: Not a file '{f}'"), } } } diff --git a/src/tools/rustfmt/src/cargo-fmt/main.rs b/src/tools/rustfmt/src/cargo-fmt/main.rs index bc9745275f2..a1ad1aafac4 100644 --- a/src/tools/rustfmt/src/cargo-fmt/main.rs +++ b/src/tools/rustfmt/src/cargo-fmt/main.rs @@ -22,27 +22,28 @@ use clap::{CommandFactory, Parser}; mod cargo_fmt_tests; #[derive(Parser)] -#[clap( +#[command( disable_version_flag = true, bin_name = "cargo fmt", about = "This utility formats all bin and lib files of \ the current crate using rustfmt." )] +#[command(styles = clap_cargo::style::CLAP_STYLING)] pub struct Opts { /// No output printed to stdout - #[clap(short = 'q', long = "quiet")] + #[arg(short = 'q', long = "quiet")] quiet: bool, /// Use verbose output - #[clap(short = 'v', long = "verbose")] + #[arg(short = 'v', long = "verbose")] verbose: bool, /// Print rustfmt version and exit - #[clap(long = "version")] + #[arg(long = "version")] version: bool, /// Specify package to format - #[clap( + #[arg( short = 'p', long = "package", value_name = "package", @@ -51,24 +52,24 @@ pub struct Opts { packages: Vec<String>, /// Specify path to Cargo.toml - #[clap(long = "manifest-path", value_name = "manifest-path")] + #[arg(long = "manifest-path", value_name = "manifest-path")] manifest_path: Option<String>, /// Specify message-format: short|json|human - #[clap(long = "message-format", value_name = "message-format")] + #[arg(long = "message-format", value_name = "message-format")] message_format: Option<String>, /// Options passed to rustfmt // 'raw = true' to make `--` explicit. - #[clap(name = "rustfmt_options", raw(true))] + #[arg(name = "rustfmt_options", raw = true)] rustfmt_options: Vec<String>, /// Format all packages, and also their local path-based dependencies - #[clap(long = "all")] + #[arg(long = "all")] format_all: bool, /// Run rustfmt in check mode - #[clap(long = "check")] + #[arg(long = "check")] check: bool, } @@ -200,14 +201,13 @@ fn convert_message_format_to_rustfmt_args( } "human" => Ok(()), _ => Err(format!( - "invalid --message-format value: {}. Allowed values are: short|json|human", - message_format + "invalid --message-format value: {message_format}. Allowed values are: short|json|human" )), } } fn print_usage_to_stderr(reason: &str) { - eprintln!("{}", reason); + eprintln!("{reason}"); let app = Opts::command(); app.after_help("") .write_help(&mut io::stderr()) @@ -460,7 +460,7 @@ fn get_targets_with_hitlist( let package = workspace_hitlist.iter().next().unwrap(); Err(io::Error::new( io::ErrorKind::InvalidInput, - format!("package `{}` is not a member of the workspace", package), + format!("package `{package}` is not a member of the workspace"), )) } } @@ -498,7 +498,7 @@ fn run_rustfmt( if verbosity == Verbosity::Verbose { print!("rustfmt"); - print!(" --edition {}", edition); + print!(" --edition {edition}"); fmt_args.iter().for_each(|f| print!(" {}", f)); files.iter().for_each(|f| print!(" {}", f.display())); println!(); diff --git a/src/tools/rustfmt/src/chains.rs b/src/tools/rustfmt/src/chains.rs index 0afce7cf659..ea23690caed 100644 --- a/src/tools/rustfmt/src/chains.rs +++ b/src/tools/rustfmt/src/chains.rs @@ -153,7 +153,13 @@ enum CommentPosition { Top, } -// An expression plus trailing `?`s to be formatted together. +/// Information about an expression in a chain. +struct SubExpr { + expr: ast::Expr, + is_method_call_receiver: bool, +} + +/// An expression plus trailing `?`s to be formatted together. #[derive(Debug)] struct ChainItem { kind: ChainItemKind, @@ -166,7 +172,10 @@ struct ChainItem { // would remove a lot of cloning. #[derive(Debug)] enum ChainItemKind { - Parent(ast::Expr), + Parent { + expr: ast::Expr, + parens: bool, + }, MethodCall( ast::PathSegment, Vec<ast::GenericArg>, @@ -181,7 +190,7 @@ enum ChainItemKind { impl ChainItemKind { fn is_block_like(&self, context: &RewriteContext<'_>, reps: &str) -> bool { match self { - ChainItemKind::Parent(ref expr) => utils::is_block_expr(context, expr, reps), + ChainItemKind::Parent { expr, .. } => utils::is_block_expr(context, expr, reps), ChainItemKind::MethodCall(..) | ChainItemKind::StructField(..) | ChainItemKind::TupleField(..) @@ -199,7 +208,11 @@ impl ChainItemKind { } } - fn from_ast(context: &RewriteContext<'_>, expr: &ast::Expr) -> (ChainItemKind, Span) { + fn from_ast( + context: &RewriteContext<'_>, + expr: &ast::Expr, + is_method_call_receiver: bool, + ) -> (ChainItemKind, Span) { let (kind, span) = match expr.kind { ast::ExprKind::MethodCall(ref call) => { let types = if let Some(ref generic_args) = call.seg.args { @@ -236,7 +249,15 @@ impl ChainItemKind { let span = mk_sp(nested.span.hi(), expr.span.hi()); (ChainItemKind::Await, span) } - _ => return (ChainItemKind::Parent(expr.clone()), expr.span), + _ => { + return ( + ChainItemKind::Parent { + expr: expr.clone(), + parens: is_method_call_receiver && should_add_parens(expr), + }, + expr.span, + ); + } }; // Remove comments from the span. @@ -249,7 +270,14 @@ impl Rewrite for ChainItem { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { let shape = shape.sub_width(self.tries)?; let rewrite = match self.kind { - ChainItemKind::Parent(ref expr) => expr.rewrite(context, shape)?, + ChainItemKind::Parent { + ref expr, + parens: true, + } => crate::expr::rewrite_paren(context, &expr, shape, expr.span)?, + ChainItemKind::Parent { + ref expr, + parens: false, + } => expr.rewrite(context, shape)?, ChainItemKind::MethodCall(ref segment, ref types, ref exprs) => { Self::rewrite_method_call(segment.ident, types, exprs, self.span, context, shape)? } @@ -268,13 +296,14 @@ impl Rewrite for ChainItem { rewrite_comment(comment, false, shape, context.config)? } }; - Some(format!("{}{}", rewrite, "?".repeat(self.tries))) + Some(format!("{rewrite}{}", "?".repeat(self.tries))) } } impl ChainItem { - fn new(context: &RewriteContext<'_>, expr: &ast::Expr, tries: usize) -> ChainItem { - let (kind, span) = ChainItemKind::from_ast(context, expr); + fn new(context: &RewriteContext<'_>, expr: &SubExpr, tries: usize) -> ChainItem { + let (kind, span) = + ChainItemKind::from_ast(context, &expr.expr, expr.is_method_call_receiver); ChainItem { kind, tries, span } } @@ -327,7 +356,7 @@ impl Chain { let mut rev_children = vec![]; let mut sub_tries = 0; for subexpr in &subexpr_list { - match subexpr.kind { + match subexpr.expr.kind { ast::ExprKind::Try(_) => sub_tries += 1, _ => { rev_children.push(ChainItem::new(context, subexpr, sub_tries)); @@ -442,11 +471,14 @@ impl Chain { // Returns a Vec of the prefixes of the chain. // E.g., for input `a.b.c` we return [`a.b.c`, `a.b`, 'a'] - fn make_subexpr_list(expr: &ast::Expr, context: &RewriteContext<'_>) -> Vec<ast::Expr> { - let mut subexpr_list = vec![expr.clone()]; + fn make_subexpr_list(expr: &ast::Expr, context: &RewriteContext<'_>) -> Vec<SubExpr> { + let mut subexpr_list = vec![SubExpr { + expr: expr.clone(), + is_method_call_receiver: false, + }]; while let Some(subexpr) = Self::pop_expr_chain(subexpr_list.last().unwrap(), context) { - subexpr_list.push(subexpr.clone()); + subexpr_list.push(subexpr); } subexpr_list @@ -454,12 +486,18 @@ impl Chain { // Returns the expression's subexpression, if it exists. When the subexpr // is a try! macro, we'll convert it to shorthand when the option is set. - fn pop_expr_chain(expr: &ast::Expr, context: &RewriteContext<'_>) -> Option<ast::Expr> { - match expr.kind { - ast::ExprKind::MethodCall(ref call) => Some(Self::convert_try(&call.receiver, context)), + fn pop_expr_chain(expr: &SubExpr, context: &RewriteContext<'_>) -> Option<SubExpr> { + match expr.expr.kind { + ast::ExprKind::MethodCall(ref call) => Some(SubExpr { + expr: Self::convert_try(&call.receiver, context), + is_method_call_receiver: true, + }), ast::ExprKind::Field(ref subexpr, _) | ast::ExprKind::Try(ref subexpr) - | ast::ExprKind::Await(ref subexpr, _) => Some(Self::convert_try(subexpr, context)), + | ast::ExprKind::Await(ref subexpr, _) => Some(SubExpr { + expr: Self::convert_try(subexpr, context), + is_method_call_receiver: false, + }), _ => None, } } @@ -940,3 +978,22 @@ fn trim_tries(s: &str) -> String { } result } + +/// Whether a method call's receiver needs parenthesis, like +/// ```rust,ignore +/// || .. .method(); +/// || 1.. .method(); +/// 1. .method(); +/// ``` +/// Which all need parenthesis or a space before `.method()`. +fn should_add_parens(expr: &ast::Expr) -> bool { + match expr.kind { + ast::ExprKind::Lit(ref lit) => crate::expr::lit_ends_in_dot(lit), + ast::ExprKind::Closure(ref cl) => match cl.body.kind { + ast::ExprKind::Range(_, _, ast::RangeLimits::HalfOpen) => true, + ast::ExprKind::Lit(ref lit) => crate::expr::lit_ends_in_dot(lit), + _ => false, + }, + _ => false, + } +} diff --git a/src/tools/rustfmt/src/closures.rs b/src/tools/rustfmt/src/closures.rs index c95e9a97b43..a09146e9592 100644 --- a/src/tools/rustfmt/src/closures.rs +++ b/src/tools/rustfmt/src/closures.rs @@ -12,7 +12,7 @@ use crate::overflow::OverflowableItem; use crate::rewrite::{Rewrite, RewriteContext}; use crate::shape::Shape; use crate::source_map::SpanUtils; -use crate::types::rewrite_lifetime_param; +use crate::types::rewrite_bound_params; use crate::utils::{last_line_width, left_most_sub_expr, stmt_expr, NodeIdExt}; // This module is pretty messy because of the rules around closures and blocks: @@ -175,7 +175,7 @@ fn rewrite_closure_with_block( shape, false, )?; - Some(format!("{} {}", prefix, block)) + Some(format!("{prefix} {block}")) } // Rewrite closure with a single expression without wrapping its body with block. @@ -246,7 +246,7 @@ fn rewrite_closure_fn_decl( "for<> ".to_owned() } ast::ClosureBinder::For { generic_params, .. } => { - let lifetime_str = rewrite_lifetime_param(context, shape, generic_params)?; + let lifetime_str = rewrite_bound_params(context, shape, generic_params)?; format!("for<{lifetime_str}> ") } ast::ClosureBinder::NotPresent => "".to_owned(), @@ -310,10 +310,7 @@ fn rewrite_closure_fn_decl( .tactic(tactic) .preserve_newline(true); let list_str = write_list(&item_vec, &fmt)?; - let mut prefix = format!( - "{}{}{}{}{}|{}|", - binder, const_, immovable, is_async, mover, list_str - ); + let mut prefix = format!("{binder}{const_}{immovable}{is_async}{mover}|{list_str}|"); if !ret_str.is_empty() { if prefix.contains('\n') { diff --git a/src/tools/rustfmt/src/comment.rs b/src/tools/rustfmt/src/comment.rs index 85918ecc116..7da0f79bd09 100644 --- a/src/tools/rustfmt/src/comment.rs +++ b/src/tools/rustfmt/src/comment.rs @@ -58,25 +58,23 @@ fn custom_opener(s: &str) -> &str { } impl<'a> CommentStyle<'a> { - /// Returns `true` if the commenting style covers a line only. + /// Returns `true` if the commenting style cannot span multiple lines. pub(crate) fn is_line_comment(&self) -> bool { - match *self { + matches!( + self, CommentStyle::DoubleSlash - | CommentStyle::TripleSlash - | CommentStyle::Doc - | CommentStyle::Custom(_) => true, - _ => false, - } + | CommentStyle::TripleSlash + | CommentStyle::Doc + | CommentStyle::Custom(_) + ) } - /// Returns `true` if the commenting style can span over multiple lines. + /// Returns `true` if the commenting style can span multiple lines. pub(crate) fn is_block_comment(&self) -> bool { - match *self { - CommentStyle::SingleBullet | CommentStyle::DoubleBullet | CommentStyle::Exclamation => { - true - } - _ => false, - } + matches!( + self, + CommentStyle::SingleBullet | CommentStyle::DoubleBullet | CommentStyle::Exclamation + ) } /// Returns `true` if the commenting style is for documentation. @@ -367,7 +365,11 @@ fn identify_comment( trim_left_preserve_layout(first_group, shape.indent, config)? } else if !config.normalize_comments() && !config.wrap_comments() - && !config.format_code_in_doc_comments() + && !( + // `format_code_in_doc_comments` should only take effect on doc comments, + // so we only consider it when this comment block is a doc comment block. + is_doc_comment && config.format_code_in_doc_comments() + ) { light_rewrite_comment(first_group, shape.indent, config, is_doc_comment) } else { @@ -484,7 +486,9 @@ impl ItemizedBlock { // allowed. for suffix in [". ", ") "] { if let Some((prefix, _)) = trimmed.split_once(suffix) { - if prefix.len() <= 2 && prefix.chars().all(|c| char::is_ascii_digit(&c)) { + let has_leading_digits = (1..=2).contains(&prefix.len()) + && prefix.chars().all(|c| char::is_ascii_digit(&c)); + if has_leading_digits { return Some(prefix.len() + suffix.len()); } } @@ -623,7 +627,7 @@ impl<'a> CommentRewrite<'a> { is_prev_line_multi_line: false, code_block_attr: None, item_block: None, - comment_line_separator: format!("{}{}", indent_str, line_start), + comment_line_separator: format!("{indent_str}{line_start}"), max_width, indent_str, fmt_indent: shape.indent, @@ -953,7 +957,7 @@ const RUSTFMT_CUSTOM_COMMENT_PREFIX: &str = "//#### "; fn hide_sharp_behind_comment(s: &str) -> Cow<'_, str> { let s_trimmed = s.trim(); if s_trimmed.starts_with("# ") || s_trimmed == "#" { - Cow::from(format!("{}{}", RUSTFMT_CUSTOM_COMMENT_PREFIX, s)) + Cow::from(format!("{RUSTFMT_CUSTOM_COMMENT_PREFIX}{s}")) } else { Cow::from(s) } @@ -1037,7 +1041,7 @@ pub(crate) fn recover_missing_comment_in_span( } else { Cow::from(" ") }; - Some(format!("{}{}", sep, missing_comment)) + Some(format!("{sep}{missing_comment}")) } } @@ -1834,8 +1838,7 @@ fn remove_comment_header(comment: &str) -> &str { } else { assert!( comment.starts_with("/*"), - "string '{}' is not a comment", - comment + "string '{comment}' is not a comment" ); &comment[2..comment.len() - 2] } @@ -2071,26 +2074,13 @@ fn main() { expected_line_start: &str, ) { let block = ItemizedBlock::new(test_input).unwrap(); - assert_eq!(1, block.lines.len(), "test_input: {:?}", test_input); - assert_eq!( - expected_line, &block.lines[0], - "test_input: {:?}", - test_input - ); - assert_eq!( - expected_indent, block.indent, - "test_input: {:?}", - test_input - ); - assert_eq!( - expected_opener, &block.opener, - "test_input: {:?}", - test_input - ); + assert_eq!(1, block.lines.len(), "test_input: {test_input:?}"); + assert_eq!(expected_line, &block.lines[0], "test_input: {test_input:?}"); + assert_eq!(expected_indent, block.indent, "test_input: {test_input:?}"); + assert_eq!(expected_opener, &block.opener, "test_input: {test_input:?}"); assert_eq!( expected_line_start, &block.line_start, - "test_input: {:?}", - test_input + "test_input: {test_input:?}" ); } @@ -2142,13 +2132,15 @@ fn main() { // https://spec.commonmark.org/0.30 says: "A start number may not be negative": "-1. Not a list item.", "-1 Not a list item.", + // Marker without prefix are not recognized as item markers: + ". Not a list item.", + ") Not a list item.", ]; for line in test_inputs.iter() { let maybe_block = ItemizedBlock::new(line); assert!( maybe_block.is_none(), - "The following line shouldn't be classified as a list item: {}", - line + "The following line shouldn't be classified as a list item: {line}" ); } } diff --git a/src/tools/rustfmt/src/config/config_type.rs b/src/tools/rustfmt/src/config/config_type.rs index c836b4bbb78..feb452d7235 100644 --- a/src/tools/rustfmt/src/config/config_type.rs +++ b/src/tools/rustfmt/src/config/config_type.rs @@ -500,18 +500,16 @@ where // Stable with an unstable option (false, false, _) => { eprintln!( - "Warning: can't set `{} = {:?}`, unstable features are only \ - available in nightly channel.", - option_name, option_value + "Warning: can't set `{option_name} = {option_value:?}`, unstable features are only \ + available in nightly channel." ); false } // Stable with a stable option, but an unstable variant (false, true, false) => { eprintln!( - "Warning: can't set `{} = {:?}`, unstable variants are only \ - available in nightly channel.", - option_name, option_value + "Warning: can't set `{option_name} = {option_value:?}`, unstable variants are only \ + available in nightly channel." ); false } diff --git a/src/tools/rustfmt/src/config/file_lines.rs b/src/tools/rustfmt/src/config/file_lines.rs index e4e51a3f3b4..e33fe9bb283 100644 --- a/src/tools/rustfmt/src/config/file_lines.rs +++ b/src/tools/rustfmt/src/config/file_lines.rs @@ -162,7 +162,7 @@ impl fmt::Display for FileLines { None => write!(f, "None")?, Some(map) => { for (file_name, ranges) in map.iter() { - write!(f, "{}: ", file_name)?; + write!(f, "{file_name}: ")?; write!(f, "{}\n", ranges.iter().format(", "))?; } } diff --git a/src/tools/rustfmt/src/config/macro_names.rs b/src/tools/rustfmt/src/config/macro_names.rs index 26ad78d6dca..edfe925c2b3 100644 --- a/src/tools/rustfmt/src/config/macro_names.rs +++ b/src/tools/rustfmt/src/config/macro_names.rs @@ -3,7 +3,7 @@ use itertools::Itertools; use std::{fmt, str}; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize}; use serde_json as json; use thiserror::Error; @@ -30,12 +30,22 @@ impl From<MacroName> for String { } /// Defines a selector to match against a macro. -#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize)] pub enum MacroSelector { Name(MacroName), All, } +impl<'de> Deserialize<'de> for MacroSelector { + fn deserialize<D>(de: D) -> Result<Self, D::Error> + where + D: Deserializer<'de>, + { + let s = String::deserialize(de)?; + std::str::FromStr::from_str(&s).map_err(serde::de::Error::custom) + } +} + impl fmt::Display for MacroSelector { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -113,6 +123,6 @@ mod test { #[test] fn macro_names_display() { let macro_names = MacroSelectors::from_str(r#"["foo", "*", "bar"]"#).unwrap(); - assert_eq!(format!("{}", macro_names), "foo, *, bar"); + assert_eq!(format!("{macro_names}"), "foo, *, bar"); } } diff --git a/src/tools/rustfmt/src/config/mod.rs b/src/tools/rustfmt/src/config/mod.rs index 6f41b299e87..7538b26522d 100644 --- a/src/tools/rustfmt/src/config/mod.rs +++ b/src/tools/rustfmt/src/config/mod.rs @@ -216,8 +216,8 @@ impl Config { let required_version = self.required_version(); if version != required_version { println!( - "Error: rustfmt version ({}) doesn't match the required version ({})", - version, required_version, + "Error: rustfmt version ({version}) doesn't match the required version \ +({required_version})" ); return false; } @@ -310,20 +310,20 @@ impl Config { .ok_or_else(|| String::from("Parsed config was not table"))?; for key in table.keys() { if !Config::is_valid_name(key) { - let msg = &format!("Warning: Unknown configuration option `{}`\n", key); + let msg = &format!("Warning: Unknown configuration option `{key}`\n"); err.push_str(msg) } } match parsed.try_into() { Ok(parsed_config) => { if !err.is_empty() { - eprint!("{}", err); + eprint!("{err}"); } Ok(Config::default().fill_from_parsed_config(parsed_config, dir)) } Err(e) => { err.push_str("Error: Decoding config file failed:\n"); - err.push_str(format!("{}\n", e).as_str()); + err.push_str(format!("{e}\n").as_str()); err.push_str("Please check your config file."); Err(err) } @@ -563,10 +563,7 @@ mod test { let toml = used_options.to_toml().unwrap(); assert_eq!( toml, - format!( - "merge_derives = {}\nskip_children = {}\n", - merge_derives, skip_children, - ) + format!("merge_derives = {merge_derives}\nskip_children = {skip_children}\n",) ); } diff --git a/src/tools/rustfmt/src/config/options.rs b/src/tools/rustfmt/src/config/options.rs index 3aa1a4de99d..03fd91eb10f 100644 --- a/src/tools/rustfmt/src/config/options.rs +++ b/src/tools/rustfmt/src/config/options.rs @@ -1,3 +1,5 @@ +#![allow(unused_imports)] + use std::collections::{hash_set, HashSet}; use std::fmt; use std::path::{Path, PathBuf}; @@ -243,7 +245,7 @@ pub struct WidthHeuristics { impl fmt::Display for WidthHeuristics { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self) + write!(f, "{self:?}") } } diff --git a/src/tools/rustfmt/src/emitter.rs b/src/tools/rustfmt/src/emitter.rs index dc2c99a301e..9c335314d75 100644 --- a/src/tools/rustfmt/src/emitter.rs +++ b/src/tools/rustfmt/src/emitter.rs @@ -47,6 +47,6 @@ pub(crate) trait Emitter { fn ensure_real_path(filename: &FileName) -> &Path { match *filename { FileName::Real(ref path) => path, - _ => panic!("cannot format `{}` and emit to files", filename), + _ => panic!("cannot format `{filename}` and emit to files"), } } diff --git a/src/tools/rustfmt/src/emitter/checkstyle.rs b/src/tools/rustfmt/src/emitter/checkstyle.rs index 545b259979d..56d6a0ed681 100644 --- a/src/tools/rustfmt/src/emitter/checkstyle.rs +++ b/src/tools/rustfmt/src/emitter/checkstyle.rs @@ -43,7 +43,7 @@ pub(crate) fn output_checkstyle_file<T>( where T: Write, { - write!(writer, r#"<file name="{}">"#, filename)?; + write!(writer, r#"<file name="{filename}">"#)?; for mismatch in diff { let begin_line = mismatch.line_number; let mut current_line; @@ -82,7 +82,7 @@ mod tests { ); assert_eq!( &writer[..], - format!(r#"<file name="{}"></file>"#, file_name).as_bytes() + format!(r#"<file name="{file_name}"></file>"#).as_bytes() ); } diff --git a/src/tools/rustfmt/src/emitter/checkstyle/xml.rs b/src/tools/rustfmt/src/emitter/checkstyle/xml.rs index f251aabe878..d1d9af70857 100644 --- a/src/tools/rustfmt/src/emitter/checkstyle/xml.rs +++ b/src/tools/rustfmt/src/emitter/checkstyle/xml.rs @@ -13,7 +13,7 @@ impl<'a> Display for XmlEscaped<'a> { '"' => write!(formatter, """), '\'' => write!(formatter, "'"), '&' => write!(formatter, "&"), - _ => write!(formatter, "{}", char), + _ => write!(formatter, "{char}"), }?; } diff --git a/src/tools/rustfmt/src/emitter/diff.rs b/src/tools/rustfmt/src/emitter/diff.rs index 5e1f1344656..764cd136e01 100644 --- a/src/tools/rustfmt/src/emitter/diff.rs +++ b/src/tools/rustfmt/src/emitter/diff.rs @@ -28,7 +28,7 @@ impl Emitter for DiffEmitter { if has_diff { if self.config.print_misformatted_file_names() { - writeln!(output, "{}", filename)?; + writeln!(output, "{filename}")?; } else { print_diff( mismatch, @@ -40,7 +40,7 @@ impl Emitter for DiffEmitter { // This occurs when the only difference between the original and formatted values // is the newline style. This happens because The make_diff function compares the // original and formatted values line by line, independent of line endings. - writeln!(output, "Incorrect newline style in {}", filename)?; + writeln!(output, "Incorrect newline style in {filename}")?; return Ok(EmitterResult { has_diff: true }); } @@ -110,7 +110,7 @@ mod tests { assert_eq!( String::from_utf8(writer).unwrap(), - format!("{}\n{}\n", bin_file, lib_file), + format!("{bin_file}\n{lib_file}\n"), ) } diff --git a/src/tools/rustfmt/src/emitter/json.rs b/src/tools/rustfmt/src/emitter/json.rs index c7f68d4675a..5594196bed9 100644 --- a/src/tools/rustfmt/src/emitter/json.rs +++ b/src/tools/rustfmt/src/emitter/json.rs @@ -96,7 +96,7 @@ impl JsonEmitter { }); } self.mismatched_files.push(MismatchedFile { - name: format!("{}", filename), + name: format!("{filename}"), mismatches, }); Ok(()) @@ -281,7 +281,7 @@ mod tests { }]) .unwrap(); assert_eq!(result.has_diff, true); - assert_eq!(&writer[..], format!("{}\n", exp_json).as_bytes()); + assert_eq!(&writer[..], format!("{exp_json}\n").as_bytes()); } #[test] @@ -341,6 +341,6 @@ mod tests { }; let exp_json = to_json_string(&vec![exp_bin, exp_lib]).unwrap(); - assert_eq!(&writer[..], format!("{}\n", exp_json).as_bytes()); + assert_eq!(&writer[..], format!("{exp_json}\n").as_bytes()); } } diff --git a/src/tools/rustfmt/src/emitter/stdout.rs b/src/tools/rustfmt/src/emitter/stdout.rs index 9fddd515e49..0bbc7332dfe 100644 --- a/src/tools/rustfmt/src/emitter/stdout.rs +++ b/src/tools/rustfmt/src/emitter/stdout.rs @@ -24,9 +24,9 @@ impl Emitter for StdoutEmitter { }: FormattedFile<'_>, ) -> Result<EmitterResult, io::Error> { if self.verbosity != Verbosity::Quiet { - writeln!(output, "{}:\n", filename)?; + writeln!(output, "{filename}:\n")?; } - write!(output, "{}", formatted_text)?; + write!(output, "{formatted_text}")?; Ok(EmitterResult::default()) } } diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs index 03cdddc4140..acde8809329 100644 --- a/src/tools/rustfmt/src/expr.rs +++ b/src/tools/rustfmt/src/expr.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use std::cmp::min; use itertools::Itertools; -use rustc_ast::token::{Delimiter, LitKind}; +use rustc_ast::token::{Delimiter, Lit, LitKind}; use rustc_ast::{ast, ptr, token}; use rustc_span::{BytePos, Span}; @@ -26,6 +26,7 @@ use crate::rewrite::{Rewrite, RewriteContext}; use crate::shape::{Indent, Shape}; use crate::source_map::{LineRangeUtils, SpanUtils}; use crate::spanned::Spanned; +use crate::stmt; use crate::string::{rewrite_string, StringFormat}; use crate::types::{rewrite_path, PathContext}; use crate::utils::{ @@ -48,6 +49,10 @@ pub(crate) enum ExprType { SubExpression, } +pub(crate) fn lit_ends_in_dot(lit: &Lit) -> bool { + matches!(lit, Lit { kind: LitKind::Float, suffix: None, symbol } if symbol.as_str().ends_with('.')) +} + pub(crate) fn format_expr( expr: &ast::Expr, expr_type: ExprType, @@ -127,7 +132,7 @@ pub(crate) fn format_expr( ast::ExprKind::Tup(ref items) => { rewrite_tuple(context, items.iter(), expr.span, shape, items.len() == 1) } - ast::ExprKind::Let(..) => None, + ast::ExprKind::Let(ref pat, ref expr, _span, _) => rewrite_let(context, shape, pat, expr), ast::ExprKind::If(..) | ast::ExprKind::ForLoop(..) | ast::ExprKind::Loop(..) @@ -182,7 +187,7 @@ pub(crate) fn format_expr( Some(label) => format!(" {}", label.ident), None => String::new(), }; - Some(format!("continue{}", id_str)) + Some(format!("continue{id_str}")) } ast::ExprKind::Break(ref opt_label, ref opt_expr) => { let id_str = match *opt_label { @@ -191,9 +196,9 @@ pub(crate) fn format_expr( }; if let Some(ref expr) = *opt_expr { - rewrite_unary_prefix(context, &format!("break{} ", id_str), &**expr, shape) + rewrite_unary_prefix(context, &format!("break{id_str} "), &**expr, shape) } else { - Some(format!("break{}", id_str)) + Some(format!("break{id_str}")) } } ast::ExprKind::Yield(ref opt_expr) => { @@ -275,12 +280,7 @@ pub(crate) fn format_expr( fn needs_space_before_range(context: &RewriteContext<'_>, lhs: &ast::Expr) -> bool { match lhs.kind { - ast::ExprKind::Lit(token_lit) => match token_lit.kind { - token::LitKind::Float if token_lit.suffix.is_none() => { - context.snippet(lhs.span).ends_with('.') - } - _ => false, - }, + ast::ExprKind::Lit(token_lit) => lit_ends_in_dot(&token_lit), ast::ExprKind::Unary(_, ref expr) => needs_space_before_range(context, expr), _ => false, } @@ -309,7 +309,7 @@ pub(crate) fn format_expr( match (lhs.as_ref().map(|x| &**x), rhs.as_ref().map(|x| &**x)) { (Some(lhs), Some(rhs)) => { let sp_delim = if context.config.spaces_around_ranges() { - format!(" {} ", delim) + format!(" {delim} ") } else { default_sp_delim(Some(lhs), Some(rhs)) }; @@ -324,7 +324,7 @@ pub(crate) fn format_expr( } (None, Some(rhs)) => { let sp_delim = if context.config.spaces_around_ranges() { - format!("{} ", delim) + format!("{delim} ") } else { default_sp_delim(None, Some(rhs)) }; @@ -332,7 +332,7 @@ pub(crate) fn format_expr( } (Some(lhs), None) => { let sp_delim = if context.config.spaces_around_ranges() { - format!(" {}", delim) + format!(" {delim}") } else { default_sp_delim(Some(lhs), None) }; @@ -375,7 +375,7 @@ pub(crate) fn format_expr( }; if let rw @ Some(_) = rewrite_single_line_block( context, - format!("{}{}", "async ", mover).as_str(), + format!("async {mover}").as_str(), block, Some(&expr.attrs), None, @@ -386,9 +386,7 @@ pub(crate) fn format_expr( // 6 = `async ` let budget = shape.width.saturating_sub(6); Some(format!( - "{}{}{}", - "async ", - mover, + "async {mover}{}", rewrite_block( block, Some(&expr.attrs), @@ -460,7 +458,7 @@ fn rewrite_empty_block( } if !block_contains_comment(context, block) && shape.width >= 2 { - return Some(format!("{}{}{{}}", prefix, label_str)); + return Some(format!("{prefix}{label_str}{{}}")); } // If a block contains only a single-line comment, then leave it on one line. @@ -473,7 +471,7 @@ fn rewrite_empty_block( && !comment_str.starts_with("//") && comment_str.len() + 4 <= shape.width { - return Some(format!("{}{}{{ {} }}", prefix, label_str, comment_str)); + return Some(format!("{prefix}{label_str}{{ {comment_str} }}")); } } @@ -516,11 +514,11 @@ fn rewrite_single_line_block( label: Option<ast::Label>, shape: Shape, ) -> Option<String> { - if is_simple_block(context, block, attrs) { + if let Some(block_expr) = stmt::Stmt::from_simple_block(context, block, attrs) { let expr_shape = shape.offset_left(last_line_width(prefix))?; - let expr_str = block.stmts[0].rewrite(context, expr_shape)?; + let expr_str = block_expr.rewrite(context, expr_shape)?; let label_str = rewrite_label(label); - let result = format!("{}{}{{ {} }}", prefix, label_str, expr_str); + let result = format!("{prefix}{label_str}{{ {expr_str} }}"); if result.len() <= shape.width && !result.contains('\n') { return Some(result); } @@ -800,19 +798,19 @@ impl<'a> ControlFlow<'a> { let fixed_cost = self.keyword.len() + " { } else { }".len(); if let ast::ExprKind::Block(ref else_node, _) = else_block.kind { - if !is_simple_block(context, self.block, None) - || !is_simple_block(context, else_node, None) - || pat_expr_str.contains('\n') - { - return None; - } + let (if_expr, else_expr) = match ( + stmt::Stmt::from_simple_block(context, self.block, None), + stmt::Stmt::from_simple_block(context, else_node, None), + pat_expr_str.contains('\n'), + ) { + (Some(if_expr), Some(else_expr), false) => (if_expr, else_expr), + _ => return None, + }; let new_width = width.checked_sub(pat_expr_str.len() + fixed_cost)?; - let expr = &self.block.stmts[0]; - let if_str = expr.rewrite(context, Shape::legacy(new_width, Indent::empty()))?; + let if_str = if_expr.rewrite(context, Shape::legacy(new_width, Indent::empty()))?; let new_width = new_width.checked_sub(if_str.len())?; - let else_expr = &else_node.stmts[0]; let else_str = else_expr.rewrite(context, Shape::legacy(new_width, Indent::empty()))?; if if_str.contains('\n') || else_str.contains('\n') { @@ -1100,7 +1098,7 @@ impl<'a> Rewrite for ControlFlow<'a> { result? }; - let mut result = format!("{}{}", cond_str, block_str); + let mut result = format!("{cond_str}{block_str}"); if let Some(else_block) = self.else_block { let shape = Shape::indented(shape.indent, context.config); @@ -1160,8 +1158,7 @@ fn rewrite_label(opt_label: Option<ast::Label>) -> Cow<'static, str> { fn extract_comment(span: Span, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { match rewrite_missing_comment(span, shape, context) { Some(ref comment) if !comment.is_empty() => Some(format!( - "{indent}{}{indent}", - comment, + "{indent}{comment}{indent}", indent = shape.indent.to_string_with_newline(context.config) )), _ => None, @@ -1436,7 +1433,7 @@ pub(crate) fn span_ends_with_comma(context: &RewriteContext<'_>, span: Span) -> result } -fn rewrite_paren( +pub(crate) fn rewrite_paren( context: &RewriteContext<'_>, mut subexpr: &ast::Expr, shape: Shape, @@ -1452,7 +1449,7 @@ fn rewrite_paren( let remove_nested_parens = context.config.remove_nested_parens(); loop { // 1 = "(" or ")" - pre_span = mk_sp(span.lo() + BytePos(1), subexpr.span.lo()); + pre_span = mk_sp(span.lo() + BytePos(1), subexpr.span().lo()); post_span = mk_sp(subexpr.span.hi(), span.hi() - BytePos(1)); pre_comment = rewrite_missing_comment(pre_span, shape, context)?; post_comment = rewrite_missing_comment(post_span, shape, context)?; @@ -1474,7 +1471,7 @@ fn rewrite_paren( let subexpr_str = subexpr.rewrite(context, sub_shape)?; let fits_single_line = !pre_comment.contains("//") && !post_comment.contains("//"); if fits_single_line { - Some(format!("({}{}{})", pre_comment, subexpr_str, post_comment)) + Some(format!("({pre_comment}{subexpr_str}{post_comment})")) } else { rewrite_paren_in_multi_line(context, subexpr, shape, pre_span, post_span) } @@ -1538,7 +1535,7 @@ fn rewrite_index( // Return if index fits in a single line. match orig_index_rw { Some(ref index_str) if !index_str.contains('\n') => { - return Some(format!("{}[{}]", expr_str, index_str)); + return Some(format!("{expr_str}[{index_str}]")); } _ => (), } @@ -1561,7 +1558,7 @@ fn rewrite_index( indent.to_string_with_newline(context.config), new_index_str, )), - (Some(ref index_str), _) => Some(format!("{}[{}]", expr_str, index_str)), + (Some(ref index_str), _) => Some(format!("{expr_str}[{index_str}]")), _ => None, } } @@ -1593,9 +1590,9 @@ fn rewrite_struct_lit<'a>( let path_str = rewrite_path(context, PathContext::Expr, qself, path, path_shape)?; let has_base_or_rest = match struct_rest { - ast::StructRest::None if fields.is_empty() => return Some(format!("{} {{}}", path_str)), + ast::StructRest::None if fields.is_empty() => return Some(format!("{path_str} {{}}")), ast::StructRest::Rest(_) if fields.is_empty() => { - return Some(format!("{} {{ .. }}", path_str)); + return Some(format!("{path_str} {{ .. }}")); } ast::StructRest::Rest(_) | ast::StructRest::Base(_) => true, _ => false, @@ -1686,7 +1683,7 @@ fn rewrite_struct_lit<'a>( let fields_str = wrap_struct_field(context, attrs, &fields_str, shape, v_shape, one_line_width)?; - Some(format!("{} {{{}}}", path_str, fields_str)) + Some(format!("{path_str} {{{fields_str}}}")) // FIXME if context.config.indent_style() == Visual, but we run out // of space, we should fall back to BlockIndent. @@ -1716,7 +1713,7 @@ pub(crate) fn wrap_struct_field( )) } else { // One liner or visual indent. - Some(format!(" {} ", fields_str)) + Some(format!(" {fields_str} ")) } } else { Some(format!( @@ -1765,7 +1762,7 @@ pub(crate) fn rewrite_field( { Some(attrs_str + name) } - Some(e) => Some(format!("{}{}{}{}", attrs_str, name, separator, e)), + Some(e) => Some(format!("{attrs_str}{name}{separator}{e}")), None => { let expr_offset = shape.indent.block_indent(context.config); let expr = field @@ -1830,7 +1827,41 @@ fn rewrite_tuple_in_visual_indent_style<'a, T: 'a + IntoOverflowableItem<'a>>( .ends_with_newline(false); let list_str = write_list(&item_vec, &fmt)?; - Some(format!("({})", list_str)) + Some(format!("({list_str})")) +} + +fn rewrite_let( + context: &RewriteContext<'_>, + shape: Shape, + pat: &ast::Pat, + expr: &ast::Expr, +) -> Option<String> { + let mut result = "let ".to_owned(); + + // TODO(ytmimi) comments could appear between `let` and the `pat` + + // 4 = "let ".len() + let pat_shape = shape.offset_left(4)?; + let pat_str = pat.rewrite(context, pat_shape)?; + result.push_str(&pat_str); + + // TODO(ytmimi) comments could appear between `pat` and `=` + result.push_str(" ="); + + let comments_lo = context + .snippet_provider + .span_after(expr.span.with_lo(pat.span.hi()), "="); + let comments_span = mk_sp(comments_lo, expr.span.lo()); + rewrite_assign_rhs_with_comments( + context, + result, + expr, + shape, + &RhsAssignKind::Expr(&expr.kind, expr.span), + RhsTactics::Default, + comments_span, + true, + ) } pub(crate) fn rewrite_tuple<'a, T: 'a + IntoOverflowableItem<'a>>( @@ -2072,7 +2103,7 @@ fn choose_rhs<R: Rewrite>( Some(ref new_str) if !new_str.contains('\n') && unicode_str_width(new_str) <= shape.width => { - Some(format!(" {}", new_str)) + Some(format!(" {new_str}")) } _ => { // Expression did not fit on the same line as the identifier. @@ -2089,21 +2120,21 @@ fn choose_rhs<R: Rewrite>( (Some(ref orig_rhs), Some(ref new_rhs)) if !filtered_str_fits(&new_rhs, context.config.max_width(), new_shape) => { - Some(format!("{}{}", before_space_str, orig_rhs)) + Some(format!("{before_space_str}{orig_rhs}")) } (Some(ref orig_rhs), Some(ref new_rhs)) if prefer_next_line(orig_rhs, new_rhs, rhs_tactics) => { - Some(format!("{}{}", new_indent_str, new_rhs)) + Some(format!("{new_indent_str}{new_rhs}")) } - (None, Some(ref new_rhs)) => Some(format!("{}{}", new_indent_str, new_rhs)), + (None, Some(ref new_rhs)) => Some(format!("{new_indent_str}{new_rhs}")), (None, None) if rhs_tactics == RhsTactics::AllowOverflow => { let shape = shape.infinite_width(); expr.rewrite(context, shape) .map(|s| format!("{}{}", before_space_str, s)) } (None, None) => None, - (Some(orig_rhs), _) => Some(format!("{}{}", before_space_str, orig_rhs)), + (Some(orig_rhs), _) => Some(format!("{before_space_str}{orig_rhs}")), } } } diff --git a/src/tools/rustfmt/src/format-diff/main.rs b/src/tools/rustfmt/src/format-diff/main.rs index f6b739e1c2a..61e2cb711a5 100644 --- a/src/tools/rustfmt/src/format-diff/main.rs +++ b/src/tools/rustfmt/src/format-diff/main.rs @@ -5,11 +5,12 @@ #![deny(warnings)] #[macro_use] -extern crate log; +extern crate tracing; use serde::{Deserialize, Serialize}; use serde_json as json; use thiserror::Error; +use tracing_subscriber::EnvFilter; use std::collections::HashSet; use std::env; @@ -63,10 +64,12 @@ pub struct Opts { } fn main() { - env_logger::Builder::from_env("RUSTFMT_LOG").init(); + tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_env("RUSTFMT_LOG")) + .init(); let opts = Opts::parse(); if let Err(e) = run(opts) { - println!("{}", e); + println!("{e}"); Opts::command() .print_help() .expect("cannot write to stdout"); @@ -110,7 +113,7 @@ fn run_rustfmt(files: &HashSet<String>, ranges: &[Range]) -> Result<(), FormatDi if !exit_status.success() { return Err(FormatDiffError::IoError(io::Error::new( io::ErrorKind::Other, - format!("rustfmt failed with {}", exit_status), + format!("rustfmt failed with {exit_status}"), ))); } Ok(()) @@ -126,12 +129,12 @@ fn scan_diff<R>( where R: io::Read, { - let diff_pattern = format!(r"^\+\+\+\s(?:.*?/){{{}}}(\S*)", skip_prefix); + let diff_pattern = format!(r"^\+\+\+\s(?:.*?/){{{skip_prefix}}}(\S*)"); let diff_pattern = Regex::new(&diff_pattern).unwrap(); let lines_pattern = Regex::new(r"^@@.*\+(\d+)(,(\d+))?").unwrap(); - let file_filter = Regex::new(&format!("^{}$", file_filter))?; + let file_filter = Regex::new(&format!("^{file_filter}$"))?; let mut current_file = None; diff --git a/src/tools/rustfmt/src/formatting.rs b/src/tools/rustfmt/src/formatting.rs index 1f4ad6960e2..cd57a025b67 100644 --- a/src/tools/rustfmt/src/formatting.rs +++ b/src/tools/rustfmt/src/formatting.rs @@ -296,7 +296,7 @@ impl<'b, T: Write + 'b> FormatHandler for Session<'b, T> { Ok(ref result) if result.has_diff => report.add_diff(), Err(e) => { // Create a new error with path_str to help users see which files failed - let err_msg = format!("{}: {}", path, e); + let err_msg = format!("{path}: {e}"); return Err(io::Error::new(e.kind(), err_msg).into()); } _ => {} diff --git a/src/tools/rustfmt/src/git-rustfmt/main.rs b/src/tools/rustfmt/src/git-rustfmt/main.rs index 579778edbe7..3059d917c6b 100644 --- a/src/tools/rustfmt/src/git-rustfmt/main.rs +++ b/src/tools/rustfmt/src/git-rustfmt/main.rs @@ -1,5 +1,5 @@ #[macro_use] -extern crate log; +extern crate tracing; use std::env; use std::io::stdout; @@ -9,6 +9,7 @@ use std::str::FromStr; use getopts::{Matches, Options}; use rustfmt_nightly as rustfmt; +use tracing_subscriber::EnvFilter; use crate::rustfmt::{load_config, CliOptions, FormatReportFormatterBuilder, Input, Session}; @@ -42,7 +43,7 @@ fn git_diff(commits: &str) -> String { let mut cmd = Command::new("git"); cmd.arg("diff"); if commits != "0" { - cmd.arg(format!("HEAD~{}", commits)); + cmd.arg(format!("HEAD~{commits}")); } let output = cmd.output().expect("Couldn't execute `git diff`"); String::from_utf8_lossy(&output.stdout).into_owned() @@ -107,7 +108,7 @@ fn check_uncommitted() { if !uncommitted.is_empty() { println!("Found untracked changes:"); for f in &uncommitted { - println!(" {}", f); + println!(" {f}"); } println!("Commit your work, or run with `-u`."); println!("Exiting."); @@ -170,7 +171,9 @@ impl Config { } fn main() { - env_logger::Builder::from_env("RUSTFMT_LOG").init(); + tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_env("RUSTFMT_LOG")) + .init(); let opts = make_opts(); let matches = opts diff --git a/src/tools/rustfmt/src/imports.rs b/src/tools/rustfmt/src/imports.rs index 339e5cef5af..f8e7fa62890 100644 --- a/src/tools/rustfmt/src/imports.rs +++ b/src/tools/rustfmt/src/imports.rs @@ -191,7 +191,7 @@ impl UseSegment { "crate" => UseSegmentKind::Crate(None), _ => { let mod_sep = if modsep { "::" } else { "" }; - UseSegmentKind::Ident(format!("{}{}", mod_sep, name), None) + UseSegmentKind::Ident(format!("{mod_sep}{name}"), None) } }; @@ -295,8 +295,8 @@ impl fmt::Display for UseSegmentKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { UseSegmentKind::Glob => write!(f, "*"), - UseSegmentKind::Ident(ref s, Some(ref alias)) => write!(f, "{} as {}", s, alias), - UseSegmentKind::Ident(ref s, None) => write!(f, "{}", s), + UseSegmentKind::Ident(ref s, Some(ref alias)) => write!(f, "{s} as {alias}"), + UseSegmentKind::Ident(ref s, None) => write!(f, "{s}"), UseSegmentKind::Slf(..) => write!(f, "self"), UseSegmentKind::Super(..) => write!(f, "super"), UseSegmentKind::Crate(..) => write!(f, "crate"), @@ -306,7 +306,7 @@ impl fmt::Display for UseSegmentKind { if i != 0 { write!(f, ", ")?; } - write!(f, "{}", item)?; + write!(f, "{item}")?; } write!(f, "}}") } @@ -319,7 +319,7 @@ impl fmt::Display for UseTree { if i != 0 { write!(f, "::")?; } - write!(f, "{}", segment)?; + write!(f, "{segment}")?; } Ok(()) } @@ -589,7 +589,7 @@ impl UseTree { // Normalise foo::{bar} -> foo::bar if let UseSegmentKind::List(ref list) = last.kind { - if list.len() == 1 && list[0].to_string() != "self" { + if list.len() == 1 && list[0].to_string() != "self" && !list[0].has_comment() { normalize_sole_list = true; } } @@ -1032,7 +1032,9 @@ fn rewrite_nested_use_tree( let list_str = write_list(&list_items, &fmt)?; - let result = if (list_str.contains('\n') || list_str.len() > remaining_width) + let result = if (list_str.contains('\n') + || list_str.len() > remaining_width + || tactic == DefinitiveListTactic::Vertical) && context.config.imports_indent() == IndentStyle::Block { format!( @@ -1042,7 +1044,7 @@ fn rewrite_nested_use_tree( shape.indent.to_string(context.config) ) } else { - format!("{{{}}}", list_str) + format!("{{{list_str}}}") }; Some(result) @@ -1052,14 +1054,14 @@ impl Rewrite for UseSegment { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { Some(match self.kind { UseSegmentKind::Ident(ref ident, Some(ref rename)) => { - format!("{} as {}", ident, rename) + format!("{ident} as {rename}") } UseSegmentKind::Ident(ref ident, None) => ident.clone(), - UseSegmentKind::Slf(Some(ref rename)) => format!("self as {}", rename), + UseSegmentKind::Slf(Some(ref rename)) => format!("self as {rename}"), UseSegmentKind::Slf(None) => "self".to_owned(), - UseSegmentKind::Super(Some(ref rename)) => format!("super as {}", rename), + UseSegmentKind::Super(Some(ref rename)) => format!("super as {rename}"), UseSegmentKind::Super(None) => "super".to_owned(), - UseSegmentKind::Crate(Some(ref rename)) => format!("crate as {}", rename), + UseSegmentKind::Crate(Some(ref rename)) => format!("crate as {rename}"), UseSegmentKind::Crate(None) => "crate".to_owned(), UseSegmentKind::Glob => "*".to_owned(), UseSegmentKind::List(ref use_tree_list) => rewrite_nested_use_tree( diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs index 002cffa9b0c..edb5a5b629a 100644 --- a/src/tools/rustfmt/src/items.rs +++ b/src/tools/rustfmt/src/items.rs @@ -75,6 +75,7 @@ impl Rewrite for ast::Local { false, )? }; + let let_kw_offset = result.len() - "let ".len(); // 4 = "let ".len() let pat_shape = shape.offset_left(4)?; @@ -127,8 +128,15 @@ impl Rewrite for ast::Local { if let Some(block) = else_block { let else_kw_span = init.span.between(block.span); + // Strip attributes and comments to check if newline is needed before the else + // keyword from the initializer part. (#5901) + let init_str = if context.config.version() == Version::Two { + &result[let_kw_offset..] + } else { + result.as_str() + }; let force_newline_else = pat_str.contains('\n') - || !same_line_else_kw_and_brace(&result, context, else_kw_span, nested_shape); + || !same_line_else_kw_and_brace(init_str, context, else_kw_span, nested_shape); let else_kw = rewrite_else_kw_with_comments( force_newline_else, true, @@ -146,11 +154,16 @@ impl Rewrite for ast::Local { std::cmp::min(shape.width, context.config.single_line_let_else_max_width()); // If available_space hits zero we know for sure this will be a multi-lined block - let available_space = max_width.saturating_sub(result.len()); + let assign_str_with_else_kw = if context.config.version() == Version::Two { + &result[let_kw_offset..] + } else { + result.as_str() + }; + let available_space = max_width.saturating_sub(assign_str_with_else_kw.len()); let allow_single_line = !force_newline_else && available_space > 0 - && allow_single_line_let_else_block(&result, block); + && allow_single_line_let_else_block(assign_str_with_else_kw, block); let mut rw_else_block = rewrite_let_else_block(block, allow_single_line, context, shape)?; @@ -248,7 +261,6 @@ impl<'a> Item<'a> { abi: format_extern( ast::Extern::from_abi(fm.abi, DUMMY_SP), config.force_explicit_abi(), - true, ), vis: None, body: fm @@ -306,22 +318,20 @@ impl<'a> FnSig<'a> { defaultness: ast::Defaultness, ) -> FnSig<'a> { match *fn_kind { - visit::FnKind::Fn(fn_ctxt, _, fn_sig, vis, generics, _) => match fn_ctxt { - visit::FnCtxt::Assoc(..) => { - let mut fn_sig = FnSig::from_method_sig(fn_sig, generics, vis); - fn_sig.defaultness = defaultness; - fn_sig - } - _ => FnSig { - decl, - generics, - ext: fn_sig.header.ext, - constness: fn_sig.header.constness, - is_async: Cow::Borrowed(&fn_sig.header.asyncness), - defaultness, - unsafety: fn_sig.header.unsafety, - visibility: vis, - }, + visit::FnKind::Fn(visit::FnCtxt::Assoc(..), _, fn_sig, vis, generics, _) => { + let mut fn_sig = FnSig::from_method_sig(fn_sig, generics, vis); + fn_sig.defaultness = defaultness; + fn_sig + } + visit::FnKind::Fn(_, _, fn_sig, vis, generics, _) => FnSig { + decl, + generics, + ext: fn_sig.header.ext, + constness: fn_sig.header.constness, + is_async: Cow::Borrowed(&fn_sig.header.asyncness), + defaultness, + unsafety: fn_sig.header.unsafety, + visibility: vis, }, _ => unreachable!(), } @@ -338,7 +348,6 @@ impl<'a> FnSig<'a> { result.push_str(&format_extern( self.ext, context.config.force_explicit_abi(), - false, )); result } @@ -472,7 +481,7 @@ impl<'a> FmtVisitor<'a> { && self.block_indent.width() + fn_str.len() + 3 <= self.config.max_width() && !last_line_contains_single_line_comment(fn_str) { - return Some(format!("{} {{}}", fn_str)); + return Some(format!("{fn_str} {{}}")); } if !self.config.fn_single_line() || !is_simple_block_stmt(&context, block, None) { @@ -484,7 +493,7 @@ impl<'a> FmtVisitor<'a> { let width = self.block_indent.width() + fn_str.len() + res.len() + 5; if !res.contains('\n') && width <= self.config.max_width() { - Some(format!("{} {{ {} }}", fn_str, res)) + Some(format!("{fn_str} {{ {res} }}")) } else { None } @@ -666,7 +675,7 @@ impl<'a> FmtVisitor<'a> { }; let variant_body = if let Some(ref expr) = field.disr_expr { - let lhs = format!("{:1$} =", variant_body, pad_discrim_ident_to); + let lhs = format!("{variant_body:pad_discrim_ident_to$} ="); let ex = &*expr.value; rewrite_assign_rhs_with( &context, @@ -829,7 +838,7 @@ pub(crate) fn format_impl( if generics.where_clause.predicates.len() == 1 { result.push(','); } - result.push_str(&format!("{}{{{}}}", sep, sep)); + result.push_str(&format!("{sep}{{{sep}}}")); } else { result.push_str(" {}"); } @@ -1020,7 +1029,7 @@ fn rewrite_trait_ref( let shape = Shape::indented(offset + used_space, context.config); if let Some(trait_ref_str) = trait_ref.rewrite(context, shape) { if !trait_ref_str.contains('\n') { - return Some(format!(" {}{}", polarity_str, trait_ref_str)); + return Some(format!(" {polarity_str}{trait_ref_str}")); } } // We could not make enough space for trait_ref, so put it on new line. @@ -1118,172 +1127,172 @@ pub(crate) fn format_trait( item: &ast::Item, offset: Indent, ) -> Option<String> { - if let ast::ItemKind::Trait(trait_kind) = &item.kind { - let ast::Trait { - is_auto, - unsafety, - ref generics, - ref bounds, - ref items, - } = **trait_kind; - let mut result = String::with_capacity(128); - let header = format!( - "{}{}{}trait ", - format_visibility(context, &item.vis), - format_unsafety(unsafety), - format_auto(is_auto), - ); - result.push_str(&header); + let ast::ItemKind::Trait(trait_kind) = &item.kind else { + unreachable!(); + }; + let ast::Trait { + is_auto, + unsafety, + ref generics, + ref bounds, + ref items, + } = **trait_kind; - let body_lo = context.snippet_provider.span_after(item.span, "{"); + let mut result = String::with_capacity(128); + let header = format!( + "{}{}{}trait ", + format_visibility(context, &item.vis), + format_unsafety(unsafety), + format_auto(is_auto), + ); + result.push_str(&header); - let shape = Shape::indented(offset, context.config).offset_left(result.len())?; - let generics_str = - rewrite_generics(context, rewrite_ident(context, item.ident), generics, shape)?; - result.push_str(&generics_str); + let body_lo = context.snippet_provider.span_after(item.span, "{"); - // FIXME(#2055): rustfmt fails to format when there are comments between trait bounds. - if !bounds.is_empty() { - let ident_hi = context - .snippet_provider - .span_after(item.span, item.ident.as_str()); - let bound_hi = bounds.last().unwrap().span().hi(); - let snippet = context.snippet(mk_sp(ident_hi, bound_hi)); - if contains_comment(snippet) { - return None; - } + let shape = Shape::indented(offset, context.config).offset_left(result.len())?; + let generics_str = + rewrite_generics(context, rewrite_ident(context, item.ident), generics, shape)?; + result.push_str(&generics_str); - result = rewrite_assign_rhs_with( - context, - result + ":", - bounds, - shape, - &RhsAssignKind::Bounds, - RhsTactics::ForceNextLineWithoutIndent, - )?; + // FIXME(#2055): rustfmt fails to format when there are comments between trait bounds. + if !bounds.is_empty() { + let ident_hi = context + .snippet_provider + .span_after(item.span, item.ident.as_str()); + let bound_hi = bounds.last().unwrap().span().hi(); + let snippet = context.snippet(mk_sp(ident_hi, bound_hi)); + if contains_comment(snippet) { + return None; } - // Rewrite where-clause. - if !generics.where_clause.predicates.is_empty() { - let where_on_new_line = context.config.indent_style() != IndentStyle::Block; + result = rewrite_assign_rhs_with( + context, + result + ":", + bounds, + shape, + &RhsAssignKind::Bounds, + RhsTactics::ForceNextLineWithoutIndent, + )?; + } + + // Rewrite where-clause. + if !generics.where_clause.predicates.is_empty() { + let where_on_new_line = context.config.indent_style() != IndentStyle::Block; - let where_budget = context.budget(last_line_width(&result)); - let pos_before_where = if bounds.is_empty() { - generics.where_clause.span.lo() + let where_budget = context.budget(last_line_width(&result)); + let pos_before_where = if bounds.is_empty() { + generics.where_clause.span.lo() + } else { + bounds[bounds.len() - 1].span().hi() + }; + let option = WhereClauseOption::snuggled(&generics_str); + let where_clause_str = rewrite_where_clause( + context, + &generics.where_clause.predicates, + generics.where_clause.span, + context.config.brace_style(), + Shape::legacy(where_budget, offset.block_only()), + where_on_new_line, + "{", + None, + pos_before_where, + option, + )?; + // If the where-clause cannot fit on the same line, + // put the where-clause on a new line + if !where_clause_str.contains('\n') + && last_line_width(&result) + where_clause_str.len() + offset.width() + > context.config.comment_width() + { + let width = offset.block_indent + context.config.tab_spaces() - 1; + let where_indent = Indent::new(0, width); + result.push_str(&where_indent.to_string_with_newline(context.config)); + } + result.push_str(&where_clause_str); + } else { + let item_snippet = context.snippet(item.span); + if let Some(lo) = item_snippet.find('/') { + // 1 = `{` + let comment_hi = if generics.params.len() > 0 { + generics.span.lo() - BytePos(1) } else { - bounds[bounds.len() - 1].span().hi() + body_lo - BytePos(1) }; - let option = WhereClauseOption::snuggled(&generics_str); - let where_clause_str = rewrite_where_clause( - context, - &generics.where_clause.predicates, - generics.where_clause.span, - context.config.brace_style(), - Shape::legacy(where_budget, offset.block_only()), - where_on_new_line, - "{", - None, - pos_before_where, - option, - )?; - // If the where-clause cannot fit on the same line, - // put the where-clause on a new line - if !where_clause_str.contains('\n') - && last_line_width(&result) + where_clause_str.len() + offset.width() - > context.config.comment_width() - { - let width = offset.block_indent + context.config.tab_spaces() - 1; - let where_indent = Indent::new(0, width); - result.push_str(&where_indent.to_string_with_newline(context.config)); - } - result.push_str(&where_clause_str); - } else { - let item_snippet = context.snippet(item.span); - if let Some(lo) = item_snippet.find('/') { - // 1 = `{` - let comment_hi = if generics.params.len() > 0 { - generics.span.lo() - BytePos(1) - } else { - body_lo - BytePos(1) - }; - let comment_lo = item.span.lo() + BytePos(lo as u32); - if comment_lo < comment_hi { - match recover_missing_comment_in_span( - mk_sp(comment_lo, comment_hi), - Shape::indented(offset, context.config), - context, - last_line_width(&result), - ) { - Some(ref missing_comment) if !missing_comment.is_empty() => { - result.push_str(missing_comment); - } - _ => (), + let comment_lo = item.span.lo() + BytePos(lo as u32); + if comment_lo < comment_hi { + match recover_missing_comment_in_span( + mk_sp(comment_lo, comment_hi), + Shape::indented(offset, context.config), + context, + last_line_width(&result), + ) { + Some(ref missing_comment) if !missing_comment.is_empty() => { + result.push_str(missing_comment); } + _ => (), } } } + } - let block_span = mk_sp(generics.where_clause.span.hi(), item.span.hi()); - let snippet = context.snippet(block_span); - let open_pos = snippet.find_uncommented("{")? + 1; + let block_span = mk_sp(generics.where_clause.span.hi(), item.span.hi()); + let snippet = context.snippet(block_span); + let open_pos = snippet.find_uncommented("{")? + 1; - match context.config.brace_style() { - _ if last_line_contains_single_line_comment(&result) - || last_line_width(&result) + 2 > context.budget(offset.width()) => - { - result.push_str(&offset.to_string_with_newline(context.config)); - } - _ if context.config.empty_item_single_line() - && items.is_empty() - && !result.contains('\n') - && !contains_comment(&snippet[open_pos..]) => + match context.config.brace_style() { + _ if last_line_contains_single_line_comment(&result) + || last_line_width(&result) + 2 > context.budget(offset.width()) => + { + result.push_str(&offset.to_string_with_newline(context.config)); + } + _ if context.config.empty_item_single_line() + && items.is_empty() + && !result.contains('\n') + && !contains_comment(&snippet[open_pos..]) => + { + result.push_str(" {}"); + return Some(result); + } + BraceStyle::AlwaysNextLine => { + result.push_str(&offset.to_string_with_newline(context.config)); + } + BraceStyle::PreferSameLine => result.push(' '), + BraceStyle::SameLineWhere => { + if result.contains('\n') + || (!generics.where_clause.predicates.is_empty() && !items.is_empty()) { - result.push_str(" {}"); - return Some(result); - } - BraceStyle::AlwaysNextLine => { result.push_str(&offset.to_string_with_newline(context.config)); - } - BraceStyle::PreferSameLine => result.push(' '), - BraceStyle::SameLineWhere => { - if result.contains('\n') - || (!generics.where_clause.predicates.is_empty() && !items.is_empty()) - { - result.push_str(&offset.to_string_with_newline(context.config)); - } else { - result.push(' '); - } + } else { + result.push(' '); } } - result.push('{'); - - let outer_indent_str = offset.block_only().to_string_with_newline(context.config); + } + result.push('{'); - if !items.is_empty() || contains_comment(&snippet[open_pos..]) { - let mut visitor = FmtVisitor::from_context(context); - visitor.block_indent = offset.block_only().block_indent(context.config); - visitor.last_pos = block_span.lo() + BytePos(open_pos as u32); + let outer_indent_str = offset.block_only().to_string_with_newline(context.config); - for item in items { - visitor.visit_trait_item(item); - } + if !items.is_empty() || contains_comment(&snippet[open_pos..]) { + let mut visitor = FmtVisitor::from_context(context); + visitor.block_indent = offset.block_only().block_indent(context.config); + visitor.last_pos = block_span.lo() + BytePos(open_pos as u32); - visitor.format_missing(item.span.hi() - BytePos(1)); + for item in items { + visitor.visit_trait_item(item); + } - let inner_indent_str = visitor.block_indent.to_string_with_newline(context.config); + visitor.format_missing(item.span.hi() - BytePos(1)); - result.push_str(&inner_indent_str); - result.push_str(visitor.buffer.trim()); - result.push_str(&outer_indent_str); - } else if result.contains('\n') { - result.push_str(&outer_indent_str); - } + let inner_indent_str = visitor.block_indent.to_string_with_newline(context.config); - result.push('}'); - Some(result) - } else { - unreachable!(); + result.push_str(&inner_indent_str); + result.push_str(visitor.buffer.trim()); + result.push_str(&outer_indent_str); + } else if result.contains('\n') { + result.push_str(&outer_indent_str); } + + result.push('}'); + Some(result) } pub(crate) struct TraitAliasBounds<'a> { @@ -1322,7 +1331,7 @@ impl<'a> Rewrite for TraitAliasBounds<'a> { shape.indent.to_string_with_newline(context.config) }; - Some(format!("{}{}{}", generic_bounds_str, space, where_str)) + Some(format!("{generic_bounds_str}{space}{where_str}")) } } @@ -1339,7 +1348,7 @@ pub(crate) fn format_trait_alias( let g_shape = shape.offset_left(6)?.sub_width(2)?; let generics_str = rewrite_generics(context, alias, generics, g_shape)?; let vis_str = format_visibility(context, vis); - let lhs = format!("{}trait {} =", vis_str, generics_str); + let lhs = format!("{vis_str}trait {generics_str} ="); // 1 = ";" let trait_alias_bounds = TraitAliasBounds { generic_bounds, @@ -1376,7 +1385,7 @@ fn format_unit_struct( } else { String::new() }; - Some(format!("{}{};", header_str, generics_str)) + Some(format!("{header_str}{generics_str};")) } pub(crate) fn format_struct_struct( @@ -1466,7 +1475,7 @@ pub(crate) fn format_struct_struct( && items_str.len() <= one_line_budget && !last_line_contains_single_line_comment(&items_str) { - Some(format!("{} {} }}", result, items_str)) + Some(format!("{result} {items_str} }}")) } else { Some(format!( "{}\n{}{}\n{}}}", @@ -1696,7 +1705,7 @@ pub(crate) fn rewrite_type_alias<'a, 'b>( rewrite_ty(rw_info, Some(bounds), ty_opt, vis) }?; match defaultness { - ast::Defaultness::Default(..) => Some(format!("default {}", result)), + ast::Defaultness::Default(..) => Some(format!("default {result}")), _ => Some(result), } } @@ -1803,14 +1812,14 @@ fn rewrite_ty<R: Rewrite>( true, )? } - _ => format!("{}=", result), + _ => format!("{result}="), }; // 1 = `;` let shape = Shape::indented(indent, context.config).sub_width(1)?; rewrite_assign_rhs(context, lhs, &*ty, &RhsAssignKind::Ty, shape).map(|s| s + ";") } else { - Some(format!("{};", result)) + Some(format!("{result};")) } } @@ -2019,7 +2028,7 @@ fn rewrite_static( let expr_lo = expr.span.lo(); let comments_span = mk_sp(comments_lo, expr_lo); - let lhs = format!("{}{} =", prefix, ty_str); + let lhs = format!("{prefix}{ty_str} ="); // 1 = ; let remaining_width = context.budget(offset.block_indent + 1); @@ -2036,7 +2045,7 @@ fn rewrite_static( .and_then(|res| recover_comment_removed(res, static_parts.span, context)) .map(|s| if s.ends_with(';') { s } else { s + ";" }) } else { - Some(format!("{}{};", prefix, ty_str)) + Some(format!("{prefix}{ty_str};")) } } @@ -2229,7 +2238,7 @@ fn rewrite_explicit_self( Some(combine_strs_with_missing_comments( context, param_attrs, - &format!("&{} {}self", lifetime_str, mut_str), + &format!("&{lifetime_str} {mut_str}self"), span, shape, !has_multiple_attr_lines, @@ -2238,7 +2247,7 @@ fn rewrite_explicit_self( None => Some(combine_strs_with_missing_comments( context, param_attrs, - &format!("&{}self", mut_str), + &format!("&{mut_str}self"), span, shape, !has_multiple_attr_lines, @@ -2909,7 +2918,7 @@ fn rewrite_where_clause_rfc_style( clause_shape.indent.to_string_with_newline(context.config) }; - Some(format!("{}{}{}", where_keyword, clause_sep, preds_str)) + Some(format!("{where_keyword}{clause_sep}{preds_str}")) } /// Rewrite `where` and comment around it. @@ -2949,8 +2958,8 @@ fn rewrite_where_keyword( let newline_before_where = comment_separator(&comment_before, shape); let newline_after_where = comment_separator(&comment_after, clause_shape); let result = format!( - "{}{}{}where{}{}", - starting_newline, comment_before, newline_before_where, newline_after_where, comment_after + "{starting_newline}{comment_before}{newline_before_where}where\ +{newline_after_where}{comment_after}" ); let allow_single_line = where_clause_option.allow_single_line && comment_before.is_empty() @@ -3001,10 +3010,12 @@ fn rewrite_bounds_on_where_clause( DefinitiveListTactic::Vertical }; + let preserve_newline = context.config.version() == Version::One; + let fmt = ListFormatting::new(shape, context.config) .tactic(shape_tactic) .trailing_separator(comma_tactic) - .preserve_newline(true); + .preserve_newline(preserve_newline); write_list(&items.collect::<Vec<_>>(), &fmt) } @@ -3105,7 +3116,7 @@ fn rewrite_where_clause( preds_str )) } else { - Some(format!(" where {}", preds_str)) + Some(format!(" where {preds_str}")) } } @@ -3234,7 +3245,7 @@ fn format_generics( if brace_pos == BracePos::None { span.hi() } else { - context.snippet_provider.span_before(span, "{") + context.snippet_provider.span_before_last(span, "{") }, ), shape, diff --git a/src/tools/rustfmt/src/lib.rs b/src/tools/rustfmt/src/lib.rs index 567628f63bd..877d057a34b 100644 --- a/src/tools/rustfmt/src/lib.rs +++ b/src/tools/rustfmt/src/lib.rs @@ -9,7 +9,7 @@ #[macro_use] extern crate lazy_static; #[macro_use] -extern crate log; +extern crate tracing; // N.B. these crates are loaded from the sysroot, so they need extern crate. extern crate rustc_ast; @@ -29,6 +29,7 @@ extern crate thin_vec; extern crate rustc_driver; use std::cell::RefCell; +use std::cmp::min; use std::collections::HashMap; use std::fmt; use std::io::{self, Write}; @@ -386,9 +387,15 @@ fn format_code_block( .snippet .rfind('}') .unwrap_or_else(|| formatted.snippet.len()); + + // It's possible that `block_len < FN_MAIN_PREFIX.len()`. This can happen if the code block was + // formatted into the empty string, leading to the enclosing `fn main() {\n}` being formatted + // into `fn main() {}`. In this case no unindentation is done. + let block_start = min(FN_MAIN_PREFIX.len(), block_len); + let mut is_indented = true; let indent_str = Indent::from_width(config, config.tab_spaces()).to_string(config); - for (kind, ref line) in LineClasses::new(&formatted.snippet[FN_MAIN_PREFIX.len()..block_len]) { + for (kind, ref line) in LineClasses::new(&formatted.snippet[block_start..block_len]) { if !is_first { result.push('\n'); } else { diff --git a/src/tools/rustfmt/src/lists.rs b/src/tools/rustfmt/src/lists.rs index a878e6cf9b2..41afef279e9 100644 --- a/src/tools/rustfmt/src/lists.rs +++ b/src/tools/rustfmt/src/lists.rs @@ -637,7 +637,7 @@ pub(crate) fn extract_post_comment( post_snippet.trim_matches(white_space) } // not comment or over two lines - else if post_snippet.ends_with(',') + else if post_snippet.ends_with(separator) && (!post_snippet.trim().starts_with("//") || post_snippet.trim().contains('\n')) { post_snippet[..(post_snippet.len() - 1)].trim_matches(white_space) diff --git a/src/tools/rustfmt/src/macros.rs b/src/tools/rustfmt/src/macros.rs index 4f45d0c7402..76553466e48 100644 --- a/src/tools/rustfmt/src/macros.rs +++ b/src/tools/rustfmt/src/macros.rs @@ -103,7 +103,7 @@ fn rewrite_macro_name( format!("{}!", pprust::path_to_string(path)) }; match extra_ident { - Some(ident) if ident.name != kw::Empty => format!("{} {}", name, ident), + Some(ident) if ident.name != kw::Empty => format!("{name} {ident}"), _ => name, } } @@ -214,14 +214,14 @@ fn rewrite_macro_inner( if ts.is_empty() && !has_comment { return match style { Delimiter::Parenthesis if position == MacroPosition::Item => { - Some(format!("{}();", macro_name)) + Some(format!("{macro_name}();")) } Delimiter::Bracket if position == MacroPosition::Item => { - Some(format!("{}[];", macro_name)) + Some(format!("{macro_name}[];")) } - Delimiter::Parenthesis => Some(format!("{}()", macro_name)), - Delimiter::Bracket => Some(format!("{}[]", macro_name)), - Delimiter::Brace => Some(format!("{} {{}}", macro_name)), + Delimiter::Parenthesis => Some(format!("{macro_name}()")), + Delimiter::Bracket => Some(format!("{macro_name}[]")), + Delimiter::Brace => Some(format!("{macro_name} {{}}")), _ => unreachable!(), }; } @@ -255,6 +255,7 @@ fn rewrite_macro_inner( ¯o_name, shape, style, + original_style, position, mac.span(), ); @@ -295,20 +296,19 @@ fn rewrite_macro_inner( // If we are rewriting `vec!` macro or other special macros, // then we can rewrite this as a usual array literal. // Otherwise, we must preserve the original existence of trailing comma. - let macro_name = ¯o_name.as_str(); let mut force_trailing_comma = if trailing_comma { Some(SeparatorTactic::Always) } else { Some(SeparatorTactic::Never) }; - if FORCED_BRACKET_MACROS.contains(macro_name) && !is_nested_macro { + if is_forced_bracket && !is_nested_macro { context.leave_macro(); if context.use_block_indent() { force_trailing_comma = Some(SeparatorTactic::Vertical); }; } let rewrite = rewrite_array( - macro_name, + ¯o_name, arg_vec.iter(), mac.span(), context, @@ -321,7 +321,7 @@ fn rewrite_macro_inner( _ => "", }; - Some(format!("{}{}", rewrite, comma)) + Some(format!("{rewrite}{comma}")) } } Delimiter::Brace => { @@ -330,8 +330,8 @@ fn rewrite_macro_inner( // anything in between the braces (for now). let snippet = context.snippet(mac.span()).trim_start_matches(|c| c != '{'); match trim_left_preserve_layout(snippet, shape.indent, context.config) { - Some(macro_body) => Some(format!("{} {}", macro_name, macro_body)), - None => Some(format!("{} {}", macro_name, snippet)), + Some(macro_body) => Some(format!("{macro_name} {macro_body}")), + None => Some(format!("{macro_name} {snippet}")), } } _ => unreachable!(), @@ -362,7 +362,7 @@ fn handle_vec_semi( && lhs.len() + rhs.len() + total_overhead <= shape.width { // macro_name(lhs; rhs) or macro_name[lhs; rhs] - Some(format!("{}{}{}; {}{}", macro_name, left, lhs, rhs, right)) + Some(format!("{macro_name}{left}{lhs}; {rhs}{right}")) } else { // macro_name(\nlhs;\nrhs\n) or macro_name[\nlhs;\nrhs\n] Some(format!( @@ -379,6 +379,23 @@ fn handle_vec_semi( } } +fn rewrite_empty_macro_def_body( + context: &RewriteContext<'_>, + span: Span, + shape: Shape, +) -> Option<String> { + // Create an empty, dummy `ast::Block` representing an empty macro body + let block = ast::Block { + stmts: vec![].into(), + id: rustc_ast::node_id::DUMMY_NODE_ID, + rules: ast::BlockCheckMode::Default, + span: span, + tokens: None, + could_be_bare_literal: false, + }; + block.rewrite(context, shape) +} + pub(crate) fn rewrite_macro_def( context: &RewriteContext<'_>, shape: Shape, @@ -419,6 +436,13 @@ pub(crate) fn rewrite_macro_def( shape }; + if parsed_def.branches.len() == 0 { + let lo = context.snippet_provider.span_before(span, "{"); + result += " "; + result += &rewrite_empty_macro_def_body(context, span.with_lo(lo), shape)?; + return Some(result); + } + let branch_items = itemize_list( context.snippet_provider, parsed_def.branches.iter(), @@ -572,8 +596,8 @@ fn delim_token_to_str( .block_indent(context.config) .to_string_with_newline(context.config); ( - format!("{}{}", lhs, nested_indent_str), - format!("{}{}", indent_str, rhs), + format!("{lhs}{nested_indent_str}"), + format!("{indent_str}{rhs}"), ) } else { (lhs.to_owned(), rhs.to_owned()) @@ -630,7 +654,7 @@ impl MacroArgKind { }; match *self { - MacroArgKind::MetaVariable(ty, ref name) => Some(format!("${}:{}", name, ty)), + MacroArgKind::MetaVariable(ty, ref name) => Some(format!("${name}:{ty}")), MacroArgKind::Repeat(delim_tok, ref args, ref another, ref tok) => { let (lhs, inner, rhs) = rewrite_delimited_inner(delim_tok, args)?; let another = another @@ -639,14 +663,14 @@ impl MacroArgKind { .unwrap_or_else(|| "".to_owned()); let repeat_tok = pprust::token_to_string(tok); - Some(format!("${}{}{}{}{}", lhs, inner, rhs, another, repeat_tok)) + Some(format!("${lhs}{inner}{rhs}{another}{repeat_tok}")) } MacroArgKind::Delimited(delim_tok, ref args) => { rewrite_delimited_inner(delim_tok, args) .map(|(lhs, inner, rhs)| format!("{}{}{}", lhs, inner, rhs)) } - MacroArgKind::Separator(ref sep, ref prefix) => Some(format!("{}{} ", prefix, sep)), - MacroArgKind::Other(ref inner, ref prefix) => Some(format!("{}{}", prefix, inner)), + MacroArgKind::Separator(ref sep, ref prefix) => Some(format!("{prefix}{sep} ")), + MacroArgKind::Other(ref inner, ref prefix) => Some(format!("{prefix}{inner}")), } } } @@ -1378,15 +1402,19 @@ fn rewrite_macro_with_items( macro_name: &str, shape: Shape, style: Delimiter, + original_style: Delimiter, position: MacroPosition, span: Span, ) -> Option<String> { - let (opener, closer) = match style { - Delimiter::Parenthesis => ("(", ")"), - Delimiter::Bracket => ("[", "]"), - Delimiter::Brace => (" {", "}"), - _ => return None, + let style_to_delims = |style| match style { + Delimiter::Parenthesis => Some(("(", ")")), + Delimiter::Bracket => Some(("[", "]")), + Delimiter::Brace => Some((" {", "}")), + _ => None, }; + + let (opener, closer) = style_to_delims(style)?; + let (original_opener, _) = style_to_delims(original_style)?; let trailing_semicolon = match style { Delimiter::Parenthesis | Delimiter::Bracket if position == MacroPosition::Item => ";", _ => "", @@ -1394,7 +1422,13 @@ fn rewrite_macro_with_items( let mut visitor = FmtVisitor::from_context(context); visitor.block_indent = shape.indent.block_indent(context.config); - visitor.last_pos = context.snippet_provider.span_after(span, opener.trim()); + + // The current opener may be different from the original opener. This can happen + // if our macro is a forced bracket macro originally written with non-bracket + // delimiters. We need to use the original opener to locate the span after it. + visitor.last_pos = context + .snippet_provider + .span_after(span, original_opener.trim()); for item in items { let item = match item { MacroArg::Item(item) => item, diff --git a/src/tools/rustfmt/src/matches.rs b/src/tools/rustfmt/src/matches.rs index 4c37116f120..95b0ed16db8 100644 --- a/src/tools/rustfmt/src/matches.rs +++ b/src/tools/rustfmt/src/matches.rs @@ -124,7 +124,7 @@ pub(crate) fn rewrite_match( if arms.is_empty() { let snippet = context.snippet(mk_sp(open_brace_pos, span.hi() - BytePos(1))); if snippet.trim().is_empty() { - Some(format!("match {} {{}}", cond_str)) + Some(format!("match {cond_str} {{}}")) } else { // Empty match with comments or inner attributes? We are not going to bother, sorry ;) Some(context.snippet(span).to_owned()) @@ -246,8 +246,18 @@ fn rewrite_match_arm( }; // Patterns - // 5 = ` => {` - let pat_shape = shape.sub_width(5)?.offset_left(pipe_offset)?; + let pat_shape = match &arm.body.kind { + ast::ExprKind::Block(_, Some(label)) => { + // Some block with a label ` => 'label: {` + // 7 = ` => : {` + let label_len = label.ident.as_str().len(); + shape.sub_width(7 + label_len)?.offset_left(pipe_offset)? + } + _ => { + // 5 = ` => {` + shape.sub_width(5)?.offset_left(pipe_offset)? + } + }; let pats_str = arm.pat.rewrite(context, pat_shape)?; // Guard @@ -264,7 +274,7 @@ fn rewrite_match_arm( let lhs_str = combine_strs_with_missing_comments( context, &attrs_str, - &format!("{}{}{}", pipe_str, pats_str, guard_str), + &format!("{pipe_str}{pats_str}{guard_str}"), missing_span, shape, false, @@ -296,8 +306,9 @@ fn block_can_be_flattened<'a>( expr: &'a ast::Expr, ) -> Option<&'a ast::Block> { match expr.kind { - ast::ExprKind::Block(ref block, _) - if !is_unsafe_block(block) + ast::ExprKind::Block(ref block, label) + if label.is_none() + && !is_unsafe_block(block) && !context.inside_macro() && is_simple_block(context, block, Some(&expr.attrs)) && !stmt_is_expr_mac(&block.stmts[0]) => @@ -532,7 +543,7 @@ fn rewrite_guard( if let Some(cond_shape) = cond_shape { if let Some(cond_str) = guard.rewrite(context, cond_shape) { if !cond_str.contains('\n') || pattern_width <= context.config.tab_spaces() { - return Some(format!(" if {}", cond_str)); + return Some(format!(" if {cond_str}")); } } } diff --git a/src/tools/rustfmt/src/pairs.rs b/src/tools/rustfmt/src/pairs.rs index d135da7e359..07c05193739 100644 --- a/src/tools/rustfmt/src/pairs.rs +++ b/src/tools/rustfmt/src/pairs.rs @@ -42,9 +42,13 @@ pub(crate) fn rewrite_all_pairs( context: &RewriteContext<'_>, ) -> Option<String> { expr.flatten(context, shape).and_then(|list| { - // First we try formatting on one line. - rewrite_pairs_one_line(&list, shape, context) - .or_else(|| rewrite_pairs_multiline(&list, shape, context)) + if list.let_chain_count() > 0 && !list.can_rewrite_let_chain_single_line() { + rewrite_pairs_multiline(&list, shape, context) + } else { + // First we try formatting on one line. + rewrite_pairs_one_line(&list, shape, context) + .or_else(|| rewrite_pairs_multiline(&list, shape, context)) + } }) } @@ -234,8 +238,8 @@ where let rhs_result = rhs.rewrite(context, rhs_shape)?; let indent_str = rhs_shape.indent.to_string_with_newline(context.config); let infix_with_sep = match separator_place { - SeparatorPlace::Back => format!("{}{}", infix, indent_str), - SeparatorPlace::Front => format!("{}{}", indent_str, infix), + SeparatorPlace::Back => format!("{infix}{indent_str}"), + SeparatorPlace::Front => format!("{indent_str}{infix}"), }; Some(format!( "{}{}{}{}", @@ -255,6 +259,37 @@ struct PairList<'a, 'b, T: Rewrite> { separators: Vec<&'a str>, } +fn is_ident(expr: &ast::Expr) -> bool { + match &expr.kind { + ast::ExprKind::Path(None, path) if path.segments.len() == 1 => true, + ast::ExprKind::Unary(_, expr) + | ast::ExprKind::AddrOf(_, _, expr) + | ast::ExprKind::Paren(expr) + | ast::ExprKind::Try(expr) => is_ident(expr), + _ => false, + } +} + +impl<'a, 'b> PairList<'a, 'b, ast::Expr> { + fn let_chain_count(&self) -> usize { + self.list + .iter() + .filter(|(expr, _)| matches!(expr.kind, ast::ExprKind::Let(..))) + .count() + } + + fn can_rewrite_let_chain_single_line(&self) -> bool { + if self.list.len() != 2 { + return false; + } + + let fist_item_is_ident = is_ident(self.list[0].0); + let second_item_is_let_chain = matches!(self.list[1].0.kind, ast::ExprKind::Let(..)); + + fist_item_is_ident && second_item_is_let_chain + } +} + impl FlattenPair for ast::Expr { fn flatten( &self, diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs index 3f94bb29933..0573df9de2f 100644 --- a/src/tools/rustfmt/src/parse/session.rs +++ b/src/tools/rustfmt/src/parse/session.rs @@ -316,8 +316,7 @@ impl LineRangeUtils for ParseSess { debug_assert_eq!( lo.sf.name, hi.sf.name, - "span crossed file boundary: lo: {:?}, hi: {:?}", - lo, hi + "span crossed file boundary: lo: {lo:?}, hi: {hi:?}" ); // in case the span starts with a newline, the line range is off by 1 without the diff --git a/src/tools/rustfmt/src/patterns.rs b/src/tools/rustfmt/src/patterns.rs index 3f335172590..33f3b4b8a21 100644 --- a/src/tools/rustfmt/src/patterns.rs +++ b/src/tools/rustfmt/src/patterns.rs @@ -208,7 +208,7 @@ impl Rewrite for Pat { None => "", Some(_) => " ", }; - format!("{}{}{}", lhs_spacing, infix, rhs_spacing) + format!("{lhs_spacing}{infix}{rhs_spacing}") } else { infix.to_owned() }; @@ -283,7 +283,7 @@ fn rewrite_struct_pat( let path_str = rewrite_path(context, PathContext::Expr, qself, path, path_shape)?; if fields.is_empty() && !ellipsis { - return Some(format!("{} {{}}", path_str)); + return Some(format!("{path_str} {{}}")); } let (ellipsis_str, terminator) = if ellipsis { (", ..", "..") } else { ("", "}") }; @@ -344,7 +344,7 @@ fn rewrite_struct_pat( // ast::Pat doesn't have attrs so use &[] let fields_str = wrap_struct_field(context, &[], &fields_str, shape, v_shape, one_line_width)?; - Some(format!("{} {{{}}}", path_str, fields_str)) + Some(format!("{path_str} {{{fields_str}}}")) } impl Rewrite for PatField { @@ -376,7 +376,7 @@ impl Rewrite for PatField { let id_str = rewrite_ident(context, self.ident); let one_line_width = id_str.len() + 2 + pat_str.len(); let pat_and_id_str = if one_line_width <= shape.width { - format!("{}: {}", id_str, pat_str) + format!("{id_str}: {pat_str}") } else { format!( "{}:\n{}{}", diff --git a/src/tools/rustfmt/src/rustfmt_diff.rs b/src/tools/rustfmt/src/rustfmt_diff.rs index 1724a0f87bf..c9883452185 100644 --- a/src/tools/rustfmt/src/rustfmt_diff.rs +++ b/src/tools/rustfmt/src/rustfmt_diff.rs @@ -95,7 +95,7 @@ impl fmt::Display for ModifiedLines { )?; for line in &chunk.lines { - writeln!(f, "{}", line)?; + writeln!(f, "{line}")?; } } @@ -166,12 +166,12 @@ impl OutputWriter { if let Some(color) = color { t.fg(color).unwrap(); } - writeln!(t, "{}", msg).unwrap(); + writeln!(t, "{msg}").unwrap(); if color.is_some() { t.reset().unwrap(); } } - None => println!("{}", msg), + None => println!("{msg}"), } } } @@ -265,16 +265,15 @@ where for line in mismatch.lines { match line { DiffLine::Context(ref str) => { - writer.writeln(&format!(" {}{}", str, line_terminator), None) + writer.writeln(&format!(" {str}{line_terminator}"), None) } DiffLine::Expected(ref str) => writer.writeln( - &format!("+{}{}", str, line_terminator), + &format!("+{str}{line_terminator}"), Some(term::color::GREEN), ), - DiffLine::Resulting(ref str) => writer.writeln( - &format!("-{}{}", str, line_terminator), - Some(term::color::RED), - ), + DiffLine::Resulting(ref str) => { + writer.writeln(&format!("-{str}{line_terminator}"), Some(term::color::RED)) + } } } } diff --git a/src/tools/rustfmt/src/skip.rs b/src/tools/rustfmt/src/skip.rs index 68f85b2ade4..d733f7068fd 100644 --- a/src/tools/rustfmt/src/skip.rs +++ b/src/tools/rustfmt/src/skip.rs @@ -105,7 +105,7 @@ pub(crate) fn is_skip_attr(segments: &[ast::PathSegment]) -> bool { fn get_skip_names(kind: &str, attrs: &[ast::Attribute]) -> Vec<String> { let mut skip_names = vec![]; - let path = format!("{}::{}::{}", RUSTFMT, SKIP, kind); + let path = format!("{RUSTFMT}::{SKIP}::{kind}"); for attr in attrs { // rustc_ast::ast::Path is implemented partialEq // but it is designed for segments.len() == 1 diff --git a/src/tools/rustfmt/src/source_file.rs b/src/tools/rustfmt/src/source_file.rs index 56d4ab40038..958f9b0154f 100644 --- a/src/tools/rustfmt/src/source_file.rs +++ b/src/tools/rustfmt/src/source_file.rs @@ -62,7 +62,7 @@ where fn ensure_real_path(filename: &FileName) -> &Path { match *filename { FileName::Real(ref path) => path, - _ => panic!("cannot format `{}` and emit to files", filename), + _ => panic!("cannot format `{filename}` and emit to files"), } } diff --git a/src/tools/rustfmt/src/stmt.rs b/src/tools/rustfmt/src/stmt.rs index 0b3854425ea..e3fe4ebca11 100644 --- a/src/tools/rustfmt/src/stmt.rs +++ b/src/tools/rustfmt/src/stmt.rs @@ -3,7 +3,7 @@ use rustc_span::Span; use crate::comment::recover_comment_removed; use crate::config::Version; -use crate::expr::{format_expr, ExprType}; +use crate::expr::{format_expr, is_simple_block, ExprType}; use crate::rewrite::{Rewrite, RewriteContext}; use crate::shape::Shape; use crate::source_map::LineRangeUtils; @@ -33,6 +33,21 @@ impl<'a> Stmt<'a> { } } + pub(crate) fn from_simple_block( + context: &RewriteContext<'_>, + block: &'a ast::Block, + attrs: Option<&[ast::Attribute]>, + ) -> Option<Self> { + if is_simple_block(context, block, attrs) { + let inner = &block.stmts[0]; + // Simple blocks only contain one expr and no stmts + let is_last = true; + Some(Stmt { inner, is_last }) + } else { + None + } + } + pub(crate) fn from_ast_node(inner: &'a ast::Stmt, is_last: bool) -> Self { Stmt { inner, is_last } } @@ -80,13 +95,13 @@ impl<'a> Rewrite for Stmt<'a> { } else { ExprType::Statement }; - format_stmt(context, shape, self.as_ast_node(), expr_type) - } -} - -impl Rewrite for ast::Stmt { - fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { - format_stmt(context, shape, self, ExprType::Statement) + format_stmt( + context, + shape, + self.as_ast_node(), + expr_type, + self.is_last_expr(), + ) } } @@ -95,13 +110,14 @@ fn format_stmt( shape: Shape, stmt: &ast::Stmt, expr_type: ExprType, + is_last_expr: bool, ) -> Option<String> { skip_out_of_file_lines_range!(context, stmt.span()); let result = match stmt.kind { ast::StmtKind::Local(ref local) => local.rewrite(context, shape), ast::StmtKind::Expr(ref ex) | ast::StmtKind::Semi(ref ex) => { - let suffix = if semicolon_for_stmt(context, stmt) { + let suffix = if semicolon_for_stmt(context, stmt, is_last_expr) { ";" } else { "" diff --git a/src/tools/rustfmt/src/string.rs b/src/tools/rustfmt/src/string.rs index 78b72a50cb2..cb666fff695 100644 --- a/src/tools/rustfmt/src/string.rs +++ b/src/tools/rustfmt/src/string.rs @@ -1,7 +1,7 @@ // Format string literals. use regex::Regex; -use unicode_categories::UnicodeCategories; +use unicode_properties::{GeneralCategory, UnicodeGeneralCategory}; use unicode_segmentation::UnicodeSegmentation; use crate::config::Config; @@ -366,7 +366,7 @@ fn is_whitespace(grapheme: &str) -> bool { fn is_punctuation(grapheme: &str) -> bool { grapheme .chars() - .all(UnicodeCategories::is_punctuation_other) + .all(|c| c.general_category() == GeneralCategory::OtherPunctuation) } fn graphemes_width(graphemes: &[&str]) -> usize { diff --git a/src/tools/rustfmt/src/test/configuration_snippet.rs b/src/tools/rustfmt/src/test/configuration_snippet.rs index c70b3c5facd..80b61c88a00 100644 --- a/src/tools/rustfmt/src/test/configuration_snippet.rs +++ b/src/tools/rustfmt/src/test/configuration_snippet.rs @@ -233,13 +233,11 @@ impl ConfigCodeBlock { Some(ConfigurationSection::ConfigName(name)) => { assert!( Config::is_valid_name(&name), - "an unknown configuration option was found: {}", - name + "an unknown configuration option was found: {name}" ); assert!( hash_set.remove(&name), - "multiple configuration guides found for option {}", - name + "multiple configuration guides found for option {name}" ); code_block.set_config_name(Some(name)); } @@ -266,7 +264,7 @@ fn configuration_snippet_tests() { // Display results. println!("Ran {} configurations tests.", blocks.len()); - assert_eq!(failures, 0, "{} configurations tests failed", failures); + assert_eq!(failures, 0, "{failures} configurations tests failed"); } // Read Configurations.md and build a `Vec` of `ConfigCodeBlock` structs with one @@ -289,7 +287,7 @@ fn get_code_blocks() -> Vec<ConfigCodeBlock> { for name in hash_set { if !Config::is_hidden_option(&name) { - panic!("{} does not have a configuration guide", name); + panic!("{name} does not have a configuration guide"); } } diff --git a/src/tools/rustfmt/src/test/mod.rs b/src/tools/rustfmt/src/test/mod.rs index 37854ead28b..47f89c1871a 100644 --- a/src/tools/rustfmt/src/test/mod.rs +++ b/src/tools/rustfmt/src/test/mod.rs @@ -47,7 +47,7 @@ const FILE_SKIP_LIST: &[&str] = &[ ]; fn init_log() { - let _ = env_logger::builder().is_test(true).try_init(); + let _ = tracing_subscriber::fmt().with_test_writer().try_init(); } struct TestSetting { @@ -203,8 +203,8 @@ fn coverage_tests() { let files = get_test_files(Path::new("tests/coverage/source"), true); let (_reports, count, fails) = check_files(files, &None); - println!("Ran {} tests in coverage mode.", count); - assert_eq!(fails, 0, "{} tests failed", fails); + println!("Ran {count} tests in coverage mode."); + assert_eq!(fails, 0, "{fails} tests failed"); } #[test] @@ -396,8 +396,8 @@ fn self_tests() { let mut warnings = 0; // Display results. - println!("Ran {} self tests.", count); - assert_eq!(fails, 0, "{} self tests failed", fails); + println!("Ran {count} self tests."); + assert_eq!(fails, 0, "{fails} self tests failed"); for format_report in reports { println!( @@ -407,11 +407,7 @@ fn self_tests() { warnings += format_report.warning_count(); } - assert_eq!( - warnings, 0, - "Rustfmt's code generated {} warnings", - warnings - ); + assert_eq!(warnings, 0, "Rustfmt's code generated {warnings} warnings"); } #[test] @@ -606,7 +602,7 @@ fn stdin_handles_mod_inner_ignore_attr() { fn format_lines_errors_are_reported() { init_log(); let long_identifier = String::from_utf8(vec![b'a'; 239]).unwrap(); - let input = Input::Text(format!("fn {}() {{}}", long_identifier)); + let input = Input::Text(format!("fn {long_identifier}() {{}}")); let mut config = Config::default(); config.set().error_on_line_overflow(true); let mut session = Session::<io::Stdout>::new(config, None); @@ -618,7 +614,7 @@ fn format_lines_errors_are_reported() { fn format_lines_errors_are_reported_with_tabs() { init_log(); let long_identifier = String::from_utf8(vec![b'a'; 97]).unwrap(); - let input = Input::Text(format!("fn a() {{\n\t{}\n}}", long_identifier)); + let input = Input::Text(format!("fn a() {{\n\t{long_identifier}\n}}")); let mut config = Config::default(); config.set().error_on_line_overflow(true); config.set().hard_tabs(true); @@ -829,11 +825,11 @@ fn handle_result( for (file_name, fmt_text) in result { // If file is in tests/source, compare to file with same name in tests/target. let target = get_target(&file_name, target); - let open_error = format!("couldn't open target {:?}", target); + let open_error = format!("couldn't open target {target:?}"); let mut f = fs::File::open(&target).expect(&open_error); let mut text = String::new(); - let read_error = format!("failed reading target {:?}", target); + let read_error = format!("failed reading target {target:?}"); f.read_to_string(&mut text).expect(&read_error); // Ignore LF and CRLF difference for Windows. diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index 5e8edd8f8bf..127aff913e3 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -301,7 +301,7 @@ where let output = match *output { FnRetTy::Ty(ref ty) => { let type_str = ty.rewrite(context, ty_shape)?; - format!(" -> {}", type_str) + format!(" -> {type_str}") } FnRetTy::Default(..) => String::new(), }; @@ -373,7 +373,7 @@ where || !context.use_block_indent() || is_inputs_empty { - format!("({})", list_str) + format!("({list_str})") } else { format!( "({}{}{})", @@ -383,7 +383,7 @@ where ) }; if output.is_empty() || last_line_width(&args) + first_line_width(&output) <= shape.width { - Some(format!("{}{}", args, output)) + Some(format!("{args}{output}")) } else { Some(format!( "{}\n{}{}", @@ -426,12 +426,12 @@ impl Rewrite for ast::WherePredicate { }) => { let type_str = bounded_ty.rewrite(context, shape)?; let colon = type_bound_colon(context).trim_end(); - let lhs = if let Some(lifetime_str) = - rewrite_lifetime_param(context, shape, bound_generic_params) + let lhs = if let Some(binder_str) = + rewrite_bound_params(context, shape, bound_generic_params) { - format!("for<{}> {}{}", lifetime_str, type_str, colon) + format!("for<{binder_str}> {type_str}{colon}") } else { - format!("{}{}", type_str, colon) + format!("{type_str}{colon}") }; rewrite_assign_rhs(context, lhs, bounds, &RhsAssignKind::Bounds, shape)? @@ -657,8 +657,7 @@ impl Rewrite for ast::GenericParam { impl Rewrite for ast::PolyTraitRef { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { - if let Some(lifetime_str) = - rewrite_lifetime_param(context, shape, &self.bound_generic_params) + if let Some(lifetime_str) = rewrite_bound_params(context, shape, &self.bound_generic_params) { // 6 is "for<> ".len() let extra_offset = lifetime_str.len() + 6; @@ -666,7 +665,7 @@ impl Rewrite for ast::PolyTraitRef { .trait_ref .rewrite(context, shape.offset_left(extra_offset)?)?; - Some(format!("for<{}> {}", lifetime_str, path_str)) + Some(format!("for<{lifetime_str}> {path_str}")) } else { self.trait_ref.rewrite(context, shape) } @@ -684,9 +683,11 @@ impl Rewrite for ast::Ty { match self.kind { ast::TyKind::TraitObject(ref bounds, tobj_syntax) => { // we have to consider 'dyn' keyword is used or not!!! - let is_dyn = tobj_syntax == ast::TraitObjectSyntax::Dyn; - // 4 is length of 'dyn ' - let shape = if is_dyn { shape.offset_left(4)? } else { shape }; + let (shape, prefix) = match tobj_syntax { + ast::TraitObjectSyntax::Dyn => (shape.offset_left(4)?, "dyn "), + ast::TraitObjectSyntax::DynStar => (shape.offset_left(5)?, "dyn* "), + ast::TraitObjectSyntax::None => (shape, ""), + }; let mut res = bounds.rewrite(context, shape)?; // We may have falsely removed a trailing `+` inside macro call. if context.inside_macro() && bounds.len() == 1 { @@ -694,11 +695,7 @@ impl Rewrite for ast::Ty { res.push('+'); } } - if is_dyn { - Some(format!("dyn {}", res)) - } else { - Some(res) - } + Some(format!("{prefix}{res}")) } ast::TyKind::Ptr(ref mt) => { let prefix = match mt.mutbl { @@ -794,7 +791,7 @@ impl Rewrite for ast::Ty { if let Some(sh) = shape.sub_width(2) { if let Some(ref s) = ty.rewrite(context, sh) { if !s.contains('\n') { - return Some(format!("({})", s)); + return Some(format!("({s})")); } } } @@ -883,8 +880,7 @@ fn rewrite_bare_fn( let mut result = String::with_capacity(128); - if let Some(ref lifetime_str) = rewrite_lifetime_param(context, shape, &bare_fn.generic_params) - { + if let Some(ref lifetime_str) = rewrite_bound_params(context, shape, &bare_fn.generic_params) { result.push_str("for<"); // 6 = "for<> ".len(), 4 = "for<". // This doesn't work out so nicely for multiline situation with lots of @@ -898,7 +894,6 @@ fn rewrite_bare_fn( result.push_str(&format_extern( bare_fn.ext, context.config.force_explicit_abi(), - false, )); result.push_str("fn"); @@ -1124,16 +1119,15 @@ pub(crate) fn can_be_overflowed_type( } } -/// Returns `None` if there is no `LifetimeDef` in the given generic parameters. -pub(crate) fn rewrite_lifetime_param( +/// Returns `None` if there is no `GenericParam` in the list +pub(crate) fn rewrite_bound_params( context: &RewriteContext<'_>, shape: Shape, generic_params: &[ast::GenericParam], ) -> Option<String> { let result = generic_params .iter() - .filter(|p| matches!(p.kind, ast::GenericParamKind::Lifetime)) - .map(|lt| lt.rewrite(context, shape)) + .map(|param| param.rewrite(context, shape)) .collect::<Option<Vec<_>>>()? .join(", "); if result.is_empty() { diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs index 4fc5a9b6896..79a759d68ce 100644 --- a/src/tools/rustfmt/src/utils.rs +++ b/src/tools/rustfmt/src/utils.rs @@ -69,7 +69,7 @@ pub(crate) fn format_visibility( let path = segments_iter.collect::<Vec<_>>().join("::"); let in_str = if is_keyword(&path) { "" } else { "in " }; - Cow::from(format!("pub({}{}) ", in_str, path)) + Cow::from(format!("pub({in_str}{path}) ")) } } } @@ -131,23 +131,18 @@ pub(crate) fn format_mutability(mutability: ast::Mutability) -> &'static str { } #[inline] -pub(crate) fn format_extern( - ext: ast::Extern, - explicit_abi: bool, - is_mod: bool, -) -> Cow<'static, str> { - let abi = match ext { - ast::Extern::None => "Rust".to_owned(), - ast::Extern::Implicit(_) => "C".to_owned(), - ast::Extern::Explicit(abi, _) => abi.symbol_unescaped.to_string(), - }; - - if abi == "Rust" && !is_mod { - Cow::from("") - } else if abi == "C" && !explicit_abi { - Cow::from("extern ") - } else { - Cow::from(format!(r#"extern "{}" "#, abi)) +pub(crate) fn format_extern(ext: ast::Extern, explicit_abi: bool) -> Cow<'static, str> { + match ext { + ast::Extern::None => Cow::from(""), + ast::Extern::Implicit(_) if explicit_abi => Cow::from("extern \"C\" "), + ast::Extern::Implicit(_) => Cow::from("extern "), + // turn `extern "C"` into `extern` when `explicit_abi` is set to false + ast::Extern::Explicit(abi, _) if abi.symbol_unescaped == sym::C && !explicit_abi => { + Cow::from("extern ") + } + ast::Extern::Explicit(abi, _) => { + Cow::from(format!(r#"extern "{}" "#, abi.symbol_unescaped)) + } } } @@ -292,14 +287,20 @@ pub(crate) fn semicolon_for_expr(context: &RewriteContext<'_>, expr: &ast::Expr) } #[inline] -pub(crate) fn semicolon_for_stmt(context: &RewriteContext<'_>, stmt: &ast::Stmt) -> bool { +pub(crate) fn semicolon_for_stmt( + context: &RewriteContext<'_>, + stmt: &ast::Stmt, + is_last_expr: bool, +) -> bool { match stmt.kind { ast::StmtKind::Semi(ref expr) => match expr.kind { ast::ExprKind::While(..) | ast::ExprKind::Loop(..) | ast::ExprKind::ForLoop(..) => { false } ast::ExprKind::Break(..) | ast::ExprKind::Continue(..) | ast::ExprKind::Ret(..) => { - context.config.trailing_semicolon() + // The only time we can skip the semi-colon is if the config option is set to false + // **and** this is the last expr (even though any following exprs are unreachable) + context.config.trailing_semicolon() || !is_last_expr } _ => true, }, diff --git a/src/tools/rustfmt/tests/cargo-fmt/main.rs b/src/tools/rustfmt/tests/cargo-fmt/main.rs index 701c36fadea..63573bf341b 100644 --- a/src/tools/rustfmt/tests/cargo-fmt/main.rs +++ b/src/tools/rustfmt/tests/cargo-fmt/main.rs @@ -26,7 +26,7 @@ fn cargo_fmt(args: &[&str]) -> (String, String) { String::from_utf8(output.stdout).expect("utf-8"), String::from_utf8(output.stderr).expect("utf-8"), ), - Err(e) => panic!("failed to run `{:?} {:?}`: {}", cmd, args, e), + Err(e) => panic!("failed to run `{cmd:?} {args:?}`: {e}"), } } diff --git a/src/tools/rustfmt/tests/config/issue-5816.toml b/src/tools/rustfmt/tests/config/issue-5816.toml new file mode 100644 index 00000000000..00375746e3f --- /dev/null +++ b/src/tools/rustfmt/tests/config/issue-5816.toml @@ -0,0 +1 @@ +skip_macro_invocations=["*", "println"] diff --git a/src/tools/rustfmt/tests/rustfmt/main.rs b/src/tools/rustfmt/tests/rustfmt/main.rs index 4936a717463..7dcf7c8416e 100644 --- a/src/tools/rustfmt/tests/rustfmt/main.rs +++ b/src/tools/rustfmt/tests/rustfmt/main.rs @@ -27,7 +27,7 @@ fn rustfmt(args: &[&str]) -> (String, String) { String::from_utf8(output.stdout).expect("utf-8"), String::from_utf8(output.stderr).expect("utf-8"), ), - Err(e) => panic!("failed to run `{:?} {:?}`: {}", cmd, args, e), + Err(e) => panic!("failed to run `{cmd:?} {args:?}`: {e}"), } } @@ -71,9 +71,7 @@ fn print_config() { ]); assert!( Path::new("minimal-config").exists(), - "stdout:\n{}\nstderr:\n{}", - stdout, - stderr + "stdout:\n{stdout}\nstderr:\n{stderr}" ); remove_file("minimal-config").unwrap(); } diff --git a/src/tools/rustfmt/tests/source/issue-3984.rs b/src/tools/rustfmt/tests/source/issue-3984.rs new file mode 100644 index 00000000000..c9bcfa40635 --- /dev/null +++ b/src/tools/rustfmt/tests/source/issue-3984.rs @@ -0,0 +1,12 @@ +use a::{item /* comment */}; +use b::{ + a, + // comment + item, +}; +use c::item /* comment */; +use d::item; // really long comment (with `use` exactly 100 characters) ____________________________ + +use std::e::{/* it's a comment! */ bar /* and another */}; +use std::f::{/* it's a comment! */ bar}; +use std::g::{bar /* and another */}; diff --git a/src/tools/rustfmt/tests/source/issue-4808.rs b/src/tools/rustfmt/tests/source/issue-4808.rs new file mode 100644 index 00000000000..93076edcdce --- /dev/null +++ b/src/tools/rustfmt/tests/source/issue-4808.rs @@ -0,0 +1,13 @@ +trait Trait { + fn method(&self) {} +} + +impl<F: Fn() -> T, T> Trait for F {} + +impl Trait for f32 {} + +fn main() { + || 10. .method(); + || .. .method(); + || 1.. .method(); +} diff --git a/src/tools/rustfmt/tests/source/issue-5655/one.rs b/src/tools/rustfmt/tests/source/issue-5655/one.rs new file mode 100644 index 00000000000..1758ec56f8b --- /dev/null +++ b/src/tools/rustfmt/tests/source/issue-5655/one.rs @@ -0,0 +1,9 @@ +// rustfmt-version: One + +fn foo<T>(_: T) +where + T: std::fmt::Debug, + + T: std::fmt::Display, +{ +} diff --git a/src/tools/rustfmt/tests/source/issue-5655/two.rs b/src/tools/rustfmt/tests/source/issue-5655/two.rs new file mode 100644 index 00000000000..e37ebbea8af --- /dev/null +++ b/src/tools/rustfmt/tests/source/issue-5655/two.rs @@ -0,0 +1,9 @@ +// rustfmt-version: Two + +fn foo<T>(_: T) +where + T: std::fmt::Debug, + + T: std::fmt::Display, +{ +} diff --git a/src/tools/rustfmt/tests/source/issue-5791.rs b/src/tools/rustfmt/tests/source/issue-5791.rs new file mode 100644 index 00000000000..40bc6daa9f9 --- /dev/null +++ b/src/tools/rustfmt/tests/source/issue-5791.rs @@ -0,0 +1,3 @@ +pub fn main() { + 0. .to_string(); +} diff --git a/src/tools/rustfmt/tests/source/issue-5835.rs b/src/tools/rustfmt/tests/source/issue-5835.rs new file mode 100644 index 00000000000..3e4da3492ce --- /dev/null +++ b/src/tools/rustfmt/tests/source/issue-5835.rs @@ -0,0 +1,8 @@ +// rustfmt-wrap_comments: true + +/// . a +pub fn foo() {} + +pub fn main() { + // . a +} diff --git a/src/tools/rustfmt/tests/source/issue-5852/default.rs b/src/tools/rustfmt/tests/source/issue-5852/default.rs new file mode 100644 index 00000000000..df84f8f58e1 --- /dev/null +++ b/src/tools/rustfmt/tests/source/issue-5852/default.rs @@ -0,0 +1,104 @@ +use std::{ + fs, + // (temporarily commented, we'll need this again in a second) io, +}; + +use foo::{ + self // this is important +}; + +use foo :: bar +; + +use foo::{bar}; + +use foo::{ + bar + // abc +}; + +use foo::{ + bar, + // abc +}; + +use foo::{ + // 345 + bar +}; + +use foo::{ + self + // abc +}; + +use foo::{ + self, + // abc +}; + +use foo::{ + // 345 + self +}; + +use foo::{ + self // a + , +}; + +use foo::{ self /* a */ }; + +use foo::{ self /* a */, }; + +use foo::{ + // abc + abc::{ + xyz + // 123 + } +}; + +use foo::{ + // abc + bar, + abc +}; + +use foo::{ + bar, + // abc + abc +}; + +use foo::{ + bar, + abc + // abc +}; + +use foo::{ + bar, + abc, + // abc +}; + +use foo::{ + self, + // abc + abc::{ + xyz + // 123 + } +}; + +use foo::{ + self, + // abc + abc::{ + // 123 + xyz + } +}; + +use path::{self /*comment*/,}; diff --git a/src/tools/rustfmt/tests/source/issue-5852/horizontal.rs b/src/tools/rustfmt/tests/source/issue-5852/horizontal.rs new file mode 100644 index 00000000000..63bfb7e57ce --- /dev/null +++ b/src/tools/rustfmt/tests/source/issue-5852/horizontal.rs @@ -0,0 +1,106 @@ +// rustfmt-imports_layout: Horizontal + +use std::{ + fs, + // (temporarily commented, we'll need this again in a second) io, +}; + +use foo::{ + self // this is important +}; + +use foo :: bar +; + +use foo::{bar}; + +use foo::{ + bar + // abc +}; + +use foo::{ + bar, + // abc +}; + +use foo::{ + // 345 + bar +}; + +use foo::{ + self + // abc +}; + +use foo::{ + self, + // abc +}; + +use foo::{ + // 345 + self +}; + +use foo::{ + self // a + , +}; + +use foo::{ self /* a */ }; + +use foo::{ self /* a */, }; + +use foo::{ + // abc + abc::{ + xyz + // 123 + } +}; + +use foo::{ + // abc + bar, + abc +}; + +use foo::{ + bar, + // abc + abc +}; + +use foo::{ + bar, + abc + // abc +}; + +use foo::{ + bar, + abc, + // abc +}; + +use foo::{ + self, + // abc + abc::{ + xyz + // 123 + } +}; + +use foo::{ + self, + // abc + abc::{ + // 123 + xyz + } +}; + +use path::{self /*comment*/,}; diff --git a/src/tools/rustfmt/tests/source/issue-5852/horizontal_vertical.rs b/src/tools/rustfmt/tests/source/issue-5852/horizontal_vertical.rs new file mode 100644 index 00000000000..3f3ce066909 --- /dev/null +++ b/src/tools/rustfmt/tests/source/issue-5852/horizontal_vertical.rs @@ -0,0 +1,106 @@ +// rustfmt-imports_layout: HorizontalVertical + +use std::{ + fs, + // (temporarily commented, we'll need this again in a second) io, +}; + +use foo::{ + self // this is important +}; + +use foo :: bar +; + +use foo::{bar}; + +use foo::{ + bar + // abc +}; + +use foo::{ + bar, + // abc +}; + +use foo::{ + // 345 + bar +}; + +use foo::{ + self + // abc +}; + +use foo::{ + self, + // abc +}; + +use foo::{ + // 345 + self +}; + +use foo::{ + self // a + , +}; + +use foo::{ self /* a */ }; + +use foo::{ self /* a */, }; + +use foo::{ + // abc + abc::{ + xyz + // 123 + } +}; + +use foo::{ + // abc + bar, + abc +}; + +use foo::{ + bar, + // abc + abc +}; + +use foo::{ + bar, + abc + // abc +}; + +use foo::{ + bar, + abc, + // abc +}; + +use foo::{ + self, + // abc + abc::{ + xyz + // 123 + } +}; + +use foo::{ + self, + // abc + abc::{ + // 123 + xyz + } +}; + +use path::{self /*comment*/,}; diff --git a/src/tools/rustfmt/tests/source/issue-5852/issue_example.rs b/src/tools/rustfmt/tests/source/issue-5852/issue_example.rs new file mode 100644 index 00000000000..20c2b764011 --- /dev/null +++ b/src/tools/rustfmt/tests/source/issue-5852/issue_example.rs @@ -0,0 +1,8 @@ +use std::{ + fs, + // (temporarily commented, we'll need this again in a second) io, +}; + +use foo::{ + self // this is important +}; diff --git a/src/tools/rustfmt/tests/source/issue-5852/split.rs b/src/tools/rustfmt/tests/source/issue-5852/split.rs new file mode 100644 index 00000000000..14e9ea1b6ca --- /dev/null +++ b/src/tools/rustfmt/tests/source/issue-5852/split.rs @@ -0,0 +1,111 @@ +// rustfmt-imports_granularity: Item + +use std::{ + fs, + // (temporarily commented, we'll need this again in a second) io, +}; + +use foo::{ + self // this is important +}; + +use foo :: bar +; + +use foo::{bar}; + +use foo::{ + bar + // abc +}; + +use foo::{ + bar, + // abc +}; + +use foo::{ + // 345 + bar +}; + +use foo::{ + self + // abc +}; + +use foo::{ + self, + // abc +}; + +use foo::{ + // 345 + self +}; + +use foo::{ + self // a + , +}; + +use foo::{ self /* a */ }; + +use foo::{ self /* a */, }; + +use foo::{ + // abc + abc::{ + xyz + // 123 + } +}; + +use foo::{ + bar, + abc +}; + +use foo::{ + // abc + bar, + abc +}; + +use foo::{ + bar, + // abc + abc +}; + +use foo::{ + bar, + abc + // abc +}; + +use foo::{ + bar, + abc, + // abc +}; + +use foo::{ + self, + // abc + abc::{ + xyz + // 123 + } +}; + +use foo::{ + self, + // abc + abc::{ + // 123 + xyz + } +}; + +use path::{self /*comment*/,}; diff --git a/src/tools/rustfmt/tests/source/issue-5852/vertical.rs b/src/tools/rustfmt/tests/source/issue-5852/vertical.rs new file mode 100644 index 00000000000..b9ba99889ac --- /dev/null +++ b/src/tools/rustfmt/tests/source/issue-5852/vertical.rs @@ -0,0 +1,106 @@ +// rustfmt-imports_layout: Vertical + +use std::{ + fs, + // (temporarily commented, we'll need this again in a second) io, +}; + +use foo::{ + self // this is important +}; + +use foo :: bar +; + +use foo::{bar}; + +use foo::{ + bar + // abc +}; + +use foo::{ + bar, + // abc +}; + +use foo::{ + // 345 + bar +}; + +use foo::{ + self + // abc +}; + +use foo::{ + self, + // abc +}; + +use foo::{ + // 345 + self +}; + +use foo::{ + self // a + , +}; + +use foo::{ self /* a */ }; + +use foo::{ self /* a */, }; + +use foo::{ + // abc + abc::{ + xyz + // 123 + } +}; + +use foo::{ + // abc + bar, + abc +}; + +use foo::{ + bar, + // abc + abc +}; + +use foo::{ + bar, + abc + // abc +}; + +use foo::{ + bar, + abc, + // abc +}; + +use foo::{ + self, + // abc + abc::{ + xyz + // 123 + } +}; + +use foo::{ + self, + // abc + abc::{ + // 123 + xyz + } +}; + +use path::{self /*comment*/,}; diff --git a/src/tools/rustfmt/tests/source/issue-5935.rs b/src/tools/rustfmt/tests/source/issue-5935.rs new file mode 100644 index 00000000000..a1aac05627a --- /dev/null +++ b/src/tools/rustfmt/tests/source/issue-5935.rs @@ -0,0 +1,9 @@ +struct Regs< + const BEGIN: u64, + const END: u64, + const DIM: usize, + const N: usize = { (END - BEGIN) as usize / (8 * DIM) + 1 }, +> +{ + _foo: u64, +} \ No newline at end of file diff --git a/src/tools/rustfmt/tests/source/issue_5721.rs b/src/tools/rustfmt/tests/source/issue_5721.rs new file mode 100644 index 00000000000..e5ae9612c98 --- /dev/null +++ b/src/tools/rustfmt/tests/source/issue_5721.rs @@ -0,0 +1,8 @@ +#![feature(non_lifetime_binders)] +#![allow(incomplete_features)] + +trait Other<U: ?Sized> {} + +trait Trait<U> +where + for<T> U: Other<T> {} diff --git a/src/tools/rustfmt/tests/source/issue_5730.rs b/src/tools/rustfmt/tests/source/issue_5730.rs new file mode 100644 index 00000000000..9a3f4f0d07a --- /dev/null +++ b/src/tools/rustfmt/tests/source/issue_5730.rs @@ -0,0 +1,3 @@ +macro_rules! statement { + () => {;}; +} diff --git a/src/tools/rustfmt/tests/source/issue_5735.rs b/src/tools/rustfmt/tests/source/issue_5735.rs new file mode 100644 index 00000000000..7708d028bf5 --- /dev/null +++ b/src/tools/rustfmt/tests/source/issue_5735.rs @@ -0,0 +1,6 @@ +fn find_errors(mut self) { + let errors: Vec<> = vec!{ + #[debug_format = "A({})"] + struct A {} + }; +} diff --git a/src/tools/rustfmt/tests/source/issue_5882.rs b/src/tools/rustfmt/tests/source/issue_5882.rs new file mode 100644 index 00000000000..e36f9965438 --- /dev/null +++ b/src/tools/rustfmt/tests/source/issue_5882.rs @@ -0,0 +1,7 @@ +macro_rules!foo{} +macro_rules!bar{/*comment*/} +macro_rules!baz{//comment +} +macro_rules!foobar{ +//comment +} diff --git a/src/tools/rustfmt/tests/source/let_chains.rs b/src/tools/rustfmt/tests/source/let_chains.rs new file mode 100644 index 00000000000..b7c1f811096 --- /dev/null +++ b/src/tools/rustfmt/tests/source/let_chains.rs @@ -0,0 +1,121 @@ +fn main() { + if let x = x && x {} + + if xxx && let x = x {} + + if aaaaaaaaaaaaaaaaaaaaa && aaaaaaaaaaaaaaa && aaaaaaaaa && let Some(x) = xxxxxxxxxxxx && aaaaaaa && let None = aaaaaaaaaa {} + + if aaaaaaaaaaaaaaaaaaaaa && aaaaaaaaaaaaaaa && aaaaaaaaa && let Some(x) = xxxxxxxxxxxx && aaaaaaa && let None = aaaaaaaaaa {} + + if let Some(Struct { x:TS(1,2) }) = path::to::<_>(hehe) + && let [Simple, people] = /* get ready */ create_universe(/* hi */ GreatPowers).initialize_badminton().populate_swamps() && + let everybody = (Loops { hi /*hi*/ , ..loopy() }) && summons::triumphantly() { todo!() } + + if let XXXXXXXXX { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyy, zzzzzzzzzzzzz} = xxxxxxx() + && let Foo = bar() { todo!() } +} + +fn test_single_line_let_chain() { + // first item in let-chain is an ident + if a && let Some(b) = foo() { + } + + // first item in let-chain is a unary ! with an ident + let unary_not = if !from_hir_call + && let Some(p) = parent + { + }; + + // first item in let-chain is a unary * with an ident + let unary_deref = if *some_deref + && let Some(p) = parent + { + }; + + // first item in let-chain is a unary - (neg) with an ident + let unary_neg = if -some_ident + && let Some(p) = parent + { + }; + + // first item in let-chain is a try (?) with an ident + let try_ = if some_try? + && let Some(p) = parent + { + }; + + // first item in let-chain is an ident wrapped in parens + let in_parens = if (some_ident) + && let Some(p) = parent + { + }; + + // first item in let-chain is a ref & with an ident + let _ref = if &some_ref + && let Some(p) = parent + { + }; + + // first item in let-chain is a ref &mut with an ident + let mut_ref = if &mut some_ref + && let Some(p) = parent + { + }; + + // chain unary ref and try + let chain_of_unary_ref_and_try = if !&*some_ref? + && let Some(p) = parent { + }; +} + +fn test_multi_line_let_chain() { + // Can only single line the let-chain if the first item is an ident + if let Some(x) = y && a { + + } + + // More than one let-chain must be formatted on multiple lines + if let Some(x) = y && let Some(a) = b { + + } + + // The ident isn't long enough so we don't wrap the first let-chain + if a && let Some(x) = y && let Some(a) = b { + + } + + // The ident is long enough so both let-chains are wrapped + if aaa && let Some(x) = y && let Some(a) = b { + + } + + // function call + if a() && let Some(x) = y { + + } + + // bool literal + if true && let Some(x) = y { + + } + + // cast to a bool + if 1 as bool && let Some(x) = y { + + } + + // matches! macro call + if matches!(a, some_type) && let Some(x) = y { + + } + + // block expression returning bool + if { true } && let Some(x) = y { + + } + + // field access + if a.x && let Some(x) = y { + + } +} diff --git a/src/tools/rustfmt/tests/source/let_else.rs b/src/tools/rustfmt/tests/source/let_else.rs index 85b3604ad3c..cb2859e805d 100644 --- a/src/tools/rustfmt/tests/source/let_else.rs +++ b/src/tools/rustfmt/tests/source/let_else.rs @@ -160,3 +160,23 @@ fn with_trailing_try_operator() { // Maybe this is a workaround? let Ok(Some(next_bucket)) = ranking_rules[cur_ranking_rule_index].next_bucket(ctx, logger, &ranking_rule_universes[cur_ranking_rule_index]) else { return }; } + +fn issue5901() { + #[cfg(target_os = "linux")] + let Some(x) = foo else { todo!() }; + + #[cfg(target_os = "linux")] + // Some comments between attributes and let-else statement + let Some(x) = foo else { todo!() }; + + #[cfg(target_os = "linux")] + #[cfg(target_arch = "x86_64")] + let Some(x) = foo else { todo!() }; + + // The else block will be multi-lined because attributes and comments before `let` + // are included when calculating max width + #[cfg(target_os = "linux")] + #[cfg(target_arch = "x86_64")] + // Some comments between attributes and let-else statement + let Some(x) = foo() else { todo!() }; +} diff --git a/src/tools/rustfmt/tests/source/let_else_v2.rs b/src/tools/rustfmt/tests/source/let_else_v2.rs new file mode 100644 index 00000000000..a420fbcf95b --- /dev/null +++ b/src/tools/rustfmt/tests/source/let_else_v2.rs @@ -0,0 +1,56 @@ +// rustfmt-version: Two +// rustfmt-single_line_let_else_max_width: 100 + +fn issue5901() { + #[cfg(target_os = "linux")] + let Some(x) = foo else { todo!() }; + + #[cfg(target_os = "linux")] + // Some comments between attributes and let-else statement + let Some(x) = foo else { todo!() }; + + #[cfg(target_os = "linux")] + #[cfg(target_arch = "x86_64")] + let Some(x) = foo else { todo!() }; + + // The else block is multi-lined + #[cfg(target_os = "linux")] + let Some(x) = foo else { return; }; + + // The else block will be single-lined because attributes and comments before `let` + // are no longer included when calculating max width + #[cfg(target_os = "linux")] + #[cfg(target_arch = "x86_64")] + // Some comments between attributes and let-else statement + let Some(x) = foo else { todo!() }; + + // Some more test cases for v2 formatting with attributes + + #[cfg(target_os = "linux")] + #[cfg(target_arch = "x86_64")] + let Some(x) = opt + // pre else keyword line-comment + else { return; }; + + #[cfg(target_os = "linux")] + #[cfg(target_arch = "x86_64")] + let Some(x) = opt else + // post else keyword line-comment + { return; }; + + #[cfg(target_os = "linux")] + #[cfg(target_arch = "x86_64")] + let Foo {x: Bar(..), y: FooBar(..), z: Baz(..)} = opt else { + return; + }; + + #[cfg(target_os = "linux")] + #[cfg(target_arch = "x86_64")] + let Some(Ok((Message::ChangeColor(super::color::Color::Rgb(r, g, b)), Point { x, y, z }))) = opt else { + return; + }; + + #[cfg(target_os = "linux")] + #[cfg(target_arch = "x86_64")] + let Some(x) = very_very_very_very_very_very_very_very_very_very_very_very_long_expression_in_assign_rhs() else { return; }; +} diff --git a/src/tools/rustfmt/tests/source/match.rs b/src/tools/rustfmt/tests/source/match.rs index b5dc9957a2c..d1d8d7f2c36 100644 --- a/src/tools/rustfmt/tests/source/match.rs +++ b/src/tools/rustfmt/tests/source/match.rs @@ -292,6 +292,9 @@ fn guards() { aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa if fooooooooooooooooooooo && (bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb || cccccccccccccccccccccccccccccccccccccccc) => {} + Hi { friend } if let None = friend => {} + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa if let Some(foooooooooooooo) = hiiiiiiiiiiiiiii => {} + aaaaaaaaaaaaaaaaa if let Superman { powers: Some(goteem), .. } = all::get_random_being::<Super>() => {} } } diff --git a/src/tools/rustfmt/tests/source/non-lifetime-binders.rs b/src/tools/rustfmt/tests/source/non-lifetime-binders.rs new file mode 100644 index 00000000000..c26393c8f89 --- /dev/null +++ b/src/tools/rustfmt/tests/source/non-lifetime-binders.rs @@ -0,0 +1,10 @@ +fn main() where for<'a, T: Sized + 'a, const C: usize> [&'a T; C]: Sized { + let x = for<T> + || {}; + + let y: dyn + for<T> Into<T>; + + let z: for<T> + fn(T); +} diff --git a/src/tools/rustfmt/tests/source/skip_macro_invocations/config_file.rs b/src/tools/rustfmt/tests/source/skip_macro_invocations/config_file.rs new file mode 100644 index 00000000000..e0f5ddf52e7 --- /dev/null +++ b/src/tools/rustfmt/tests/source/skip_macro_invocations/config_file.rs @@ -0,0 +1,9 @@ +// rustfmt-unstable: true +// rustfmt-config: issue-5816.toml + +fn main() { + println!( "Hello, world!"); + let x = +7 +; +} diff --git a/src/tools/rustfmt/tests/target/extern-rust.rs b/src/tools/rustfmt/tests/target/extern-rust.rs new file mode 100644 index 00000000000..32824c91203 --- /dev/null +++ b/src/tools/rustfmt/tests/target/extern-rust.rs @@ -0,0 +1 @@ +extern "Rust" fn uwu() {} diff --git a/src/tools/rustfmt/tests/target/issue-3984.rs b/src/tools/rustfmt/tests/target/issue-3984.rs new file mode 100644 index 00000000000..9e700c0f761 --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue-3984.rs @@ -0,0 +1,12 @@ +use a::{item /* comment */}; +use b::{ + a, + // comment + item, +}; +use c::item; /* comment */ +use d::item; // really long comment (with `use` exactly 100 characters) ____________________________ + +use std::e::{/* it's a comment! */ bar /* and another */}; +use std::f::{/* it's a comment! */ bar}; +use std::g::{bar /* and another */}; diff --git a/src/tools/rustfmt/tests/target/issue-4808.rs b/src/tools/rustfmt/tests/target/issue-4808.rs new file mode 100644 index 00000000000..cdef53a1bfc --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue-4808.rs @@ -0,0 +1,13 @@ +trait Trait { + fn method(&self) {} +} + +impl<F: Fn() -> T, T> Trait for F {} + +impl Trait for f32 {} + +fn main() { + || (10.).method(); + (|| ..).method(); + (|| 1..).method(); +} diff --git a/src/tools/rustfmt/tests/target/issue-5568.rs b/src/tools/rustfmt/tests/target/issue-5568.rs new file mode 100644 index 00000000000..03ca3a4523c --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue-5568.rs @@ -0,0 +1,14 @@ +// rustfmt-max_width: 119 +// rustfmt-format_code_in_doc_comments: true + +mod libs { + fn mrbgems_sources() { + [ + "mrbgems/mruby-compiler/core/codegen.c", // Ruby parser and bytecode generation + "mrbgems/mruby-compiler/core/y.tab.c", // Ruby parser and bytecode generation + "mrbgems/mruby-metaprog/src/metaprog.c", // APIs on Kernel and Module for accessing classes and variables + "mrbgems/mruby-method/src/method.c", // `Method`, `UnboundMethod`, and method APIs on Kernel and Module + "mrbgems/mruby-pack/src/pack.c", // Array#pack and String#unpack + ] + } +} diff --git a/src/tools/rustfmt/tests/target/issue-5655/one.rs b/src/tools/rustfmt/tests/target/issue-5655/one.rs new file mode 100644 index 00000000000..1758ec56f8b --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue-5655/one.rs @@ -0,0 +1,9 @@ +// rustfmt-version: One + +fn foo<T>(_: T) +where + T: std::fmt::Debug, + + T: std::fmt::Display, +{ +} diff --git a/src/tools/rustfmt/tests/target/issue-5655/two.rs b/src/tools/rustfmt/tests/target/issue-5655/two.rs new file mode 100644 index 00000000000..14fbc3d1321 --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue-5655/two.rs @@ -0,0 +1,8 @@ +// rustfmt-version: Two + +fn foo<T>(_: T) +where + T: std::fmt::Debug, + T: std::fmt::Display, +{ +} diff --git a/src/tools/rustfmt/tests/target/issue-5791.rs b/src/tools/rustfmt/tests/target/issue-5791.rs new file mode 100644 index 00000000000..3a44cf19a1b --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue-5791.rs @@ -0,0 +1,3 @@ +pub fn main() { + (0.).to_string(); +} diff --git a/src/tools/rustfmt/tests/target/issue-5797/retain_trailing_semicolon.rs b/src/tools/rustfmt/tests/target/issue-5797/retain_trailing_semicolon.rs new file mode 100644 index 00000000000..851073971f6 --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue-5797/retain_trailing_semicolon.rs @@ -0,0 +1,7 @@ +// rustfmt-trailing_semicolon: false + +fn foo() {} +fn main() { + return; + foo() +} diff --git a/src/tools/rustfmt/tests/target/issue-5835.rs b/src/tools/rustfmt/tests/target/issue-5835.rs new file mode 100644 index 00000000000..3e4da3492ce --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue-5835.rs @@ -0,0 +1,8 @@ +// rustfmt-wrap_comments: true + +/// . a +pub fn foo() {} + +pub fn main() { + // . a +} diff --git a/src/tools/rustfmt/tests/target/issue-5852/default.rs b/src/tools/rustfmt/tests/target/issue-5852/default.rs new file mode 100644 index 00000000000..a86872a68bf --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue-5852/default.rs @@ -0,0 +1,97 @@ +use std::{ + fs, + // (temporarily commented, we'll need this again in a second) io, +}; + +use foo::{ + self, // this is important +}; + +use foo::bar; + +use foo::bar; + +use foo::{ + bar, // abc +}; + +use foo::{ + bar, + // abc +}; + +use foo::{ + // 345 + bar, +}; + +use foo::{ + self, // abc +}; + +use foo::{ + self, + // abc +}; + +use foo::{ + // 345 + self, +}; + +use foo::{ + self, // a +}; + +use foo::{self /* a */}; + +use foo::{self /* a */}; + +use foo::{ + // abc + abc::{ + xyz, // 123 + }, +}; + +use foo::{ + abc, + // abc + bar, +}; + +use foo::{ + // abc + abc, + bar, +}; + +use foo::{ + abc, // abc + bar, +}; + +use foo::{ + abc, + // abc + bar, +}; + +use foo::{ + self, + // abc + abc::{ + xyz, // 123 + }, +}; + +use foo::{ + self, + // abc + abc::{ + // 123 + xyz, + }, +}; + +use path::{self /*comment*/}; diff --git a/src/tools/rustfmt/tests/target/issue-5852/horizontal.rs b/src/tools/rustfmt/tests/target/issue-5852/horizontal.rs new file mode 100644 index 00000000000..017d83c9f9a --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue-5852/horizontal.rs @@ -0,0 +1,99 @@ +// rustfmt-imports_layout: Horizontal + +use std::{ + fs, + // (temporarily commented, we'll need this again in a second) io, +}; + +use foo::{ + self, // this is important +}; + +use foo::bar; + +use foo::bar; + +use foo::{ + bar, // abc +}; + +use foo::{ + bar, + // abc +}; + +use foo::{ + // 345 + bar, +}; + +use foo::{ + self, // abc +}; + +use foo::{ + self, + // abc +}; + +use foo::{ + // 345 + self, +}; + +use foo::{ + self, // a +}; + +use foo::{self /* a */}; + +use foo::{self /* a */}; + +use foo::{ + // abc + abc::{ + xyz, // 123 + }, +}; + +use foo::{ + abc, + // abc + bar, +}; + +use foo::{ + // abc + abc, + bar, +}; + +use foo::{ + abc, // abc + bar, +}; + +use foo::{ + abc, + // abc + bar, +}; + +use foo::{ + self, + // abc + abc::{ + xyz, // 123 + }, +}; + +use foo::{ + self, + // abc + abc::{ + // 123 + xyz, + }, +}; + +use path::{self /*comment*/}; diff --git a/src/tools/rustfmt/tests/target/issue-5852/horizontal_vertical.rs b/src/tools/rustfmt/tests/target/issue-5852/horizontal_vertical.rs new file mode 100644 index 00000000000..35e2d0a2624 --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue-5852/horizontal_vertical.rs @@ -0,0 +1,99 @@ +// rustfmt-imports_layout: HorizontalVertical + +use std::{ + fs, + // (temporarily commented, we'll need this again in a second) io, +}; + +use foo::{ + self, // this is important +}; + +use foo::bar; + +use foo::bar; + +use foo::{ + bar, // abc +}; + +use foo::{ + bar, + // abc +}; + +use foo::{ + // 345 + bar, +}; + +use foo::{ + self, // abc +}; + +use foo::{ + self, + // abc +}; + +use foo::{ + // 345 + self, +}; + +use foo::{ + self, // a +}; + +use foo::{self /* a */}; + +use foo::{self /* a */}; + +use foo::{ + // abc + abc::{ + xyz, // 123 + }, +}; + +use foo::{ + abc, + // abc + bar, +}; + +use foo::{ + // abc + abc, + bar, +}; + +use foo::{ + abc, // abc + bar, +}; + +use foo::{ + abc, + // abc + bar, +}; + +use foo::{ + self, + // abc + abc::{ + xyz, // 123 + }, +}; + +use foo::{ + self, + // abc + abc::{ + // 123 + xyz, + }, +}; + +use path::{self /*comment*/}; diff --git a/src/tools/rustfmt/tests/target/issue-5852/issue_example.rs b/src/tools/rustfmt/tests/target/issue-5852/issue_example.rs new file mode 100644 index 00000000000..46326291433 --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue-5852/issue_example.rs @@ -0,0 +1,8 @@ +use std::{ + fs, + // (temporarily commented, we'll need this again in a second) io, +}; + +use foo::{ + self, // this is important +}; diff --git a/src/tools/rustfmt/tests/target/issue-5852/split.rs b/src/tools/rustfmt/tests/target/issue-5852/split.rs new file mode 100644 index 00000000000..e00086dd47b --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue-5852/split.rs @@ -0,0 +1,102 @@ +// rustfmt-imports_granularity: Item + +use std::{ + fs, + // (temporarily commented, we'll need this again in a second) io, +}; + +use foo::{ + self, // this is important +}; + +use foo::bar; + +use foo::bar; + +use foo::{ + bar, // abc +}; + +use foo::{ + bar, + // abc +}; + +use foo::{ + // 345 + bar, +}; + +use foo::{ + self, // abc +}; + +use foo::{ + self, + // abc +}; + +use foo::{ + // 345 + self, +}; + +use foo::{ + self, // a +}; + +use foo::{self /* a */}; + +use foo::{self /* a */}; + +use foo::{ + // abc + abc::{ + xyz, // 123 + }, +}; + +use foo::abc; +use foo::bar; + +use foo::{ + abc, + // abc + bar, +}; + +use foo::{ + // abc + abc, + bar, +}; + +use foo::{ + abc, // abc + bar, +}; + +use foo::{ + abc, + // abc + bar, +}; + +use foo::{ + self, + // abc + abc::{ + xyz, // 123 + }, +}; + +use foo::{ + self, + // abc + abc::{ + // 123 + xyz, + }, +}; + +use path::{self /*comment*/}; diff --git a/src/tools/rustfmt/tests/target/issue-5852/vertical.rs b/src/tools/rustfmt/tests/target/issue-5852/vertical.rs new file mode 100644 index 00000000000..f53a68c944a --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue-5852/vertical.rs @@ -0,0 +1,105 @@ +// rustfmt-imports_layout: Vertical + +use std::{ + fs, + // (temporarily commented, we'll need this again in a second) io, +}; + +use foo::{ + self, // this is important +}; + +use foo::bar; + +use foo::bar; + +use foo::{ + bar, // abc +}; + +use foo::{ + bar, + // abc +}; + +use foo::{ + // 345 + bar, +}; + +use foo::{ + self, // abc +}; + +use foo::{ + self, + // abc +}; + +use foo::{ + // 345 + self, +}; + +use foo::{ + self, // a +}; + +use foo::{ + self, /* a */ +}; + +use foo::{ + self, /* a */ +}; + +use foo::{ + // abc + abc::{ + xyz, // 123 + }, +}; + +use foo::{ + abc, + // abc + bar, +}; + +use foo::{ + // abc + abc, + bar, +}; + +use foo::{ + abc, // abc + bar, +}; + +use foo::{ + abc, + // abc + bar, +}; + +use foo::{ + self, + // abc + abc::{ + xyz, // 123 + }, +}; + +use foo::{ + self, + // abc + abc::{ + // 123 + xyz, + }, +}; + +use path::{ + self, /*comment*/ +}; diff --git a/src/tools/rustfmt/tests/target/issue-5871.rs b/src/tools/rustfmt/tests/target/issue-5871.rs new file mode 100644 index 00000000000..3116533bcb8 --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue-5871.rs @@ -0,0 +1,8 @@ +#![feature(stmt_expr_attributes)] +fn okay() -> u32 { + ( + // Comments in parentheses-expressions caused attributes to be duplicated. + #[allow(unused_variables)] + 0 + ) +} diff --git a/src/tools/rustfmt/tests/target/issue-5935.rs b/src/tools/rustfmt/tests/target/issue-5935.rs new file mode 100644 index 00000000000..ebc62c46466 --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue-5935.rs @@ -0,0 +1,8 @@ +struct Regs< + const BEGIN: u64, + const END: u64, + const DIM: usize, + const N: usize = { (END - BEGIN) as usize / (8 * DIM) + 1 }, +> { + _foo: u64, +} diff --git a/src/tools/rustfmt/tests/target/issue_5533.rs b/src/tools/rustfmt/tests/target/issue_5533.rs new file mode 100644 index 00000000000..c3095a44022 --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue_5533.rs @@ -0,0 +1,6 @@ +// rustfmt-format_code_in_doc_comments: true + +struct TestStruct { + position_currency: String, // Currency for position of this contract. If not null, 1 contract = 1 positionCurrency. + pu: Option<i64>, // Previous event update sequense ("u" of previous message), -1 also means None +} diff --git a/src/tools/rustfmt/tests/target/issue_5542.rs b/src/tools/rustfmt/tests/target/issue_5542.rs new file mode 100644 index 00000000000..730bb7b681a --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue_5542.rs @@ -0,0 +1,10 @@ +#![feature(dyn_star)] +#![allow(incomplete_features)] + +use core::fmt::Debug; + +fn main() { + let i = 42; + let dyn_i = i as dyn* Debug; + dbg!(dyn_i); +} diff --git a/src/tools/rustfmt/tests/target/issue_5676.rs b/src/tools/rustfmt/tests/target/issue_5676.rs new file mode 100644 index 00000000000..25877110545 --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue_5676.rs @@ -0,0 +1,8 @@ +fn main() { + match true { + true => 'a: { + break 'a; + } + _ => (), + } +} diff --git a/src/tools/rustfmt/tests/target/issue_5721.rs b/src/tools/rustfmt/tests/target/issue_5721.rs new file mode 100644 index 00000000000..d073b09cac2 --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue_5721.rs @@ -0,0 +1,10 @@ +#![feature(non_lifetime_binders)] +#![allow(incomplete_features)] + +trait Other<U: ?Sized> {} + +trait Trait<U> +where + for<T> U: Other<T>, +{ +} diff --git a/src/tools/rustfmt/tests/target/issue_5730.rs b/src/tools/rustfmt/tests/target/issue_5730.rs new file mode 100644 index 00000000000..7922fdcc90f --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue_5730.rs @@ -0,0 +1,3 @@ +macro_rules! statement { + () => {}; +} diff --git a/src/tools/rustfmt/tests/target/issue_5735.rs b/src/tools/rustfmt/tests/target/issue_5735.rs new file mode 100644 index 00000000000..2d1376303f5 --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue_5735.rs @@ -0,0 +1,6 @@ +fn find_errors(mut self) { + let errors: Vec = vec![ + #[debug_format = "A({})"] + struct A {} + ]; +} diff --git a/src/tools/rustfmt/tests/target/issue_5882.rs b/src/tools/rustfmt/tests/target/issue_5882.rs new file mode 100644 index 00000000000..565fb434a6a --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue_5882.rs @@ -0,0 +1,7 @@ +macro_rules! foo {} +macro_rules! bar { /*comment*/ } +macro_rules! baz { //comment +} +macro_rules! foobar { + //comment +} diff --git a/src/tools/rustfmt/tests/target/issue_5907.rs b/src/tools/rustfmt/tests/target/issue_5907.rs new file mode 100644 index 00000000000..144de636b9b --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue_5907.rs @@ -0,0 +1,6 @@ +// rustfmt-format_code_in_doc_comments: true + +// ``` +// [ +// ] +// ``` diff --git a/src/tools/rustfmt/tests/target/let_chains.rs b/src/tools/rustfmt/tests/target/let_chains.rs new file mode 100644 index 00000000000..1ceecac8abc --- /dev/null +++ b/src/tools/rustfmt/tests/target/let_chains.rs @@ -0,0 +1,129 @@ +fn main() { + if let x = x + && x + {} + + if xxx && let x = x {} + + if aaaaaaaaaaaaaaaaaaaaa + && aaaaaaaaaaaaaaa + && aaaaaaaaa + && let Some(x) = xxxxxxxxxxxx + && aaaaaaa + && let None = aaaaaaaaaa + {} + + if aaaaaaaaaaaaaaaaaaaaa + && aaaaaaaaaaaaaaa + && aaaaaaaaa + && let Some(x) = xxxxxxxxxxxx + && aaaaaaa + && let None = aaaaaaaaaa + {} + + if let Some(Struct { x: TS(1, 2) }) = path::to::<_>(hehe) + && let [Simple, people] = /* get ready */ + create_universe(/* hi */ GreatPowers) + .initialize_badminton() + .populate_swamps() + && let everybody = (Loops { + hi, /*hi*/ + ..loopy() + }) + && summons::triumphantly() + { + todo!() + } + + if let XXXXXXXXX { + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, + yyyyyyyyyyyyy, + zzzzzzzzzzzzz, + } = xxxxxxx() + && let Foo = bar() + { + todo!() + } +} + +fn test_single_line_let_chain() { + // first item in let-chain is an ident + if a && let Some(b) = foo() {} + + // first item in let-chain is a unary ! with an ident + let unary_not = if !from_hir_call && let Some(p) = parent {}; + + // first item in let-chain is a unary * with an ident + let unary_deref = if *some_deref && let Some(p) = parent {}; + + // first item in let-chain is a unary - (neg) with an ident + let unary_neg = if -some_ident && let Some(p) = parent {}; + + // first item in let-chain is a try (?) with an ident + let try_ = if some_try? && let Some(p) = parent {}; + + // first item in let-chain is an ident wrapped in parens + let in_parens = if (some_ident) && let Some(p) = parent {}; + + // first item in let-chain is a ref & with an ident + let _ref = if &some_ref && let Some(p) = parent {}; + + // first item in let-chain is a ref &mut with an ident + let mut_ref = if &mut some_ref && let Some(p) = parent {}; + + // chain unary ref and try + let chain_of_unary_ref_and_try = if !&*some_ref? && let Some(p) = parent {}; +} + +fn test_multi_line_let_chain() { + // Can only single line the let-chain if the first item is an ident + if let Some(x) = y + && a + {} + + // More than one let-chain must be formatted on multiple lines + if let Some(x) = y + && let Some(a) = b + {} + + // The ident isn't long enough so we don't wrap the first let-chain + if a && let Some(x) = y + && let Some(a) = b + {} + + // The ident is long enough so both let-chains are wrapped + if aaa + && let Some(x) = y + && let Some(a) = b + {} + + // function call + if a() + && let Some(x) = y + {} + + // bool literal + if true + && let Some(x) = y + {} + + // cast to a bool + if 1 as bool + && let Some(x) = y + {} + + // matches! macro call + if matches!(a, some_type) + && let Some(x) = y + {} + + // block expression returning bool + if { true } + && let Some(x) = y + {} + + // field access + if a.x + && let Some(x) = y + {} +} diff --git a/src/tools/rustfmt/tests/target/let_else.rs b/src/tools/rustfmt/tests/target/let_else.rs index 6554a0961c0..f6560e85462 100644 --- a/src/tools/rustfmt/tests/target/let_else.rs +++ b/src/tools/rustfmt/tests/target/let_else.rs @@ -252,3 +252,34 @@ fn with_trailing_try_operator() { return; }; } + +fn issue5901() { + #[cfg(target_os = "linux")] + let Some(x) = foo + else { + todo!() + }; + + #[cfg(target_os = "linux")] + // Some comments between attributes and let-else statement + let Some(x) = foo + else { + todo!() + }; + + #[cfg(target_os = "linux")] + #[cfg(target_arch = "x86_64")] + let Some(x) = foo + else { + todo!() + }; + + // The else block will be multi-lined because attributes and comments before `let` + // are included when calculating max width + #[cfg(target_os = "linux")] + #[cfg(target_arch = "x86_64")] + // Some comments between attributes and let-else statement + let Some(x) = foo() else { + todo!() + }; +} diff --git a/src/tools/rustfmt/tests/target/let_else_v2.rs b/src/tools/rustfmt/tests/target/let_else_v2.rs new file mode 100644 index 00000000000..b25ac1609d8 --- /dev/null +++ b/src/tools/rustfmt/tests/target/let_else_v2.rs @@ -0,0 +1,73 @@ +// rustfmt-version: Two +// rustfmt-single_line_let_else_max_width: 100 + +fn issue5901() { + #[cfg(target_os = "linux")] + let Some(x) = foo else { todo!() }; + + #[cfg(target_os = "linux")] + // Some comments between attributes and let-else statement + let Some(x) = foo else { todo!() }; + + #[cfg(target_os = "linux")] + #[cfg(target_arch = "x86_64")] + let Some(x) = foo else { todo!() }; + + // The else block is multi-lined + #[cfg(target_os = "linux")] + let Some(x) = foo else { + return; + }; + + // The else block will be single-lined because attributes and comments before `let` + // are no longer included when calculating max width + #[cfg(target_os = "linux")] + #[cfg(target_arch = "x86_64")] + // Some comments between attributes and let-else statement + let Some(x) = foo else { todo!() }; + + // Some more test cases for v2 formatting with attributes + + #[cfg(target_os = "linux")] + #[cfg(target_arch = "x86_64")] + let Some(x) = opt + // pre else keyword line-comment + else { + return; + }; + + #[cfg(target_os = "linux")] + #[cfg(target_arch = "x86_64")] + let Some(x) = opt else + // post else keyword line-comment + { + return; + }; + + #[cfg(target_os = "linux")] + #[cfg(target_arch = "x86_64")] + let Foo { + x: Bar(..), + y: FooBar(..), + z: Baz(..), + } = opt + else { + return; + }; + + #[cfg(target_os = "linux")] + #[cfg(target_arch = "x86_64")] + let Some(Ok((Message::ChangeColor(super::color::Color::Rgb(r, g, b)), Point { x, y, z }))) = + opt + else { + return; + }; + + #[cfg(target_os = "linux")] + #[cfg(target_arch = "x86_64")] + let Some(x) = + very_very_very_very_very_very_very_very_very_very_very_very_long_expression_in_assign_rhs() + else { + return; + }; +} diff --git a/src/tools/rustfmt/tests/target/match.rs b/src/tools/rustfmt/tests/target/match.rs index 1bf3fb758ee..0e7815a814d 100644 --- a/src/tools/rustfmt/tests/target/match.rs +++ b/src/tools/rustfmt/tests/target/match.rs @@ -317,6 +317,14 @@ fn guards() { if fooooooooooooooooooooo && (bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb || cccccccccccccccccccccccccccccccccccccccc) => {} + Hi { friend } if let None = friend => {} + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + if let Some(foooooooooooooo) = hiiiiiiiiiiiiiii => {} + aaaaaaaaaaaaaaaaa + if let Superman { + powers: Some(goteem), + .. + } = all::get_random_being::<Super>() => {} } } diff --git a/src/tools/rustfmt/tests/target/non-lifetime-binders.rs b/src/tools/rustfmt/tests/target/non-lifetime-binders.rs new file mode 100644 index 00000000000..ca6941a0c00 --- /dev/null +++ b/src/tools/rustfmt/tests/target/non-lifetime-binders.rs @@ -0,0 +1,10 @@ +fn main() +where + for<'a, T: Sized + 'a, const C: usize> [&'a T; C]: Sized, +{ + let x = for<T> || {}; + + let y: dyn for<T> Into<T>; + + let z: for<T> fn(T); +} diff --git a/src/tools/rustfmt/tests/target/skip_macro_invocations/config_file.rs b/src/tools/rustfmt/tests/target/skip_macro_invocations/config_file.rs new file mode 100644 index 00000000000..008e28db42d --- /dev/null +++ b/src/tools/rustfmt/tests/target/skip_macro_invocations/config_file.rs @@ -0,0 +1,7 @@ +// rustfmt-unstable: true +// rustfmt-config: issue-5816.toml + +fn main() { + println!( "Hello, world!"); + let x = 7; +} diff --git a/src/tools/rustfmt/triagebot.toml b/src/tools/rustfmt/triagebot.toml index fa0824ac53c..b8691192e24 100644 --- a/src/tools/rustfmt/triagebot.toml +++ b/src/tools/rustfmt/triagebot.toml @@ -1 +1,4 @@ +[autolabel."pr-not-reviewed"] +new_pr = true + [assign] diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 2b828e58d54..dcf218bc189 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -11,7 +11,7 @@ use std::path::{Path, PathBuf}; const ENTRY_LIMIT: usize = 900; // FIXME: The following limits should be reduced eventually. const ISSUES_ENTRY_LIMIT: usize = 1854; -const ROOT_ENTRY_LIMIT: usize = 865; +const ROOT_ENTRY_LIMIT: usize = 866; const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[ "rs", // test source files diff --git a/tests/codegen/instrument-coverage-off.rs b/tests/codegen/instrument-coverage-off.rs new file mode 100644 index 00000000000..ca803beec0b --- /dev/null +++ b/tests/codegen/instrument-coverage-off.rs @@ -0,0 +1,23 @@ +// Test that `-Cinstrument-coverage=off` does not add coverage instrumentation to LLVM IR. + +// needs-profiler-support +// revisions: n no off false zero +// [n] compile-flags: -Cinstrument-coverage=n +// [no] compile-flags: -Cinstrument-coverage=no +// [off] compile-flags: -Cinstrument-coverage=off +// [false] compile-flags: -Cinstrument-coverage=false +// [zero] compile-flags: -Cinstrument-coverage=0 + +// CHECK-NOT: __llvm_profile_filename +// CHECK-NOT: __llvm_coverage_mapping + +#![crate_type="lib"] + +#[inline(never)] +fn some_function() { + +} + +pub fn some_other_function() { + some_function(); +} diff --git a/tests/codegen/instrument-coverage.rs b/tests/codegen/instrument-coverage.rs index 78f8875a2d9..f8437dac463 100644 --- a/tests/codegen/instrument-coverage.rs +++ b/tests/codegen/instrument-coverage.rs @@ -1,9 +1,16 @@ // Test that `-Cinstrument-coverage` creates expected __llvm_profile_filename symbol in LLVM IR. // needs-profiler-support -// compile-flags: -Cinstrument-coverage +// revisions: default y yes on true all +// [default] compile-flags: -Cinstrument-coverage +// [y] compile-flags: -Cinstrument-coverage=y +// [yes] compile-flags: -Cinstrument-coverage=yes +// [on] compile-flags: -Cinstrument-coverage=on +// [true] compile-flags: -Cinstrument-coverage=true +// [all] compile-flags: -Cinstrument-coverage=all // CHECK: @__llvm_profile_filename = {{.*}}"default_%m_%p.profraw\00"{{.*}} +// CHECK: @__llvm_coverage_mapping #![crate_type="lib"] diff --git a/tests/mir-opt/inline/unsized_argument.caller.Inline.diff b/tests/mir-opt/inline/unsized_argument.caller.Inline.diff index ab81f707148..37083973fd1 100644 --- a/tests/mir-opt/inline/unsized_argument.caller.Inline.diff +++ b/tests/mir-opt/inline/unsized_argument.caller.Inline.diff @@ -6,24 +6,18 @@ let mut _0: (); let _2: (); let mut _3: std::boxed::Box<[i32]>; - let mut _4: &mut std::boxed::Box<[i32]>; - let mut _5: (); - let mut _6: &mut std::boxed::Box<[i32]>; - let mut _7: (); - let mut _8: &mut std::boxed::Box<[i32]>; - let mut _9: (); - let mut _10: *const [i32]; + let mut _4: *const [i32]; bb0: { StorageLive(_2); StorageLive(_3); _3 = move _1; - _10 = (((_3.0: std::ptr::Unique<[i32]>).0: std::ptr::NonNull<[i32]>).0: *const [i32]); - _2 = callee(move (*_10)) -> [return: bb3, unwind: bb4]; + _4 = (((_3.0: std::ptr::Unique<[i32]>).0: std::ptr::NonNull<[i32]>).0: *const [i32]); + _2 = callee(move (*_4)) -> [return: bb1, unwind: bb3]; } - bb1 (cleanup): { - resume; + bb1: { + drop(_3) -> [return: bb2, unwind: bb4]; } bb2: { @@ -33,14 +27,12 @@ return; } - bb3: { - _4 = &mut _3; - _5 = <Box<[i32]> as Drop>::drop(move _4) -> [return: bb2, unwind: bb1]; + bb3 (cleanup): { + drop(_3) -> [return: bb4, unwind terminate(cleanup)]; } bb4 (cleanup): { - _8 = &mut _3; - _9 = <Box<[i32]> as Drop>::drop(move _8) -> [return: bb1, unwind terminate(cleanup)]; + resume; } } diff --git a/tests/mir-opt/issue_41888.main.ElaborateDrops.panic-abort.diff b/tests/mir-opt/issue_41888.main.ElaborateDrops.panic-abort.diff index b57fe348c2d..ae18ddc8366 100644 --- a/tests/mir-opt/issue_41888.main.ElaborateDrops.panic-abort.diff +++ b/tests/mir-opt/issue_41888.main.ElaborateDrops.panic-abort.diff @@ -10,9 +10,8 @@ let mut _5: isize; + let mut _7: bool; + let mut _8: bool; -+ let mut _9: bool; ++ let mut _9: isize; + let mut _10: isize; -+ let mut _11: isize; scope 1 { debug e => _1; scope 2 { @@ -24,7 +23,6 @@ bb0: { + _7 = const false; + _8 = const false; -+ _9 = const false; StorageLive(_1); StorageLive(_2); _2 = cond() -> [return: bb1, unwind: bb11]; @@ -47,7 +45,6 @@ bb3: { + _7 = const true; + _8 = const true; -+ _9 = const true; _1 = move _3; - drop(_3) -> [return: bb5, unwind: bb11]; + goto -> bb5; @@ -56,7 +53,6 @@ bb4 (cleanup): { + _7 = const true; + _8 = const true; -+ _9 = const true; _1 = move _3; - drop(_3) -> [return: bb11, unwind terminate(cleanup)]; + goto -> bb11; @@ -70,7 +66,6 @@ bb6: { StorageLive(_6); -+ _9 = const false; _6 = move ((_1 as F).0: K); _0 = const (); StorageDead(_6); @@ -90,13 +85,12 @@ bb9: { StorageDead(_2); - drop(_1) -> [return: bb10, unwind: bb12]; -+ goto -> bb18; ++ goto -> bb19; } bb10: { + _7 = const false; + _8 = const false; -+ _9 = const false; StorageDead(_1); return; } @@ -116,33 +110,37 @@ + } + + bb14 (cleanup): { -+ goto -> bb12; ++ drop(((_1 as F).0: K)) -> [return: bb12, unwind terminate(cleanup)]; + } + -+ bb15: { -+ drop(_1) -> [return: bb13, unwind: bb12]; ++ bb15 (cleanup): { ++ switchInt(_7) -> [0: bb12, otherwise: bb14]; + } + -+ bb16 (cleanup): { -+ drop(_1) -> [return: bb12, unwind terminate(cleanup)]; ++ bb16: { ++ drop(_1) -> [return: bb13, unwind: bb12]; + } + -+ bb17: { -+ _10 = discriminant(_1); -+ switchInt(move _10) -> [0: bb13, otherwise: bb15]; ++ bb17 (cleanup): { ++ drop(_1) -> [return: bb12, unwind terminate(cleanup)]; + } + + bb18: { -+ switchInt(_7) -> [0: bb13, otherwise: bb17]; ++ _9 = discriminant(_1); ++ switchInt(move _9) -> [0: bb13, otherwise: bb16]; + } + -+ bb19 (cleanup): { -+ _11 = discriminant(_1); -+ switchInt(move _11) -> [0: bb14, otherwise: bb16]; ++ bb19: { ++ switchInt(_7) -> [0: bb13, otherwise: bb18]; + } + + bb20 (cleanup): { -+ switchInt(_7) -> [0: bb12, otherwise: bb19]; ++ _10 = discriminant(_1); ++ switchInt(move _10) -> [0: bb15, otherwise: bb17]; ++ } ++ ++ bb21 (cleanup): { ++ switchInt(_7) -> [0: bb12, otherwise: bb20]; } } diff --git a/tests/mir-opt/issue_41888.main.ElaborateDrops.panic-unwind.diff b/tests/mir-opt/issue_41888.main.ElaborateDrops.panic-unwind.diff index 2156850e38c..d08113c0ba5 100644 --- a/tests/mir-opt/issue_41888.main.ElaborateDrops.panic-unwind.diff +++ b/tests/mir-opt/issue_41888.main.ElaborateDrops.panic-unwind.diff @@ -10,9 +10,8 @@ let mut _5: isize; + let mut _7: bool; + let mut _8: bool; -+ let mut _9: bool; ++ let mut _9: isize; + let mut _10: isize; -+ let mut _11: isize; scope 1 { debug e => _1; scope 2 { @@ -24,7 +23,6 @@ bb0: { + _7 = const false; + _8 = const false; -+ _9 = const false; StorageLive(_1); StorageLive(_2); _2 = cond() -> [return: bb1, unwind: bb11]; @@ -47,7 +45,6 @@ bb3: { + _7 = const true; + _8 = const true; -+ _9 = const true; _1 = move _3; - drop(_3) -> [return: bb5, unwind: bb11]; + goto -> bb5; @@ -56,7 +53,6 @@ bb4 (cleanup): { + _7 = const true; + _8 = const true; -+ _9 = const true; _1 = move _3; - drop(_3) -> [return: bb11, unwind terminate(cleanup)]; + goto -> bb11; @@ -70,7 +66,6 @@ bb6: { StorageLive(_6); -+ _9 = const false; _6 = move ((_1 as F).0: K); _0 = const (); StorageDead(_6); @@ -90,13 +85,12 @@ bb9: { StorageDead(_2); - drop(_1) -> [return: bb10, unwind continue]; -+ goto -> bb18; ++ goto -> bb19; } bb10: { + _7 = const false; + _8 = const false; -+ _9 = const false; StorageDead(_1); return; } @@ -116,33 +110,37 @@ + } + + bb14 (cleanup): { -+ goto -> bb12; ++ drop(((_1 as F).0: K)) -> [return: bb12, unwind terminate(cleanup)]; + } + -+ bb15: { -+ drop(_1) -> [return: bb13, unwind: bb12]; ++ bb15 (cleanup): { ++ switchInt(_7) -> [0: bb12, otherwise: bb14]; + } + -+ bb16 (cleanup): { -+ drop(_1) -> [return: bb12, unwind terminate(cleanup)]; ++ bb16: { ++ drop(_1) -> [return: bb13, unwind: bb12]; + } + -+ bb17: { -+ _10 = discriminant(_1); -+ switchInt(move _10) -> [0: bb13, otherwise: bb15]; ++ bb17 (cleanup): { ++ drop(_1) -> [return: bb12, unwind terminate(cleanup)]; + } + + bb18: { -+ switchInt(_7) -> [0: bb13, otherwise: bb17]; ++ _9 = discriminant(_1); ++ switchInt(move _9) -> [0: bb13, otherwise: bb16]; + } + -+ bb19 (cleanup): { -+ _11 = discriminant(_1); -+ switchInt(move _11) -> [0: bb14, otherwise: bb16]; ++ bb19: { ++ switchInt(_7) -> [0: bb13, otherwise: bb18]; + } + + bb20 (cleanup): { -+ switchInt(_7) -> [0: bb12, otherwise: bb19]; ++ _10 = discriminant(_1); ++ switchInt(move _10) -> [0: bb15, otherwise: bb17]; ++ } ++ ++ bb21 (cleanup): { ++ switchInt(_7) -> [0: bb12, otherwise: bb20]; } } diff --git a/tests/mir-opt/jump_threading.custom_discr.JumpThreading.panic-abort.diff b/tests/mir-opt/jump_threading.custom_discr.JumpThreading.panic-abort.diff new file mode 100644 index 00000000000..462cc207785 --- /dev/null +++ b/tests/mir-opt/jump_threading.custom_discr.JumpThreading.panic-abort.diff @@ -0,0 +1,57 @@ +- // MIR for `custom_discr` before JumpThreading ++ // MIR for `custom_discr` after JumpThreading + + fn custom_discr(_1: bool) -> u8 { + debug x => _1; + let mut _0: u8; + let mut _2: CustomDiscr; + let mut _3: bool; + let mut _4: u8; + + bb0: { + StorageLive(_2); + StorageLive(_3); + _3 = _1; + switchInt(move _3) -> [0: bb2, otherwise: bb1]; + } + + bb1: { + _2 = CustomDiscr::A; +- goto -> bb3; ++ goto -> bb7; + } + + bb2: { + _2 = CustomDiscr::B; + goto -> bb3; + } + + bb3: { + StorageDead(_3); + _4 = discriminant(_2); +- switchInt(move _4) -> [35: bb5, otherwise: bb4]; ++ goto -> bb4; + } + + bb4: { + _0 = const 13_u8; + goto -> bb6; + } + + bb5: { + _0 = const 5_u8; + goto -> bb6; + } + + bb6: { + StorageDead(_2); + return; ++ } ++ ++ bb7: { ++ StorageDead(_3); ++ _4 = discriminant(_2); ++ goto -> bb5; + } + } + diff --git a/tests/mir-opt/jump_threading.custom_discr.JumpThreading.panic-unwind.diff b/tests/mir-opt/jump_threading.custom_discr.JumpThreading.panic-unwind.diff new file mode 100644 index 00000000000..462cc207785 --- /dev/null +++ b/tests/mir-opt/jump_threading.custom_discr.JumpThreading.panic-unwind.diff @@ -0,0 +1,57 @@ +- // MIR for `custom_discr` before JumpThreading ++ // MIR for `custom_discr` after JumpThreading + + fn custom_discr(_1: bool) -> u8 { + debug x => _1; + let mut _0: u8; + let mut _2: CustomDiscr; + let mut _3: bool; + let mut _4: u8; + + bb0: { + StorageLive(_2); + StorageLive(_3); + _3 = _1; + switchInt(move _3) -> [0: bb2, otherwise: bb1]; + } + + bb1: { + _2 = CustomDiscr::A; +- goto -> bb3; ++ goto -> bb7; + } + + bb2: { + _2 = CustomDiscr::B; + goto -> bb3; + } + + bb3: { + StorageDead(_3); + _4 = discriminant(_2); +- switchInt(move _4) -> [35: bb5, otherwise: bb4]; ++ goto -> bb4; + } + + bb4: { + _0 = const 13_u8; + goto -> bb6; + } + + bb5: { + _0 = const 5_u8; + goto -> bb6; + } + + bb6: { + StorageDead(_2); + return; ++ } ++ ++ bb7: { ++ StorageDead(_3); ++ _4 = discriminant(_2); ++ goto -> bb5; + } + } + diff --git a/tests/mir-opt/jump_threading.dfa.JumpThreading.panic-abort.diff b/tests/mir-opt/jump_threading.dfa.JumpThreading.panic-abort.diff new file mode 100644 index 00000000000..ad5846c97de --- /dev/null +++ b/tests/mir-opt/jump_threading.dfa.JumpThreading.panic-abort.diff @@ -0,0 +1,68 @@ +- // MIR for `dfa` before JumpThreading ++ // MIR for `dfa` after JumpThreading + + fn dfa() -> () { + let mut _0: (); + let mut _1: DFA; + let mut _2: !; + let mut _3: (); + let mut _4: isize; + let mut _5: DFA; + let mut _6: DFA; + let mut _7: DFA; + let mut _8: !; + scope 1 { + debug state => _1; + } + + bb0: { + StorageLive(_1); + _1 = DFA::A; + StorageLive(_2); + goto -> bb1; + } + + bb1: { + _4 = discriminant(_1); + switchInt(move _4) -> [0: bb4, 1: bb5, 2: bb6, 3: bb2, otherwise: bb3]; + } + + bb2: { + _0 = const (); + StorageDead(_2); + StorageDead(_1); + return; + } + + bb3: { + unreachable; + } + + bb4: { + StorageLive(_5); + _5 = DFA::B; + _1 = move _5; + _3 = const (); + StorageDead(_5); + goto -> bb1; + } + + bb5: { + StorageLive(_6); + _6 = DFA::C; + _1 = move _6; + _3 = const (); + StorageDead(_6); + goto -> bb1; + } + + bb6: { + StorageLive(_7); + _7 = DFA::D; + _1 = move _7; + _3 = const (); + StorageDead(_7); + goto -> bb1; + } + } + diff --git a/tests/mir-opt/jump_threading.dfa.JumpThreading.panic-unwind.diff b/tests/mir-opt/jump_threading.dfa.JumpThreading.panic-unwind.diff new file mode 100644 index 00000000000..ad5846c97de --- /dev/null +++ b/tests/mir-opt/jump_threading.dfa.JumpThreading.panic-unwind.diff @@ -0,0 +1,68 @@ +- // MIR for `dfa` before JumpThreading ++ // MIR for `dfa` after JumpThreading + + fn dfa() -> () { + let mut _0: (); + let mut _1: DFA; + let mut _2: !; + let mut _3: (); + let mut _4: isize; + let mut _5: DFA; + let mut _6: DFA; + let mut _7: DFA; + let mut _8: !; + scope 1 { + debug state => _1; + } + + bb0: { + StorageLive(_1); + _1 = DFA::A; + StorageLive(_2); + goto -> bb1; + } + + bb1: { + _4 = discriminant(_1); + switchInt(move _4) -> [0: bb4, 1: bb5, 2: bb6, 3: bb2, otherwise: bb3]; + } + + bb2: { + _0 = const (); + StorageDead(_2); + StorageDead(_1); + return; + } + + bb3: { + unreachable; + } + + bb4: { + StorageLive(_5); + _5 = DFA::B; + _1 = move _5; + _3 = const (); + StorageDead(_5); + goto -> bb1; + } + + bb5: { + StorageLive(_6); + _6 = DFA::C; + _1 = move _6; + _3 = const (); + StorageDead(_6); + goto -> bb1; + } + + bb6: { + StorageLive(_7); + _7 = DFA::D; + _1 = move _7; + _3 = const (); + StorageDead(_7); + goto -> bb1; + } + } + diff --git a/tests/mir-opt/jump_threading.disappearing_bb.JumpThreading.panic-abort.diff b/tests/mir-opt/jump_threading.disappearing_bb.JumpThreading.panic-abort.diff new file mode 100644 index 00000000000..f290da84e5d --- /dev/null +++ b/tests/mir-opt/jump_threading.disappearing_bb.JumpThreading.panic-abort.diff @@ -0,0 +1,59 @@ +- // MIR for `disappearing_bb` before JumpThreading ++ // MIR for `disappearing_bb` after JumpThreading + + fn disappearing_bb(_1: u8) -> u8 { + let mut _0: u8; + let mut _2: bool; + let mut _3: bool; + + bb0: { + _2 = const true; + _3 = const true; + switchInt(_1) -> [0: bb3, 1: bb3, 2: bb1, otherwise: bb2]; + } + + bb1: { + _3 = const false; +- goto -> bb4; ++ goto -> bb9; + } + + bb2: { + unreachable; + } + + bb3: { + _2 = const false; + goto -> bb4; + } + + bb4: { + switchInt(_3) -> [0: bb5, otherwise: bb7]; + } + + bb5: { + switchInt(_2) -> [0: bb6, otherwise: bb8]; + } + + bb6: { + return; + } + + bb7: { +- goto -> bb5; ++ goto -> bb10; + } + + bb8: { ++ goto -> bb6; ++ } ++ ++ bb9: { ++ goto -> bb5; ++ } ++ ++ bb10: { + goto -> bb6; + } + } + diff --git a/tests/mir-opt/jump_threading.disappearing_bb.JumpThreading.panic-unwind.diff b/tests/mir-opt/jump_threading.disappearing_bb.JumpThreading.panic-unwind.diff new file mode 100644 index 00000000000..f290da84e5d --- /dev/null +++ b/tests/mir-opt/jump_threading.disappearing_bb.JumpThreading.panic-unwind.diff @@ -0,0 +1,59 @@ +- // MIR for `disappearing_bb` before JumpThreading ++ // MIR for `disappearing_bb` after JumpThreading + + fn disappearing_bb(_1: u8) -> u8 { + let mut _0: u8; + let mut _2: bool; + let mut _3: bool; + + bb0: { + _2 = const true; + _3 = const true; + switchInt(_1) -> [0: bb3, 1: bb3, 2: bb1, otherwise: bb2]; + } + + bb1: { + _3 = const false; +- goto -> bb4; ++ goto -> bb9; + } + + bb2: { + unreachable; + } + + bb3: { + _2 = const false; + goto -> bb4; + } + + bb4: { + switchInt(_3) -> [0: bb5, otherwise: bb7]; + } + + bb5: { + switchInt(_2) -> [0: bb6, otherwise: bb8]; + } + + bb6: { + return; + } + + bb7: { +- goto -> bb5; ++ goto -> bb10; + } + + bb8: { ++ goto -> bb6; ++ } ++ ++ bb9: { ++ goto -> bb5; ++ } ++ ++ bb10: { + goto -> bb6; + } + } + diff --git a/tests/mir-opt/jump_threading.duplicate_chain.JumpThreading.panic-abort.diff b/tests/mir-opt/jump_threading.duplicate_chain.JumpThreading.panic-abort.diff new file mode 100644 index 00000000000..adcedfb3667 --- /dev/null +++ b/tests/mir-opt/jump_threading.duplicate_chain.JumpThreading.panic-abort.diff @@ -0,0 +1,45 @@ +- // MIR for `duplicate_chain` before JumpThreading ++ // MIR for `duplicate_chain` after JumpThreading + + fn duplicate_chain(_1: bool) -> u8 { + let mut _0: u8; + let mut _2: u8; + let mut _3: i32; + let mut _4: i32; + + bb0: { + switchInt(_1) -> [1: bb1, otherwise: bb2]; + } + + bb1: { + _2 = const 5_u8; + goto -> bb3; + } + + bb2: { + _2 = const 5_u8; + goto -> bb3; + } + + bb3: { + _3 = const 13_i32; + goto -> bb4; + } + + bb4: { + _4 = const 15_i32; +- switchInt(_2) -> [5: bb5, otherwise: bb6]; ++ goto -> bb5; + } + + bb5: { + _0 = const 7_u8; + return; + } + + bb6: { + _0 = const 9_u8; + return; + } + } + diff --git a/tests/mir-opt/jump_threading.duplicate_chain.JumpThreading.panic-unwind.diff b/tests/mir-opt/jump_threading.duplicate_chain.JumpThreading.panic-unwind.diff new file mode 100644 index 00000000000..adcedfb3667 --- /dev/null +++ b/tests/mir-opt/jump_threading.duplicate_chain.JumpThreading.panic-unwind.diff @@ -0,0 +1,45 @@ +- // MIR for `duplicate_chain` before JumpThreading ++ // MIR for `duplicate_chain` after JumpThreading + + fn duplicate_chain(_1: bool) -> u8 { + let mut _0: u8; + let mut _2: u8; + let mut _3: i32; + let mut _4: i32; + + bb0: { + switchInt(_1) -> [1: bb1, otherwise: bb2]; + } + + bb1: { + _2 = const 5_u8; + goto -> bb3; + } + + bb2: { + _2 = const 5_u8; + goto -> bb3; + } + + bb3: { + _3 = const 13_i32; + goto -> bb4; + } + + bb4: { + _4 = const 15_i32; +- switchInt(_2) -> [5: bb5, otherwise: bb6]; ++ goto -> bb5; + } + + bb5: { + _0 = const 7_u8; + return; + } + + bb6: { + _0 = const 9_u8; + return; + } + } + diff --git a/tests/mir-opt/jump_threading.identity.JumpThreading.panic-abort.diff b/tests/mir-opt/jump_threading.identity.JumpThreading.panic-abort.diff new file mode 100644 index 00000000000..f17c9ba3f9f --- /dev/null +++ b/tests/mir-opt/jump_threading.identity.JumpThreading.panic-abort.diff @@ -0,0 +1,139 @@ +- // MIR for `identity` before JumpThreading ++ // MIR for `identity` after JumpThreading + + fn identity(_1: Result<i32, i32>) -> Result<i32, i32> { + debug x => _1; + let mut _0: std::result::Result<i32, i32>; + let mut _2: i32; + let mut _3: std::ops::ControlFlow<std::result::Result<std::convert::Infallible, i32>, i32>; + let mut _4: std::result::Result<i32, i32>; + let mut _5: isize; + let _6: std::result::Result<std::convert::Infallible, i32>; + let mut _7: !; + let mut _8: std::result::Result<std::convert::Infallible, i32>; + let _9: i32; + scope 1 { + debug residual => _6; + scope 2 { + scope 8 (inlined #[track_caller] <Result<i32, i32> as FromResidual<Result<Infallible, i32>>>::from_residual) { + debug residual => _8; + let _14: i32; + let mut _15: i32; + scope 9 { + debug e => _14; + scope 10 (inlined <i32 as From<i32>>::from) { + debug t => _14; + } + } + } + } + } + scope 3 { + debug val => _9; + scope 4 { + } + } + scope 5 (inlined <Result<i32, i32> as Try>::branch) { + debug self => _4; + let mut _10: isize; + let _11: i32; + let _12: i32; + let mut _13: std::result::Result<std::convert::Infallible, i32>; + scope 6 { + debug v => _11; + } + scope 7 { + debug e => _12; + } + } + + bb0: { + StorageLive(_2); + StorageLive(_3); + StorageLive(_4); + _4 = _1; + StorageLive(_10); + StorageLive(_11); + StorageLive(_12); + _10 = discriminant(_4); + switchInt(move _10) -> [0: bb8, 1: bb6, otherwise: bb7]; + } + + bb1: { + StorageDead(_12); + StorageDead(_11); + StorageDead(_10); + StorageDead(_4); + _5 = discriminant(_3); +- switchInt(move _5) -> [0: bb2, 1: bb4, otherwise: bb3]; ++ goto -> bb2; + } + + bb2: { + StorageLive(_9); + _9 = ((_3 as Continue).0: i32); + _2 = _9; + StorageDead(_9); + _0 = Result::<i32, i32>::Ok(move _2); + StorageDead(_2); + StorageDead(_3); + goto -> bb5; + } + + bb3: { + unreachable; + } + + bb4: { + StorageLive(_6); + _6 = ((_3 as Break).0: std::result::Result<std::convert::Infallible, i32>); + StorageLive(_8); + _8 = _6; + StorageLive(_14); + _14 = move ((_8 as Err).0: i32); + StorageLive(_15); + _15 = move _14; + _0 = Result::<i32, i32>::Err(move _15); + StorageDead(_15); + StorageDead(_14); + StorageDead(_8); + StorageDead(_6); + StorageDead(_2); + StorageDead(_3); + goto -> bb5; + } + + bb5: { + return; + } + + bb6: { + _12 = move ((_4 as Err).0: i32); + StorageLive(_13); + _13 = Result::<Infallible, i32>::Err(move _12); + _3 = ControlFlow::<Result<Infallible, i32>, i32>::Break(move _13); + StorageDead(_13); +- goto -> bb1; ++ goto -> bb9; + } + + bb7: { + unreachable; + } + + bb8: { + _11 = move ((_4 as Ok).0: i32); + _3 = ControlFlow::<Result<Infallible, i32>, i32>::Continue(move _11); + goto -> bb1; ++ } ++ ++ bb9: { ++ StorageDead(_12); ++ StorageDead(_11); ++ StorageDead(_10); ++ StorageDead(_4); ++ _5 = discriminant(_3); ++ goto -> bb4; + } + } + diff --git a/tests/mir-opt/jump_threading.identity.JumpThreading.panic-unwind.diff b/tests/mir-opt/jump_threading.identity.JumpThreading.panic-unwind.diff new file mode 100644 index 00000000000..f17c9ba3f9f --- /dev/null +++ b/tests/mir-opt/jump_threading.identity.JumpThreading.panic-unwind.diff @@ -0,0 +1,139 @@ +- // MIR for `identity` before JumpThreading ++ // MIR for `identity` after JumpThreading + + fn identity(_1: Result<i32, i32>) -> Result<i32, i32> { + debug x => _1; + let mut _0: std::result::Result<i32, i32>; + let mut _2: i32; + let mut _3: std::ops::ControlFlow<std::result::Result<std::convert::Infallible, i32>, i32>; + let mut _4: std::result::Result<i32, i32>; + let mut _5: isize; + let _6: std::result::Result<std::convert::Infallible, i32>; + let mut _7: !; + let mut _8: std::result::Result<std::convert::Infallible, i32>; + let _9: i32; + scope 1 { + debug residual => _6; + scope 2 { + scope 8 (inlined #[track_caller] <Result<i32, i32> as FromResidual<Result<Infallible, i32>>>::from_residual) { + debug residual => _8; + let _14: i32; + let mut _15: i32; + scope 9 { + debug e => _14; + scope 10 (inlined <i32 as From<i32>>::from) { + debug t => _14; + } + } + } + } + } + scope 3 { + debug val => _9; + scope 4 { + } + } + scope 5 (inlined <Result<i32, i32> as Try>::branch) { + debug self => _4; + let mut _10: isize; + let _11: i32; + let _12: i32; + let mut _13: std::result::Result<std::convert::Infallible, i32>; + scope 6 { + debug v => _11; + } + scope 7 { + debug e => _12; + } + } + + bb0: { + StorageLive(_2); + StorageLive(_3); + StorageLive(_4); + _4 = _1; + StorageLive(_10); + StorageLive(_11); + StorageLive(_12); + _10 = discriminant(_4); + switchInt(move _10) -> [0: bb8, 1: bb6, otherwise: bb7]; + } + + bb1: { + StorageDead(_12); + StorageDead(_11); + StorageDead(_10); + StorageDead(_4); + _5 = discriminant(_3); +- switchInt(move _5) -> [0: bb2, 1: bb4, otherwise: bb3]; ++ goto -> bb2; + } + + bb2: { + StorageLive(_9); + _9 = ((_3 as Continue).0: i32); + _2 = _9; + StorageDead(_9); + _0 = Result::<i32, i32>::Ok(move _2); + StorageDead(_2); + StorageDead(_3); + goto -> bb5; + } + + bb3: { + unreachable; + } + + bb4: { + StorageLive(_6); + _6 = ((_3 as Break).0: std::result::Result<std::convert::Infallible, i32>); + StorageLive(_8); + _8 = _6; + StorageLive(_14); + _14 = move ((_8 as Err).0: i32); + StorageLive(_15); + _15 = move _14; + _0 = Result::<i32, i32>::Err(move _15); + StorageDead(_15); + StorageDead(_14); + StorageDead(_8); + StorageDead(_6); + StorageDead(_2); + StorageDead(_3); + goto -> bb5; + } + + bb5: { + return; + } + + bb6: { + _12 = move ((_4 as Err).0: i32); + StorageLive(_13); + _13 = Result::<Infallible, i32>::Err(move _12); + _3 = ControlFlow::<Result<Infallible, i32>, i32>::Break(move _13); + StorageDead(_13); +- goto -> bb1; ++ goto -> bb9; + } + + bb7: { + unreachable; + } + + bb8: { + _11 = move ((_4 as Ok).0: i32); + _3 = ControlFlow::<Result<Infallible, i32>, i32>::Continue(move _11); + goto -> bb1; ++ } ++ ++ bb9: { ++ StorageDead(_12); ++ StorageDead(_11); ++ StorageDead(_10); ++ StorageDead(_4); ++ _5 = discriminant(_3); ++ goto -> bb4; + } + } + diff --git a/tests/mir-opt/jump_threading.multiple_match.JumpThreading.panic-abort.diff b/tests/mir-opt/jump_threading.multiple_match.JumpThreading.panic-abort.diff new file mode 100644 index 00000000000..2ca03e439a0 --- /dev/null +++ b/tests/mir-opt/jump_threading.multiple_match.JumpThreading.panic-abort.diff @@ -0,0 +1,54 @@ +- // MIR for `multiple_match` before JumpThreading ++ // MIR for `multiple_match` after JumpThreading + + fn multiple_match(_1: u8) -> u8 { + let mut _0: u8; + let mut _2: u8; + let mut _3: u8; + + bb0: { + switchInt(_1) -> [3: bb1, otherwise: bb2]; + } + + bb1: { + _2 = _1; +- switchInt(_2) -> [3: bb3, otherwise: bb4]; ++ goto -> bb3; + } + + bb2: { + _3 = _1; +- switchInt(_3) -> [3: bb5, otherwise: bb6]; ++ goto -> bb6; + } + + bb3: { + _0 = const 5_u8; + return; + } + + bb4: { + _0 = const 7_u8; + return; + } + + bb5: { + _0 = const 9_u8; + return; + } + + bb6: { + switchInt(_3) -> [1: bb7, otherwise: bb8]; + } + + bb7: { + _0 = const 9_u8; + return; + } + + bb8: { + _0 = const 11_u8; + return; + } + } + diff --git a/tests/mir-opt/jump_threading.multiple_match.JumpThreading.panic-unwind.diff b/tests/mir-opt/jump_threading.multiple_match.JumpThreading.panic-unwind.diff new file mode 100644 index 00000000000..2ca03e439a0 --- /dev/null +++ b/tests/mir-opt/jump_threading.multiple_match.JumpThreading.panic-unwind.diff @@ -0,0 +1,54 @@ +- // MIR for `multiple_match` before JumpThreading ++ // MIR for `multiple_match` after JumpThreading + + fn multiple_match(_1: u8) -> u8 { + let mut _0: u8; + let mut _2: u8; + let mut _3: u8; + + bb0: { + switchInt(_1) -> [3: bb1, otherwise: bb2]; + } + + bb1: { + _2 = _1; +- switchInt(_2) -> [3: bb3, otherwise: bb4]; ++ goto -> bb3; + } + + bb2: { + _3 = _1; +- switchInt(_3) -> [3: bb5, otherwise: bb6]; ++ goto -> bb6; + } + + bb3: { + _0 = const 5_u8; + return; + } + + bb4: { + _0 = const 7_u8; + return; + } + + bb5: { + _0 = const 9_u8; + return; + } + + bb6: { + switchInt(_3) -> [1: bb7, otherwise: bb8]; + } + + bb7: { + _0 = const 9_u8; + return; + } + + bb8: { + _0 = const 11_u8; + return; + } + } + diff --git a/tests/mir-opt/jump_threading.mutable_ref.JumpThreading.panic-abort.diff b/tests/mir-opt/jump_threading.mutable_ref.JumpThreading.panic-abort.diff new file mode 100644 index 00000000000..80a42263643 --- /dev/null +++ b/tests/mir-opt/jump_threading.mutable_ref.JumpThreading.panic-abort.diff @@ -0,0 +1,56 @@ +- // MIR for `mutable_ref` before JumpThreading ++ // MIR for `mutable_ref` after JumpThreading + + fn mutable_ref() -> bool { + let mut _0: bool; + let mut _1: i32; + let _3: (); + let mut _4: bool; + let mut _5: i32; + scope 1 { + debug x => _1; + let _2: *mut i32; + scope 2 { + debug a => _2; + scope 3 { + } + } + } + + bb0: { + StorageLive(_1); + _1 = const 5_i32; + StorageLive(_2); + _2 = &raw mut _1; + _1 = const 7_i32; + StorageLive(_3); + (*_2) = const 8_i32; + _3 = const (); + StorageDead(_3); + StorageLive(_4); + StorageLive(_5); + _5 = _1; + _4 = Eq(move _5, const 7_i32); + switchInt(move _4) -> [0: bb2, otherwise: bb1]; + } + + bb1: { + StorageDead(_5); + _0 = const true; + goto -> bb3; + } + + bb2: { + StorageDead(_5); + _0 = const false; + goto -> bb3; + } + + bb3: { + StorageDead(_4); + StorageDead(_2); + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/jump_threading.mutable_ref.JumpThreading.panic-unwind.diff b/tests/mir-opt/jump_threading.mutable_ref.JumpThreading.panic-unwind.diff new file mode 100644 index 00000000000..80a42263643 --- /dev/null +++ b/tests/mir-opt/jump_threading.mutable_ref.JumpThreading.panic-unwind.diff @@ -0,0 +1,56 @@ +- // MIR for `mutable_ref` before JumpThreading ++ // MIR for `mutable_ref` after JumpThreading + + fn mutable_ref() -> bool { + let mut _0: bool; + let mut _1: i32; + let _3: (); + let mut _4: bool; + let mut _5: i32; + scope 1 { + debug x => _1; + let _2: *mut i32; + scope 2 { + debug a => _2; + scope 3 { + } + } + } + + bb0: { + StorageLive(_1); + _1 = const 5_i32; + StorageLive(_2); + _2 = &raw mut _1; + _1 = const 7_i32; + StorageLive(_3); + (*_2) = const 8_i32; + _3 = const (); + StorageDead(_3); + StorageLive(_4); + StorageLive(_5); + _5 = _1; + _4 = Eq(move _5, const 7_i32); + switchInt(move _4) -> [0: bb2, otherwise: bb1]; + } + + bb1: { + StorageDead(_5); + _0 = const true; + goto -> bb3; + } + + bb2: { + StorageDead(_5); + _0 = const false; + goto -> bb3; + } + + bb3: { + StorageDead(_4); + StorageDead(_2); + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/jump_threading.mutate_discriminant.JumpThreading.panic-abort.diff b/tests/mir-opt/jump_threading.mutate_discriminant.JumpThreading.panic-abort.diff new file mode 100644 index 00000000000..8821b47c345 --- /dev/null +++ b/tests/mir-opt/jump_threading.mutate_discriminant.JumpThreading.panic-abort.diff @@ -0,0 +1,26 @@ +- // MIR for `mutate_discriminant` before JumpThreading ++ // MIR for `mutate_discriminant` after JumpThreading + + fn mutate_discriminant() -> u8 { + let mut _0: u8; + let mut _1: std::option::Option<NonZeroUsize>; + let mut _2: isize; + + bb0: { + discriminant(_1) = 1; + (((_1 as variant#1).0: NonZeroUsize).0: usize) = const 0_usize; + _2 = discriminant(_1); + switchInt(_2) -> [0: bb1, otherwise: bb2]; + } + + bb1: { + _0 = const 1_u8; + return; + } + + bb2: { + _0 = const 2_u8; + unreachable; + } + } + diff --git a/tests/mir-opt/jump_threading.mutate_discriminant.JumpThreading.panic-unwind.diff b/tests/mir-opt/jump_threading.mutate_discriminant.JumpThreading.panic-unwind.diff new file mode 100644 index 00000000000..8821b47c345 --- /dev/null +++ b/tests/mir-opt/jump_threading.mutate_discriminant.JumpThreading.panic-unwind.diff @@ -0,0 +1,26 @@ +- // MIR for `mutate_discriminant` before JumpThreading ++ // MIR for `mutate_discriminant` after JumpThreading + + fn mutate_discriminant() -> u8 { + let mut _0: u8; + let mut _1: std::option::Option<NonZeroUsize>; + let mut _2: isize; + + bb0: { + discriminant(_1) = 1; + (((_1 as variant#1).0: NonZeroUsize).0: usize) = const 0_usize; + _2 = discriminant(_1); + switchInt(_2) -> [0: bb1, otherwise: bb2]; + } + + bb1: { + _0 = const 1_u8; + return; + } + + bb2: { + _0 = const 2_u8; + unreachable; + } + } + diff --git a/tests/mir-opt/jump_threading.renumbered_bb.JumpThreading.panic-abort.diff b/tests/mir-opt/jump_threading.renumbered_bb.JumpThreading.panic-abort.diff new file mode 100644 index 00000000000..2d943a4bee2 --- /dev/null +++ b/tests/mir-opt/jump_threading.renumbered_bb.JumpThreading.panic-abort.diff @@ -0,0 +1,57 @@ +- // MIR for `renumbered_bb` before JumpThreading ++ // MIR for `renumbered_bb` after JumpThreading + + fn renumbered_bb(_1: bool) -> u8 { + let mut _0: u8; + let mut _2: bool; + let mut _3: bool; + + bb0: { + _3 = const false; + switchInt(_1) -> [1: bb1, otherwise: bb2]; + } + + bb1: { + _2 = const false; +- goto -> bb3; ++ goto -> bb8; + } + + bb2: { + _2 = _1; + _3 = _1; + goto -> bb3; + } + + bb3: { + switchInt(_2) -> [0: bb4, otherwise: bb5]; + } + + bb4: { + switchInt(_3) -> [0: bb6, otherwise: bb7]; + } + + bb5: { + _0 = const 7_u8; + return; + } + + bb6: { + _0 = const 9_u8; + return; + } + + bb7: { + _0 = const 11_u8; + return; ++ } ++ ++ bb8: { ++ goto -> bb9; ++ } ++ ++ bb9: { ++ goto -> bb6; + } + } + diff --git a/tests/mir-opt/jump_threading.renumbered_bb.JumpThreading.panic-unwind.diff b/tests/mir-opt/jump_threading.renumbered_bb.JumpThreading.panic-unwind.diff new file mode 100644 index 00000000000..2d943a4bee2 --- /dev/null +++ b/tests/mir-opt/jump_threading.renumbered_bb.JumpThreading.panic-unwind.diff @@ -0,0 +1,57 @@ +- // MIR for `renumbered_bb` before JumpThreading ++ // MIR for `renumbered_bb` after JumpThreading + + fn renumbered_bb(_1: bool) -> u8 { + let mut _0: u8; + let mut _2: bool; + let mut _3: bool; + + bb0: { + _3 = const false; + switchInt(_1) -> [1: bb1, otherwise: bb2]; + } + + bb1: { + _2 = const false; +- goto -> bb3; ++ goto -> bb8; + } + + bb2: { + _2 = _1; + _3 = _1; + goto -> bb3; + } + + bb3: { + switchInt(_2) -> [0: bb4, otherwise: bb5]; + } + + bb4: { + switchInt(_3) -> [0: bb6, otherwise: bb7]; + } + + bb5: { + _0 = const 7_u8; + return; + } + + bb6: { + _0 = const 9_u8; + return; + } + + bb7: { + _0 = const 11_u8; + return; ++ } ++ ++ bb8: { ++ goto -> bb9; ++ } ++ ++ bb9: { ++ goto -> bb6; + } + } + diff --git a/tests/mir-opt/jump_threading.rs b/tests/mir-opt/jump_threading.rs new file mode 100644 index 00000000000..852dcd0db01 --- /dev/null +++ b/tests/mir-opt/jump_threading.rs @@ -0,0 +1,480 @@ +// unit-test: JumpThreading +// compile-flags: -Zmir-enable-passes=+Inline +// EMIT_MIR_FOR_EACH_PANIC_STRATEGY + +#![feature(control_flow_enum)] +#![feature(try_trait_v2)] +#![feature(custom_mir, core_intrinsics, rustc_attrs)] + +use std::intrinsics::mir::*; +use std::ops::ControlFlow; + +fn too_complex(x: Result<i32, usize>) -> Option<i32> { + // CHECK-LABEL: fn too_complex( + // CHECK: bb0: { + // CHECK: switchInt(move {{_.*}}) -> [0: bb3, 1: bb1, otherwise: bb2]; + // CHECK: bb1: { + // CHECK: [[controlflow:_.*]] = ControlFlow::<usize, i32>::Break( + // CHECK: goto -> bb8; + // CHECK: bb2: { + // CHECK: unreachable; + // CHECK: bb3: { + // CHECK: [[controlflow]] = ControlFlow::<usize, i32>::Continue( + // CHECK: goto -> bb4; + // CHECK: bb4: { + // CHECK: goto -> bb6; + // CHECK: bb5: { + // CHECK: {{_.*}} = (([[controlflow]] as Break).0: usize); + // CHECK: _0 = Option::<i32>::None; + // CHECK: goto -> bb7; + // CHECK: bb6: { + // CHECK: {{_.*}} = (([[controlflow]] as Continue).0: i32); + // CHECK: _0 = Option::<i32>::Some( + // CHECK: goto -> bb7; + // CHECK: bb7: { + // CHECK: return; + // CHECK: bb8: { + // CHECK: goto -> bb5; + match { + match x { + Ok(v) => ControlFlow::Continue(v), + Err(r) => ControlFlow::Break(r), + } + } { + ControlFlow::Continue(v) => Some(v), + ControlFlow::Break(r) => None, + } +} + +fn identity(x: Result<i32, i32>) -> Result<i32, i32> { + // CHECK-LABEL: fn identity( + // CHECK: bb0: { + // CHECK: [[x:_.*]] = _1; + // CHECK: switchInt(move {{_.*}}) -> [0: bb8, 1: bb6, otherwise: bb7]; + // CHECK: bb1: { + // CHECK: goto -> bb2; + // CHECK: bb2: { + // CHECK: {{_.*}} = (([[controlflow:_.*]] as Continue).0: i32); + // CHECK: _0 = Result::<i32, i32>::Ok( + // CHECK: goto -> bb5; + // CHECK: bb3: { + // CHECK: unreachable; + // CHECK: bb4: { + // CHECK: {{_.*}} = (([[controlflow]] as Break).0: std::result::Result<std::convert::Infallible, i32>); + // CHECK: _0 = Result::<i32, i32>::Err( + // CHECK: goto -> bb5; + // CHECK: bb5: { + // CHECK: return; + // CHECK: bb6: { + // CHECK: {{_.*}} = move (([[x]] as Err).0: i32); + // CHECK: [[controlflow]] = ControlFlow::<Result<Infallible, i32>, i32>::Break( + // CHECK: goto -> bb9; + // CHECK: bb7: { + // CHECK: unreachable; + // CHECK: bb8: { + // CHECK: {{_.*}} = move (([[x]] as Ok).0: i32); + // CHECK: [[controlflow]] = ControlFlow::<Result<Infallible, i32>, i32>::Continue( + // CHECK: goto -> bb1; + // CHECK: bb9: { + // CHECK: goto -> bb4; + Ok(x?) +} + +enum DFA { + A, + B, + C, + D, +} + +/// Check that we do not thread through a loop header, +/// to avoid creating an irreducible CFG. +fn dfa() { + // CHECK-LABEL: fn dfa( + // CHECK: bb0: { + // CHECK: {{_.*}} = DFA::A; + // CHECK: goto -> bb1; + // CHECK: bb1: { + // CHECK: switchInt({{.*}}) -> [0: bb4, 1: bb5, 2: bb6, 3: bb2, otherwise: bb3]; + // CHECK: bb2: { + // CHECK: return; + // CHECK: bb3: { + // CHECK: unreachable; + // CHECK: bb4: { + // CHECK: {{_.*}} = DFA::B; + // CHECK: goto -> bb1; + // CHECK: bb5: { + // CHECK: {{_.*}} = DFA::C; + // CHECK: goto -> bb1; + // CHECK: bb6: { + // CHECK: {{_.*}} = DFA::D; + // CHECK: goto -> bb1; + let mut state = DFA::A; + loop { + match state { + DFA::A => state = DFA::B, + DFA::B => state = DFA::C, + DFA::C => state = DFA::D, + DFA::D => return, + } + } +} + +#[repr(u8)] +enum CustomDiscr { + A = 35, + B = 73, + C = 99, +} + +/// Verify that we correctly match the discriminant value, and not its index. +fn custom_discr(x: bool) -> u8 { + // CHECK-LABEL: fn custom_discr( + // CHECK: bb0: { + // CHECK: switchInt({{.*}}) -> [0: bb2, otherwise: bb1]; + // CHECK: bb1: { + // CHECK: {{_.*}} = CustomDiscr::A; + // CHECK: goto -> bb7; + // CHECK: bb2: { + // CHECK: {{_.*}} = CustomDiscr::B; + // CHECK: goto -> bb3; + // CHECK: bb3: { + // CHECK: goto -> bb4; + // CHECK: bb4: { + // CHECK: _0 = const 13_u8; + // CHECK: goto -> bb6; + // CHECK: bb5: { + // CHECK: _0 = const 5_u8; + // CHECK: goto -> bb6; + // CHECK: bb6: { + // CHECK: return; + // CHECK: bb7: { + // CHECK: goto -> bb5; + match if x { CustomDiscr::A } else { CustomDiscr::B } { + CustomDiscr::A => 5, + _ => 13, + } +} + +#[custom_mir(dialect = "runtime", phase = "post-cleanup")] +fn multiple_match(x: u8) -> u8 { + // CHECK-LABEL: fn multiple_match( + mir!( + { + // CHECK: bb0: { + // CHECK: switchInt([[x:_.*]]) -> [3: bb1, otherwise: bb2]; + match x { 3 => bb1, _ => bb2 } + } + bb1 = { + // We know `x == 3`, so we can take `bb3`. + // CHECK: bb1: { + // CHECK: {{_.*}} = [[x]]; + // CHECK: goto -> bb3; + let y = x; + match y { 3 => bb3, _ => bb4 } + } + bb2 = { + // We know `x != 3`, so we can take `bb6`. + // CHECK: bb2: { + // CHECK: [[z:_.*]] = [[x]]; + // CHECK: goto -> bb6; + let z = x; + match z { 3 => bb5, _ => bb6 } + } + bb3 = { + // CHECK: bb3: { + // CHECK: _0 = const 5_u8; + // CHECK: return; + RET = 5; + Return() + } + bb4 = { + // CHECK: bb4: { + // CHECK: _0 = const 7_u8; + // CHECK: return; + RET = 7; + Return() + } + bb5 = { + // CHECK: bb5: { + // CHECK: _0 = const 9_u8; + // CHECK: return; + RET = 9; + Return() + } + bb6 = { + // We know `z != 3`, so we CANNOT take `bb7`. + // CHECK: bb6: { + // CHECK: switchInt([[z]]) -> [1: bb7, otherwise: bb8]; + match z { 1 => bb7, _ => bb8 } + } + bb7 = { + // CHECK: bb7: { + // CHECK: _0 = const 9_u8; + // CHECK: return; + RET = 9; + Return() + } + bb8 = { + // CHECK: bb8: { + // CHECK: _0 = const 11_u8; + // CHECK: return; + RET = 11; + Return() + } + ) +} + +/// Both 1-3-4 and 2-3-4 are threadable. As 1 and 2 are the only predecessors of 3, +/// verify that we only thread the 3-4 part. +#[custom_mir(dialect = "runtime", phase = "post-cleanup")] +fn duplicate_chain(x: bool) -> u8 { + // CHECK-LABEL: fn duplicate_chain( + mir!( + let a: u8; + { + // CHECK: bb0: { + // CHECK: switchInt({{.*}}) -> [1: bb1, otherwise: bb2]; + match x { true => bb1, _ => bb2 } + } + bb1 = { + // CHECK: bb1: { + // CHECK: [[a:_.*]] = const 5_u8; + // CHECK: goto -> bb3; + a = 5; + Goto(bb3) + } + bb2 = { + // CHECK: bb2: { + // CHECK: [[a]] = const 5_u8; + // CHECK: goto -> bb3; + a = 5; + Goto(bb3) + } + bb3 = { + // CHECK: bb3: { + // CHECK: {{_.*}} = const 13_i32; + // CHECK: goto -> bb4; + let b = 13; + Goto(bb4) + } + bb4 = { + // CHECK: bb4: { + // CHECK: {{_.*}} = const 15_i32; + // CHECK-NOT: switchInt( + // CHECK: goto -> bb5; + let c = 15; + match a { 5 => bb5, _ => bb6 } + } + bb5 = { + // CHECK: bb5: { + // CHECK: _0 = const 7_u8; + // CHECK: return; + RET = 7; + Return() + } + bb6 = { + // CHECK: bb6: { + // CHECK: _0 = const 9_u8; + // CHECK: return; + RET = 9; + Return() + } + ) +} + +#[rustc_layout_scalar_valid_range_start(1)] +#[rustc_nonnull_optimization_guaranteed] +struct NonZeroUsize(usize); + +/// Verify that we correctly discard threads that may mutate a discriminant by aliasing. +#[custom_mir(dialect = "runtime", phase = "post-cleanup")] +fn mutate_discriminant() -> u8 { + // CHECK-LABEL: fn mutate_discriminant( + // CHECK-NOT: goto -> {{bb.*}}; + // CHECK: switchInt( + // CHECK-NOT: goto -> {{bb.*}}; + mir!( + let x: Option<NonZeroUsize>; + { + SetDiscriminant(x, 1); + // This assignment overwrites the niche in which the discriminant is stored. + place!(Field(Field(Variant(x, 1), 0), 0)) = 0_usize; + // So we cannot know the value of this discriminant. + let a = Discriminant(x); + match a { + 0 => bb1, + _ => bad, + } + } + bb1 = { + RET = 1; + Return() + } + bad = { + RET = 2; + Unreachable() + } + ) +} + +/// Verify that we do not try to reason when there are mutable pointers involved. +fn mutable_ref() -> bool { + // CHECK-LABEL: fn mutable_ref( + // CHECK-NOT: goto -> {{bb.*}}; + // CHECK: switchInt( + // CHECK: goto -> [[bbret:bb.*]]; + // CHECK: goto -> [[bbret]]; + // CHECK: [[bbret]]: { + // CHECK-NOT: {{bb.*}}: { + // CHECK: return; + let mut x = 5; + let a = std::ptr::addr_of_mut!(x); + x = 7; + unsafe { *a = 8 }; + if x == 7 { + true + } else { + false + } +} + +/// This function has 2 TOs: 1-3-4 and 0-1-3-4-6. +/// We verify that the second TO does not modify 3 once the first has been applied. +#[custom_mir(dialect = "runtime", phase = "post-cleanup")] +fn renumbered_bb(x: bool) -> u8 { + // CHECK-LABEL: fn renumbered_bb( + mir!( + let a: bool; + let b: bool; + { + // CHECK: bb0: { + // CHECK: switchInt({{.*}}) -> [1: bb1, otherwise: bb2]; + b = false; + match x { true => bb1, _ => bb2 } + } + bb1 = { + // CHECK: bb1: { + // CHECK: goto -> bb8; + a = false; + Goto(bb3) + } + bb2 = { + // CHECK: bb2: { + // CHECK: goto -> bb3; + a = x; + b = x; + Goto(bb3) + } + bb3 = { + // CHECK: bb3: { + // CHECK: switchInt({{.*}}) -> [0: bb4, otherwise: bb5]; + match a { false => bb4, _ => bb5 } + } + bb4 = { + // CHECK: bb4: { + // CHECK: switchInt({{.*}}) -> [0: bb6, otherwise: bb7]; + match b { false => bb6, _ => bb7 } + } + bb5 = { + // CHECK: bb5: { + // CHECK: _0 = const 7_u8; + RET = 7; + Return() + } + bb6 = { + // CHECK: bb6: { + // CHECK: _0 = const 9_u8; + RET = 9; + Return() + } + bb7 = { + // CHECK: bb7: { + // CHECK: _0 = const 11_u8; + RET = 11; + Return() + } + // Duplicate of bb3. + // CHECK: bb8: { + // CHECK-NEXT: goto -> bb9; + // Duplicate of bb4. + // CHECK: bb9: { + // CHECK-NEXT: goto -> bb6; + ) +} + +/// This function has 3 TOs: 1-4-5, 0-1-4-7-5-8 and 3-4-7-5-6 +/// After applying the first TO, we create bb9 to replace 4, and rename 1-4 edge by 1-9. The +/// second TO may try to thread non-existing edge 9-4. +/// This test verifies that we preserve semantics by bailing out of this second TO. +#[custom_mir(dialect = "runtime", phase = "post-cleanup")] +fn disappearing_bb(x: u8) -> u8 { + // CHECK-LABEL: fn disappearing_bb( + mir!( + let a: bool; + let b: bool; + { + a = true; + b = true; + match x { 0 => bb3, 1 => bb3, 2 => bb1, _ => bb2 } + } + bb1 = { + // CHECK: bb1: { + // CHECK: goto -> bb9; + b = false; + Goto(bb4) + } + bb2 = { + Unreachable() + } + bb3 = { + // CHECK: bb3: { + // CHECK: goto -> bb10; + a = false; + Goto(bb4) + } + bb4 = { + match b { false => bb5, _ => bb7 } + } + bb5 = { + match a { false => bb6, _ => bb8 } + } + bb6 = { + Return() + } + bb7 = { + Goto(bb5) + } + bb8 = { + Goto(bb6) + } + // CHECK: bb9: { + // CHECK: goto -> bb5; + // CHECK: bb10: { + // CHECK: goto -> bb6; + ) +} + +fn main() { + too_complex(Ok(0)); + identity(Ok(0)); + custom_discr(false); + dfa(); + multiple_match(5); + duplicate_chain(false); + mutate_discriminant(); + mutable_ref(); + renumbered_bb(true); + disappearing_bb(7); +} + +// EMIT_MIR jump_threading.too_complex.JumpThreading.diff +// EMIT_MIR jump_threading.identity.JumpThreading.diff +// EMIT_MIR jump_threading.custom_discr.JumpThreading.diff +// EMIT_MIR jump_threading.dfa.JumpThreading.diff +// EMIT_MIR jump_threading.multiple_match.JumpThreading.diff +// EMIT_MIR jump_threading.duplicate_chain.JumpThreading.diff +// EMIT_MIR jump_threading.mutate_discriminant.JumpThreading.diff +// EMIT_MIR jump_threading.mutable_ref.JumpThreading.diff +// EMIT_MIR jump_threading.renumbered_bb.JumpThreading.diff +// EMIT_MIR jump_threading.disappearing_bb.JumpThreading.diff diff --git a/tests/mir-opt/jump_threading.too_complex.JumpThreading.panic-abort.diff b/tests/mir-opt/jump_threading.too_complex.JumpThreading.panic-abort.diff new file mode 100644 index 00000000000..f5eade4a914 --- /dev/null +++ b/tests/mir-opt/jump_threading.too_complex.JumpThreading.panic-abort.diff @@ -0,0 +1,98 @@ +- // MIR for `too_complex` before JumpThreading ++ // MIR for `too_complex` after JumpThreading + + fn too_complex(_1: Result<i32, usize>) -> Option<i32> { + debug x => _1; + let mut _0: std::option::Option<i32>; + let mut _2: std::ops::ControlFlow<usize, i32>; + let mut _3: isize; + let _4: i32; + let mut _5: i32; + let _6: usize; + let mut _7: usize; + let mut _8: isize; + let _9: i32; + let mut _10: i32; + let _11: usize; + scope 1 { + debug v => _4; + } + scope 2 { + debug r => _6; + } + scope 3 { + debug v => _9; + } + scope 4 { + debug r => _11; + } + + bb0: { + StorageLive(_2); + _3 = discriminant(_1); + switchInt(move _3) -> [0: bb3, 1: bb1, otherwise: bb2]; + } + + bb1: { + StorageLive(_6); + _6 = ((_1 as Err).0: usize); + StorageLive(_7); + _7 = _6; + _2 = ControlFlow::<usize, i32>::Break(move _7); + StorageDead(_7); + StorageDead(_6); +- goto -> bb4; ++ goto -> bb8; + } + + bb2: { + unreachable; + } + + bb3: { + StorageLive(_4); + _4 = ((_1 as Ok).0: i32); + StorageLive(_5); + _5 = _4; + _2 = ControlFlow::<usize, i32>::Continue(move _5); + StorageDead(_5); + StorageDead(_4); + goto -> bb4; + } + + bb4: { + _8 = discriminant(_2); +- switchInt(move _8) -> [0: bb6, 1: bb5, otherwise: bb2]; ++ goto -> bb6; + } + + bb5: { + StorageLive(_11); + _11 = ((_2 as Break).0: usize); + _0 = Option::<i32>::None; + StorageDead(_11); + goto -> bb7; + } + + bb6: { + StorageLive(_9); + _9 = ((_2 as Continue).0: i32); + StorageLive(_10); + _10 = _9; + _0 = Option::<i32>::Some(move _10); + StorageDead(_10); + StorageDead(_9); + goto -> bb7; + } + + bb7: { + StorageDead(_2); + return; ++ } ++ ++ bb8: { ++ _8 = discriminant(_2); ++ goto -> bb5; + } + } + diff --git a/tests/mir-opt/jump_threading.too_complex.JumpThreading.panic-unwind.diff b/tests/mir-opt/jump_threading.too_complex.JumpThreading.panic-unwind.diff new file mode 100644 index 00000000000..f5eade4a914 --- /dev/null +++ b/tests/mir-opt/jump_threading.too_complex.JumpThreading.panic-unwind.diff @@ -0,0 +1,98 @@ +- // MIR for `too_complex` before JumpThreading ++ // MIR for `too_complex` after JumpThreading + + fn too_complex(_1: Result<i32, usize>) -> Option<i32> { + debug x => _1; + let mut _0: std::option::Option<i32>; + let mut _2: std::ops::ControlFlow<usize, i32>; + let mut _3: isize; + let _4: i32; + let mut _5: i32; + let _6: usize; + let mut _7: usize; + let mut _8: isize; + let _9: i32; + let mut _10: i32; + let _11: usize; + scope 1 { + debug v => _4; + } + scope 2 { + debug r => _6; + } + scope 3 { + debug v => _9; + } + scope 4 { + debug r => _11; + } + + bb0: { + StorageLive(_2); + _3 = discriminant(_1); + switchInt(move _3) -> [0: bb3, 1: bb1, otherwise: bb2]; + } + + bb1: { + StorageLive(_6); + _6 = ((_1 as Err).0: usize); + StorageLive(_7); + _7 = _6; + _2 = ControlFlow::<usize, i32>::Break(move _7); + StorageDead(_7); + StorageDead(_6); +- goto -> bb4; ++ goto -> bb8; + } + + bb2: { + unreachable; + } + + bb3: { + StorageLive(_4); + _4 = ((_1 as Ok).0: i32); + StorageLive(_5); + _5 = _4; + _2 = ControlFlow::<usize, i32>::Continue(move _5); + StorageDead(_5); + StorageDead(_4); + goto -> bb4; + } + + bb4: { + _8 = discriminant(_2); +- switchInt(move _8) -> [0: bb6, 1: bb5, otherwise: bb2]; ++ goto -> bb6; + } + + bb5: { + StorageLive(_11); + _11 = ((_2 as Break).0: usize); + _0 = Option::<i32>::None; + StorageDead(_11); + goto -> bb7; + } + + bb6: { + StorageLive(_9); + _9 = ((_2 as Continue).0: i32); + StorageLive(_10); + _10 = _9; + _0 = Option::<i32>::Some(move _10); + StorageDead(_10); + StorageDead(_9); + goto -> bb7; + } + + bb7: { + StorageDead(_2); + return; ++ } ++ ++ bb8: { ++ _8 = discriminant(_2); ++ goto -> bb5; + } + } + diff --git a/tests/mir-opt/while_storage.while_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/while_storage.while_loop.PreCodegen.after.panic-abort.mir index e6ce33ed682..21c4b92cf04 100644 --- a/tests/mir-opt/while_storage.while_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/while_storage.while_loop.PreCodegen.after.panic-abort.mir @@ -11,18 +11,6 @@ fn while_loop(_1: bool) -> () { } bb0: { - goto -> bb1; - } - - bb1: { - switchInt(_1) -> [0: bb3, otherwise: bb2]; - } - - bb2: { - switchInt(_1) -> [0: bb1, otherwise: bb3]; - } - - bb3: { return; } } diff --git a/tests/mir-opt/while_storage.while_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/while_storage.while_loop.PreCodegen.after.panic-unwind.mir index e6ce33ed682..21c4b92cf04 100644 --- a/tests/mir-opt/while_storage.while_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/while_storage.while_loop.PreCodegen.after.panic-unwind.mir @@ -11,18 +11,6 @@ fn while_loop(_1: bool) -> () { } bb0: { - goto -> bb1; - } - - bb1: { - switchInt(_1) -> [0: bb3, otherwise: bb2]; - } - - bb2: { - switchInt(_1) -> [0: bb1, otherwise: bb3]; - } - - bb3: { return; } } diff --git a/tests/run-make/coverage-llvmir/filecheck.testprog.txt b/tests/run-make/coverage-llvmir/filecheck.testprog.txt index 9d63fabd788..8ab18da21a2 100644 --- a/tests/run-make/coverage-llvmir/filecheck.testprog.txt +++ b/tests/run-make/coverage-llvmir/filecheck.testprog.txt @@ -3,12 +3,12 @@ WINDOWS: $__llvm_profile_runtime_user = comdat any -CHECK: @__covrec_{{[A-F0-9]+}}u = linkonce_odr hidden constant -CHECK-SAME: section "[[INSTR_PROF_COVFUN]]"[[COMDAT_IF_SUPPORTED]], align 8 - CHECK: @__llvm_coverage_mapping = private constant CHECK-SAME: section "[[INSTR_PROF_COVMAP]]", align 8 +CHECK: @__covrec_{{[A-F0-9]+}}u = linkonce_odr hidden constant +CHECK-SAME: section "[[INSTR_PROF_COVFUN]]"[[COMDAT_IF_SUPPORTED]], align 8 + WINDOWS: @__llvm_profile_runtime = external{{.*}}global i32 CHECK: @__profc__R{{[a-zA-Z0-9_]+}}testprog14will_be_called = {{private|internal}} global diff --git a/tests/ui-fulldeps/stable-mir/check_instance.rs b/tests/ui-fulldeps/stable-mir/check_instance.rs index 288c163a6a3..c6a9e08ed02 100644 --- a/tests/ui-fulldeps/stable-mir/check_instance.rs +++ b/tests/ui-fulldeps/stable-mir/check_instance.rs @@ -4,6 +4,7 @@ // ignore-stage1 // ignore-cross-compile // ignore-remote +// ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 // edition: 2021 #![feature(rustc_private)] @@ -11,8 +12,11 @@ #![feature(control_flow_enum)] extern crate rustc_middle; +#[macro_use] extern crate rustc_smir; extern crate stable_mir; +extern crate rustc_driver; +extern crate rustc_interface; use rustc_middle::ty::TyCtxt; use mir::{mono::Instance, TerminatorKind::*}; @@ -82,7 +86,7 @@ fn main() { CRATE_NAME.to_string(), path.to_string(), ]; - rustc_internal::StableMir::new(args, test_stable_mir).run().unwrap(); + run!(args, tcx, test_stable_mir(tcx)).unwrap(); } fn generate_input(path: &str) -> std::io::Result<()> { diff --git a/tests/ui-fulldeps/stable-mir/compilation-result.rs b/tests/ui-fulldeps/stable-mir/compilation-result.rs index 3ec1519fb13..fc56e24814b 100644 --- a/tests/ui-fulldeps/stable-mir/compilation-result.rs +++ b/tests/ui-fulldeps/stable-mir/compilation-result.rs @@ -4,19 +4,22 @@ // ignore-stage1 // ignore-cross-compile // ignore-remote +// ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 // edition: 2021 #![feature(rustc_private)] #![feature(assert_matches)] extern crate rustc_middle; +#[macro_use] extern crate rustc_smir; +extern crate rustc_driver; +extern crate rustc_interface; extern crate stable_mir; use rustc_middle::ty::TyCtxt; use rustc_smir::rustc_internal; use std::io::Write; -use std::ops::ControlFlow; /// This test will generate and analyze a dummy crate using the stable mir. /// For that, it will first write the dummy crate into a file. @@ -33,28 +36,26 @@ fn main() { } fn test_continue(args: Vec<String>) { - let continue_fn = |_: TyCtxt| ControlFlow::Continue::<(), bool>(true); - let result = rustc_internal::StableMir::new(args, continue_fn).run(); + let result = run!(args, ControlFlow::Continue::<(), bool>(true)); assert_eq!(result, Ok(true)); } fn test_break(args: Vec<String>) { - let continue_fn = |_: TyCtxt| ControlFlow::Break::<bool, i32>(false); - let result = rustc_internal::StableMir::new(args, continue_fn).run(); + let result = run!(args, ControlFlow::Break::<bool, i32>(false)); assert_eq!(result, Err(stable_mir::CompilerError::Interrupted(false))); } +#[allow(unreachable_code)] fn test_skipped(mut args: Vec<String>) { args.push("--version".to_string()); - let unreach_fn = |_: TyCtxt| -> ControlFlow<()> { unreachable!() }; - let result = rustc_internal::StableMir::new(args, unreach_fn).run(); + let result = run!(args, unreachable!() as ControlFlow<()>); assert_eq!(result, Err(stable_mir::CompilerError::Skipped)); } +#[allow(unreachable_code)] fn test_failed(mut args: Vec<String>) { args.push("--cfg=broken".to_string()); - let unreach_fn = |_: TyCtxt| -> ControlFlow<()> { unreachable!() }; - let result = rustc_internal::StableMir::new(args, unreach_fn).run(); + let result = run!(args, unreachable!() as ControlFlow<()>); assert_eq!(result, Err(stable_mir::CompilerError::CompilationFailed)); } diff --git a/tests/ui-fulldeps/stable-mir/crate-info.rs b/tests/ui-fulldeps/stable-mir/crate-info.rs index 8a812bd3265..3cb71b5a025 100644 --- a/tests/ui-fulldeps/stable-mir/crate-info.rs +++ b/tests/ui-fulldeps/stable-mir/crate-info.rs @@ -4,6 +4,7 @@ // ignore-stage1 // ignore-cross-compile // ignore-remote +// ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 // edition: 2021 #![feature(rustc_private)] @@ -12,7 +13,10 @@ extern crate rustc_hir; extern crate rustc_middle; +#[macro_use] extern crate rustc_smir; +extern crate rustc_driver; +extern crate rustc_interface; extern crate stable_mir; use rustc_hir::def::DefKind; @@ -185,7 +189,7 @@ fn main() { CRATE_NAME.to_string(), path.to_string(), ]; - rustc_internal::StableMir::new(args, test_stable_mir).run().unwrap(); + run!(args, tcx, test_stable_mir(tcx)).unwrap(); } fn generate_input(path: &str) -> std::io::Result<()> { diff --git a/tests/ui/associated-consts/issue-105330.rs b/tests/ui/associated-consts/issue-105330.rs index 86e45f10b0e..285e89cce49 100644 --- a/tests/ui/associated-consts/issue-105330.rs +++ b/tests/ui/associated-consts/issue-105330.rs @@ -9,13 +9,10 @@ impl TraitWAssocConst for impl Demo { //~ ERROR E0404 } fn foo<A: TraitWAssocConst<A=32>>() { //~ ERROR E0658 - foo::<Demo>()(); //~ ERROR E0271 - //~^ ERROR E0618 - //~| ERROR E0277 + foo::<Demo>()(); } -fn main<A: TraitWAssocConst<A=32>>() { //~ ERROR E0131 +fn main<A: TraitWAssocConst<A=32>>() { //~^ ERROR E0658 - foo::<Demo>(); //~ ERROR E0277 - //~^ ERROR E0271 + foo::<Demo>(); } diff --git a/tests/ui/associated-consts/issue-105330.stderr b/tests/ui/associated-consts/issue-105330.stderr index 200856caa25..55207909f7a 100644 --- a/tests/ui/associated-consts/issue-105330.stderr +++ b/tests/ui/associated-consts/issue-105330.stderr @@ -25,7 +25,7 @@ LL | fn foo<A: TraitWAssocConst<A=32>>() { = help: add `#![feature(associated_const_equality)]` to the crate attributes to enable error[E0658]: associated const equality is incomplete - --> $DIR/issue-105330.rs:17:29 + --> $DIR/issue-105330.rs:15:29 | LL | fn main<A: TraitWAssocConst<A=32>>() { | ^^^^ @@ -39,85 +39,7 @@ error[E0562]: `impl Trait` only allowed in function and inherent method argument LL | impl TraitWAssocConst for impl Demo { | ^^^^^^^^^ -error[E0131]: `main` function is not allowed to have generic parameters - --> $DIR/issue-105330.rs:17:8 - | -LL | fn main<A: TraitWAssocConst<A=32>>() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `main` cannot have generic parameters - -error[E0277]: the trait bound `Demo: TraitWAssocConst` is not satisfied - --> $DIR/issue-105330.rs:12:11 - | -LL | foo::<Demo>()(); - | ^^^^ the trait `TraitWAssocConst` is not implemented for `Demo` - | -help: this trait has no implementations, consider adding one - --> $DIR/issue-105330.rs:1:1 - | -LL | pub trait TraitWAssocConst { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: required by a bound in `foo` - --> $DIR/issue-105330.rs:11:11 - | -LL | fn foo<A: TraitWAssocConst<A=32>>() { - | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `foo` - -error[E0271]: type mismatch resolving `<Demo as TraitWAssocConst>::A == 32` - --> $DIR/issue-105330.rs:12:11 - | -LL | foo::<Demo>()(); - | ^^^^ expected `32`, found `<Demo as TraitWAssocConst>::A` - | - = note: expected constant `32` - found constant `<Demo as TraitWAssocConst>::A` -note: required by a bound in `foo` - --> $DIR/issue-105330.rs:11:28 - | -LL | fn foo<A: TraitWAssocConst<A=32>>() { - | ^^^^ required by this bound in `foo` - -error[E0618]: expected function, found `()` - --> $DIR/issue-105330.rs:12:5 - | -LL | fn foo<A: TraitWAssocConst<A=32>>() { - | ----------------------------------- `foo::<Demo>` defined here returns `()` -LL | foo::<Demo>()(); - | ^^^^^^^^^^^^^-- - | | - | call expression requires function - -error[E0277]: the trait bound `Demo: TraitWAssocConst` is not satisfied - --> $DIR/issue-105330.rs:19:11 - | -LL | foo::<Demo>(); - | ^^^^ the trait `TraitWAssocConst` is not implemented for `Demo` - | -help: this trait has no implementations, consider adding one - --> $DIR/issue-105330.rs:1:1 - | -LL | pub trait TraitWAssocConst { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: required by a bound in `foo` - --> $DIR/issue-105330.rs:11:11 - | -LL | fn foo<A: TraitWAssocConst<A=32>>() { - | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `foo` - -error[E0271]: type mismatch resolving `<Demo as TraitWAssocConst>::A == 32` - --> $DIR/issue-105330.rs:19:11 - | -LL | foo::<Demo>(); - | ^^^^ expected `32`, found `<Demo as TraitWAssocConst>::A` - | - = note: expected constant `32` - found constant `<Demo as TraitWAssocConst>::A` -note: required by a bound in `foo` - --> $DIR/issue-105330.rs:11:28 - | -LL | fn foo<A: TraitWAssocConst<A=32>>() { - | ^^^^ required by this bound in `foo` - -error: aborting due to 11 previous errors +error: aborting due to 5 previous errors -Some errors have detailed explanations: E0131, E0271, E0277, E0404, E0562, E0618, E0658. -For more information about an error, try `rustc --explain E0131`. +Some errors have detailed explanations: E0404, E0562, E0658. +For more information about an error, try `rustc --explain E0404`. diff --git a/tests/ui/associated-consts/issue-58022.rs b/tests/ui/associated-consts/issue-58022.rs index 2a8a1eaa6d3..8e2a441f239 100644 --- a/tests/ui/associated-consts/issue-58022.rs +++ b/tests/ui/associated-consts/issue-58022.rs @@ -11,6 +11,7 @@ impl Bar<[u8]> { const SIZE: usize = 32; fn new(slice: &[u8; Self::SIZE]) -> Self { + //~^ ERROR: the size for values of type `[u8]` cannot be known at compilation time Foo(Box::new(*slice)) //~^ ERROR: expected function, tuple struct or tuple variant, found trait `Foo` } diff --git a/tests/ui/associated-consts/issue-58022.stderr b/tests/ui/associated-consts/issue-58022.stderr index 56d85c066a8..82cbc9ed3b0 100644 --- a/tests/ui/associated-consts/issue-58022.stderr +++ b/tests/ui/associated-consts/issue-58022.stderr @@ -7,13 +7,27 @@ LL | LL | fn new(slice: &[u8; Foo::SIZE]) -> Self; | ^^^^^^^^^ cannot refer to the associated constant of trait +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/issue-58022.rs:13:41 + | +LL | fn new(slice: &[u8; Self::SIZE]) -> Self { + | ^^^^ doesn't have a size known at compile-time + | + = help: within `Bar<[u8]>`, the trait `Sized` is not implemented for `[u8]` +note: required because it appears within the type `Bar<[u8]>` + --> $DIR/issue-58022.rs:8:12 + | +LL | pub struct Bar<T: ?Sized>(T); + | ^^^ + = note: the return type of a function must have a statically known size + error[E0423]: expected function, tuple struct or tuple variant, found trait `Foo` - --> $DIR/issue-58022.rs:14:9 + --> $DIR/issue-58022.rs:15:9 | LL | Foo(Box::new(*slice)) | ^^^ not a function, tuple struct or tuple variant -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0423, E0790. -For more information about an error, try `rustc --explain E0423`. +Some errors have detailed explanations: E0277, E0423, E0790. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/async-await/issue-66312.rs b/tests/ui/async-await/issue-66312.rs index 9224971ecb1..fbc58697d48 100644 --- a/tests/ui/async-await/issue-66312.rs +++ b/tests/ui/async-await/issue-66312.rs @@ -6,7 +6,7 @@ trait Test<T> { async fn f() { let x = Some(2); - if x.is_some() { + if x.is_some() { //~ ERROR mismatched types println!("Some"); } } diff --git a/tests/ui/async-await/issue-66312.stderr b/tests/ui/async-await/issue-66312.stderr index 80d294a10a0..dad5807cb50 100644 --- a/tests/ui/async-await/issue-66312.stderr +++ b/tests/ui/async-await/issue-66312.stderr @@ -7,6 +7,13 @@ LL | fn is_some(self: T); = note: type of `self` must be `Self` or a type that dereferences to it = help: consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one of the previous types except `Self`) -error: aborting due to previous error +error[E0308]: mismatched types + --> $DIR/issue-66312.rs:9:8 + | +LL | if x.is_some() { + | ^^^^^^^^^^^ expected `bool`, found `()` + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0307`. +Some errors have detailed explanations: E0307, E0308. +For more information about an error, try `rustc --explain E0307`. diff --git a/tests/ui/const-generics/generic_arg_infer/infer-arg-test.rs b/tests/ui/const-generics/generic_arg_infer/infer-arg-test.rs index 29aa0f59d74..c254b4ee09d 100644 --- a/tests/ui/const-generics/generic_arg_infer/infer-arg-test.rs +++ b/tests/ui/const-generics/generic_arg_infer/infer-arg-test.rs @@ -15,7 +15,7 @@ fn bad_infer_fn<_>() {} fn main() { - let a: All<_, _, _>; + let a: All<_, _, _>; //~ ERROR struct takes 2 generic arguments but 3 all_fn(); let v: [u8; _]; let v: [u8; 10] = [0; _]; diff --git a/tests/ui/const-generics/generic_arg_infer/infer-arg-test.stderr b/tests/ui/const-generics/generic_arg_infer/infer-arg-test.stderr index e6d0c743d01..a6b736261e0 100644 --- a/tests/ui/const-generics/generic_arg_infer/infer-arg-test.stderr +++ b/tests/ui/const-generics/generic_arg_infer/infer-arg-test.stderr @@ -19,6 +19,21 @@ LL | struct BadInfer<_>; = help: consider removing `_`, referring to it in a field, or using a marker such as `PhantomData` = help: if you intended `_` to be a const parameter, use `const _: usize` instead -error: aborting due to 3 previous errors +error[E0107]: struct takes 2 generic arguments but 3 generic arguments were supplied + --> $DIR/infer-arg-test.rs:18:10 + | +LL | let a: All<_, _, _>; + | ^^^ - help: remove this generic argument + | | + | expected 2 generic arguments + | +note: struct defined here, with 2 generic parameters: `T`, `N` + --> $DIR/infer-arg-test.rs:3:8 + | +LL | struct All<'a, T, const N: usize> { + | ^^^ - -------------- + +error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0392`. +Some errors have detailed explanations: E0107, E0392. +For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/consts/issue-116186.rs b/tests/ui/consts/issue-116186.rs new file mode 100644 index 00000000000..a77c38c64dc --- /dev/null +++ b/tests/ui/consts/issue-116186.rs @@ -0,0 +1,12 @@ +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] + +fn something(path: [usize; N]) -> impl Clone { + //~^ ERROR cannot find value `N` in this scope + match path { + [] => 0, //~ ERROR cannot pattern-match on an array without a fixed length + _ => 1, + }; +} + +fn main() {} diff --git a/tests/ui/consts/issue-116186.stderr b/tests/ui/consts/issue-116186.stderr new file mode 100644 index 00000000000..e6eae2d9f55 --- /dev/null +++ b/tests/ui/consts/issue-116186.stderr @@ -0,0 +1,21 @@ +error[E0425]: cannot find value `N` in this scope + --> $DIR/issue-116186.rs:4:28 + | +LL | fn something(path: [usize; N]) -> impl Clone { + | ^ not found in this scope + | +help: you might be missing a const parameter + | +LL | fn something<const N: /* Type */>(path: [usize; N]) -> impl Clone { + | +++++++++++++++++++++ + +error[E0730]: cannot pattern-match on an array without a fixed length + --> $DIR/issue-116186.rs:7:9 + | +LL | [] => 0, + | ^^ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0425, E0730. +For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/consts/issue-39974.rs b/tests/ui/consts/issue-39974.rs index 503647ef4a8..9cb180014b8 100644 --- a/tests/ui/consts/issue-39974.rs +++ b/tests/ui/consts/issue-39974.rs @@ -1,4 +1,5 @@ const LENGTH: f64 = 2; +//~^ ERROR mismatched types struct Thing { f: [[f64; 2]; LENGTH], diff --git a/tests/ui/consts/issue-39974.stderr b/tests/ui/consts/issue-39974.stderr index 56365e51e0a..4bde599039e 100644 --- a/tests/ui/consts/issue-39974.stderr +++ b/tests/ui/consts/issue-39974.stderr @@ -1,9 +1,18 @@ error[E0308]: mismatched types - --> $DIR/issue-39974.rs:4:19 + --> $DIR/issue-39974.rs:5:19 | LL | f: [[f64; 2]; LENGTH], | ^^^^^^ expected `usize`, found `f64` -error: aborting due to previous error +error[E0308]: mismatched types + --> $DIR/issue-39974.rs:1:21 + | +LL | const LENGTH: f64 = 2; + | ^ + | | + | expected `f64`, found integer + | help: use a float literal: `2.0` + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/generic-associated-types/issue-86787.rs b/tests/ui/generic-associated-types/issue-86787.rs index 96075ca503d..5edd0a9f02f 100644 --- a/tests/ui/generic-associated-types/issue-86787.rs +++ b/tests/ui/generic-associated-types/issue-86787.rs @@ -22,8 +22,8 @@ where type T = Either<Left::T, Right::T>; type TRef<'a> = Either<&'a Left::T, &'a Right::T> where - <Left as HasChildrenOf>::T: 'a, - <Right as HasChildrenOf>::T: 'a; + <Left as HasChildrenOf>::T: 'a, //~ ERROR impl has stricter requirements than trait + <Right as HasChildrenOf>::T: 'a; //~ ERROR impl has stricter requirements than trait fn ref_children<'a>(&'a self) -> Vec<Self::TRef<'a>> { todo!() diff --git a/tests/ui/generic-associated-types/issue-86787.stderr b/tests/ui/generic-associated-types/issue-86787.stderr index f34c63cf72e..00795abbd05 100644 --- a/tests/ui/generic-associated-types/issue-86787.stderr +++ b/tests/ui/generic-associated-types/issue-86787.stderr @@ -9,5 +9,24 @@ LL | type TRef<'a>; = note: this bound is currently required to ensure that impls have maximum flexibility = note: we are soliciting feedback, see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information -error: aborting due to previous error +error[E0276]: impl has stricter requirements than trait + --> $DIR/issue-86787.rs:25:37 + | +LL | type TRef<'a>; + | ------------- definition of `TRef` from trait +... +LL | <Left as HasChildrenOf>::T: 'a, + | ^^ impl has extra requirement `<Left as HasChildrenOf>::T: 'a` + +error[E0276]: impl has stricter requirements than trait + --> $DIR/issue-86787.rs:26:38 + | +LL | type TRef<'a>; + | ------------- definition of `TRef` from trait +... +LL | <Right as HasChildrenOf>::T: 'a; + | ^^ impl has extra requirement `<Right as HasChildrenOf>::T: 'a` + +error: aborting due to 3 previous errors +For more information about this error, try `rustc --explain E0276`. diff --git a/tests/ui/imports/pub-reexport-empty.rs b/tests/ui/imports/pub-reexport-empty.rs new file mode 100644 index 00000000000..2a46f4c8de8 --- /dev/null +++ b/tests/ui/imports/pub-reexport-empty.rs @@ -0,0 +1,25 @@ +#![deny(unused_imports)] + +mod a {} + +pub use a::*; +//~^ ERROR: unused import: `a::*` + +mod b { + mod c { + #[derive(Clone)] + pub struct D; + } + pub use self::c::*; // don't show unused import lint +} + +pub use b::*; // don't show unused import lint + +mod d { + const D: i32 = 1; +} + +pub use d::*; +//~^ ERROR: unused import: `d::*` + +fn main() {} diff --git a/tests/ui/imports/pub-reexport-empty.stderr b/tests/ui/imports/pub-reexport-empty.stderr new file mode 100644 index 00000000000..813b2ef71c5 --- /dev/null +++ b/tests/ui/imports/pub-reexport-empty.stderr @@ -0,0 +1,20 @@ +error: unused import: `a::*` + --> $DIR/pub-reexport-empty.rs:5:9 + | +LL | pub use a::*; + | ^^^^ + | +note: the lint level is defined here + --> $DIR/pub-reexport-empty.rs:1:9 + | +LL | #![deny(unused_imports)] + | ^^^^^^^^^^^^^^ + +error: unused import: `d::*` + --> $DIR/pub-reexport-empty.rs:22:9 + | +LL | pub use d::*; + | ^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/imports/reexports.rs b/tests/ui/imports/reexports.rs index d76cc41be4e..cb1a3ebe180 100644 --- a/tests/ui/imports/reexports.rs +++ b/tests/ui/imports/reexports.rs @@ -5,9 +5,12 @@ mod a { mod foo {} mod a { - pub use super::foo; //~ ERROR cannot be re-exported + pub use super::foo; + //~^ ERROR cannot be re-exported + //~| WARNING unused import: `super::foo` pub use super::*; //~^ WARNING glob import doesn't reexport anything because no candidate is public enough + //~| WARNING unused import: `super::*` } } diff --git a/tests/ui/imports/reexports.stderr b/tests/ui/imports/reexports.stderr index 8cbff0ac73d..401e422af0f 100644 --- a/tests/ui/imports/reexports.stderr +++ b/tests/ui/imports/reexports.stderr @@ -11,44 +11,44 @@ LL | pub use super::foo; | ^^^^^^^^^^ error[E0603]: module import `foo` is private - --> $DIR/reexports.rs:33:15 + --> $DIR/reexports.rs:36:15 | LL | use b::a::foo::S; | ^^^ private module import | note: the module import `foo` is defined here... - --> $DIR/reexports.rs:21:17 + --> $DIR/reexports.rs:24:17 | LL | pub use super::foo; // This is OK since the value `foo` is visible enough. | ^^^^^^^^^^ note: ...and refers to the module `foo` which is defined here - --> $DIR/reexports.rs:16:5 + --> $DIR/reexports.rs:19:5 | LL | mod foo { | ^^^^^^^ error[E0603]: module import `foo` is private - --> $DIR/reexports.rs:34:15 + --> $DIR/reexports.rs:37:15 | LL | use b::b::foo::S as T; | ^^^ private module import | note: the module import `foo` is defined here... - --> $DIR/reexports.rs:26:17 + --> $DIR/reexports.rs:29:17 | LL | pub use super::*; // This is also OK since the value `foo` is visible enough. | ^^^^^^^^ note: ...and refers to the module `foo` which is defined here - --> $DIR/reexports.rs:16:5 + --> $DIR/reexports.rs:19:5 | LL | mod foo { | ^^^^^^^ -warning: glob import doesn't reexport anything because no candidate is public enough - --> $DIR/reexports.rs:9:17 +warning: unused import: `super::foo` + --> $DIR/reexports.rs:8:17 | -LL | pub use super::*; - | ^^^^^^^^ +LL | pub use super::foo; + | ^^^^^^^^^^ | note: the lint level is defined here --> $DIR/reexports.rs:1:9 @@ -56,7 +56,19 @@ note: the lint level is defined here LL | #![warn(unused_imports)] | ^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors; 1 warning emitted +warning: glob import doesn't reexport anything because no candidate is public enough + --> $DIR/reexports.rs:11:17 + | +LL | pub use super::*; + | ^^^^^^^^ + +warning: unused import: `super::*` + --> $DIR/reexports.rs:11:17 + | +LL | pub use super::*; + | ^^^^^^^^ + +error: aborting due to 3 previous errors; 3 warnings emitted Some errors have detailed explanations: E0364, E0603. For more information about an error, try `rustc --explain E0364`. diff --git a/tests/ui/inference/issue-107090.rs b/tests/ui/inference/issue-107090.rs index a22e12c6d88..799c3641833 100644 --- a/tests/ui/inference/issue-107090.rs +++ b/tests/ui/inference/issue-107090.rs @@ -2,8 +2,8 @@ use std::marker::PhantomData; struct Foo<'a, 'b, T>(PhantomData<(&'a (), &'b (), T)>) where Foo<'short, 'out, T>: Convert<'a, 'b>; - //~^ ERROR use of undeclared lifetime name - //~| ERROR use of undeclared lifetime name `'out` +//~^ ERROR use of undeclared lifetime name +//~| ERROR use of undeclared lifetime name `'out` trait Convert<'a, 'b>: Sized { fn cast(&'a self) -> &'b Self; @@ -19,7 +19,7 @@ impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> { fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, 'out, T>) -> &'out T { //~^ ERROR use of undeclared lifetime name - sadness.cast() //~ ERROR mismatched types + sadness.cast() } fn main() {} diff --git a/tests/ui/inference/issue-107090.stderr b/tests/ui/inference/issue-107090.stderr index 55825f7765b..e509e262fb1 100644 --- a/tests/ui/inference/issue-107090.stderr +++ b/tests/ui/inference/issue-107090.stderr @@ -66,19 +66,6 @@ LL | fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, | | | help: consider introducing lifetime `'short` here: `'short,` -error[E0308]: mismatched types - --> $DIR/issue-107090.rs:22:5 - | -LL | fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, 'out, T>) -> &'out T { - | - expected this type parameter ------- expected `&'out T` because of return type -LL | -LL | sadness.cast() - | ^^^^^^^^^^^^^^ expected `&T`, found `&Foo<'_, '_, T>` - | - = note: expected reference `&'out T` - found reference `&Foo<'_, '_, T>` - -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors -Some errors have detailed explanations: E0261, E0308. -For more information about an error, try `rustc --explain E0261`. +For more information about this error, try `rustc --explain E0261`. diff --git a/tests/ui/infinite/infinite-struct.stderr b/tests/ui/infinite/infinite-struct.stderr index b6c72b1de46..82d147b63cd 100644 --- a/tests/ui/infinite/infinite-struct.stderr +++ b/tests/ui/infinite/infinite-struct.stderr @@ -22,6 +22,10 @@ help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle LL | x: Bar<Box<Foo>>, | ++++ + -error: aborting due to 2 previous errors +error: reached the recursion limit finding the struct tail for `Take` + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0072`. diff --git a/tests/ui/instrument-coverage/bad-value.bad.stderr b/tests/ui/instrument-coverage/bad-value.bad.stderr new file mode 100644 index 00000000000..246c4f31a4b --- /dev/null +++ b/tests/ui/instrument-coverage/bad-value.bad.stderr @@ -0,0 +1,2 @@ +error: incorrect value `bad-value` for codegen option `instrument-coverage` - `all` (default), `except-unused-generics`, `except-unused-functions`, or `off` was expected + diff --git a/tests/ui/instrument-coverage/bad-value.blank.stderr b/tests/ui/instrument-coverage/bad-value.blank.stderr new file mode 100644 index 00000000000..b539c558d9b --- /dev/null +++ b/tests/ui/instrument-coverage/bad-value.blank.stderr @@ -0,0 +1,2 @@ +error: incorrect value `` for codegen option `instrument-coverage` - `all` (default), `except-unused-generics`, `except-unused-functions`, or `off` was expected + diff --git a/tests/ui/instrument-coverage/bad-value.rs b/tests/ui/instrument-coverage/bad-value.rs new file mode 100644 index 00000000000..1925c36aa53 --- /dev/null +++ b/tests/ui/instrument-coverage/bad-value.rs @@ -0,0 +1,5 @@ +// revisions: blank bad +// [blank] compile-flags: -Cinstrument-coverage= +// [bad] compile-flags: -Cinstrument-coverage=bad-value + +fn main() {} diff --git a/tests/ui/instrument-coverage/except-unused-functions.rs b/tests/ui/instrument-coverage/except-unused-functions.rs new file mode 100644 index 00000000000..5a0b7d4fef9 --- /dev/null +++ b/tests/ui/instrument-coverage/except-unused-functions.rs @@ -0,0 +1,3 @@ +// compile-flags: -Cinstrument-coverage=except-unused-functions + +fn main() {} diff --git a/tests/ui/instrument-coverage/except-unused-functions.stderr b/tests/ui/instrument-coverage/except-unused-functions.stderr new file mode 100644 index 00000000000..82c1c630cbf --- /dev/null +++ b/tests/ui/instrument-coverage/except-unused-functions.stderr @@ -0,0 +1,2 @@ +error: `-C instrument-coverage=except-*` requires `-Z unstable-options` + diff --git a/tests/ui/instrument-coverage/except-unused-generics.rs b/tests/ui/instrument-coverage/except-unused-generics.rs new file mode 100644 index 00000000000..4b1ddf29026 --- /dev/null +++ b/tests/ui/instrument-coverage/except-unused-generics.rs @@ -0,0 +1,3 @@ +// compile-flags: -Cinstrument-coverage=except-unused-generics + +fn main() {} diff --git a/tests/ui/instrument-coverage/except-unused-generics.stderr b/tests/ui/instrument-coverage/except-unused-generics.stderr new file mode 100644 index 00000000000..82c1c630cbf --- /dev/null +++ b/tests/ui/instrument-coverage/except-unused-generics.stderr @@ -0,0 +1,2 @@ +error: `-C instrument-coverage=except-*` requires `-Z unstable-options` + diff --git a/tests/ui/instrument-coverage/off-values.rs b/tests/ui/instrument-coverage/off-values.rs new file mode 100644 index 00000000000..0f9a0c47425 --- /dev/null +++ b/tests/ui/instrument-coverage/off-values.rs @@ -0,0 +1,9 @@ +// check-pass +// revisions: n no off false zero +// [n] compile-flags: -Cinstrument-coverage=n +// [no] compile-flags: -Cinstrument-coverage=no +// [off] compile-flags: -Cinstrument-coverage=off +// [false] compile-flags: -Cinstrument-coverage=false +// [zero] compile-flags: -Cinstrument-coverage=0 + +fn main() {} diff --git a/tests/ui/instrument-coverage/on-values.rs b/tests/ui/instrument-coverage/on-values.rs new file mode 100644 index 00000000000..cc54c71c656 --- /dev/null +++ b/tests/ui/instrument-coverage/on-values.rs @@ -0,0 +1,11 @@ +// check-pass +// needs-profiler-support +// revisions: default y yes on true all +// [default] compile-flags: -Cinstrument-coverage +// [y] compile-flags: -Cinstrument-coverage=y +// [yes] compile-flags: -Cinstrument-coverage=yes +// [on] compile-flags: -Cinstrument-coverage=on +// [true] compile-flags: -Cinstrument-coverage=true +// [all] compile-flags: -Cinstrument-coverage=all + +fn main() {} diff --git a/tests/ui/issues/issue-12567.stderr b/tests/ui/issues/issue-12567.stderr index 7fa06825f0f..3f95f18a967 100644 --- a/tests/ui/issues/issue-12567.stderr +++ b/tests/ui/issues/issue-12567.stderr @@ -8,7 +8,7 @@ LL | (&[], &[hd, ..]) | (&[hd, ..], &[]) | -- data moved here LL | => println!("one empty"), LL | (&[hd1, ..], &[hd2, ..]) - | --- ...and here + | --- ...and here | = note: move occurs because these variables have types that don't implement the `Copy` trait help: consider borrowing the pattern binding @@ -17,8 +17,8 @@ LL | (&[], &[ref hd, ..]) | (&[hd, ..], &[]) | +++ help: consider borrowing the pattern binding | -LL | (&[ref hd1, ..], &[hd2, ..]) - | +++ +LL | (&[hd1, ..], &[ref hd2, ..]) + | +++ error[E0508]: cannot move out of type `[T]`, a non-copy slice --> $DIR/issue-12567.rs:2:11 @@ -30,7 +30,7 @@ LL | (&[], &[hd, ..]) | (&[hd, ..], &[]) | -- data moved here LL | => println!("one empty"), LL | (&[hd1, ..], &[hd2, ..]) - | --- ...and here + | --- ...and here | = note: move occurs because these variables have types that don't implement the `Copy` trait help: consider borrowing the pattern binding @@ -39,8 +39,8 @@ LL | (&[], &[ref hd, ..]) | (&[hd, ..], &[]) | +++ help: consider borrowing the pattern binding | -LL | (&[hd1, ..], &[ref hd2, ..]) - | +++ +LL | (&[ref hd1, ..], &[hd2, ..]) + | +++ error: aborting due to 2 previous errors diff --git a/tests/ui/issues/issue-77919.rs b/tests/ui/issues/issue-77919.rs index 966d76d148a..3cbf493afb8 100644 --- a/tests/ui/issues/issue-77919.rs +++ b/tests/ui/issues/issue-77919.rs @@ -10,4 +10,3 @@ struct Multiply<N, M> { } impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {} //~^ ERROR cannot find type `VAL` in this scope -//~| ERROR not all trait items implemented, missing: `VAL` diff --git a/tests/ui/issues/issue-77919.stderr b/tests/ui/issues/issue-77919.stderr index dbbe70ff069..9d2f859e05a 100644 --- a/tests/ui/issues/issue-77919.stderr +++ b/tests/ui/issues/issue-77919.stderr @@ -20,16 +20,6 @@ help: you might be missing a type parameter LL | impl<N, M, VAL> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {} | +++++ -error[E0046]: not all trait items implemented, missing: `VAL` - --> $DIR/issue-77919.rs:11:1 - | -LL | const VAL: T; - | ------------ `VAL` from trait -... -LL | impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `VAL` in implementation - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0046, E0412. -For more information about an error, try `rustc --explain E0046`. +For more information about this error, try `rustc --explain E0412`. diff --git a/tests/ui/layout/cannot-transmute-unnormalizable-type.rs b/tests/ui/layout/cannot-transmute-unnormalizable-type.rs index d2b6e1d8eba..1a2ff8c4705 100644 --- a/tests/ui/layout/cannot-transmute-unnormalizable-type.rs +++ b/tests/ui/layout/cannot-transmute-unnormalizable-type.rs @@ -16,7 +16,8 @@ struct Other { fn main() { unsafe { + // FIXME(oli-obk): make this report a transmute error again. std::mem::transmute::<Option<()>, Option<&Other>>(None); - //~^ ERROR cannot transmute between types of different sizes, or dependently-sized types + //^ ERROR cannot transmute between types of different sizes, or dependently-sized types } } diff --git a/tests/ui/layout/cannot-transmute-unnormalizable-type.stderr b/tests/ui/layout/cannot-transmute-unnormalizable-type.stderr index dd5119318ff..ee2c5ab7e39 100644 --- a/tests/ui/layout/cannot-transmute-unnormalizable-type.stderr +++ b/tests/ui/layout/cannot-transmute-unnormalizable-type.stderr @@ -4,16 +4,6 @@ error[E0412]: cannot find type `Missing` in this scope LL | Missing: Trait, | ^^^^^^^ not found in this scope -error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/cannot-transmute-unnormalizable-type.rs:19:9 - | -LL | std::mem::transmute::<Option<()>, Option<&Other>>(None); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: source type: `Option<()>` (8 bits) - = note: target type: `Option<&Other>` (unable to determine layout for `Other` because `<() as Trait>::RefTarget` cannot be normalized) - -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0412, E0512. -For more information about an error, try `rustc --explain E0412`. +For more information about this error, try `rustc --explain E0412`. diff --git a/tests/ui/lint/unused/lint-unused-imports.rs b/tests/ui/lint/unused/lint-unused-imports.rs index 953992ecf71..4fa6511c97e 100644 --- a/tests/ui/lint/unused/lint-unused-imports.rs +++ b/tests/ui/lint/unused/lint-unused-imports.rs @@ -42,7 +42,7 @@ mod foo { pub struct Square{pub p: Point, pub h: usize, pub w: usize} } -mod bar { +pub mod bar { // Don't ignore on 'pub use' because we're not sure if it's used or not pub use std::cmp::PartialEq; pub struct Square; diff --git a/tests/ui/nll/move-errors.stderr b/tests/ui/nll/move-errors.stderr index 58b8aa31d4c..0d994ef29ba 100644 --- a/tests/ui/nll/move-errors.stderr +++ b/tests/ui/nll/move-errors.stderr @@ -186,7 +186,7 @@ help: consider borrowing the pattern binding LL | F(s, ref mut t) => (), | +++ -error[E0507]: cannot move out of `x` as enum variant `Err` which is behind a shared reference +error[E0507]: cannot move out of `x` as enum variant `Ok` which is behind a shared reference --> $DIR/move-errors.rs:110:11 | LL | match *x { diff --git a/tests/ui/nll/polonius/location-insensitive-scopes-issue-116657.nll.stderr b/tests/ui/nll/polonius/location-insensitive-scopes-issue-116657.nll.stderr new file mode 100644 index 00000000000..6f9b3303163 --- /dev/null +++ b/tests/ui/nll/polonius/location-insensitive-scopes-issue-116657.nll.stderr @@ -0,0 +1,36 @@ +error[E0046]: not all trait items implemented, missing: `call` + --> $DIR/location-insensitive-scopes-issue-116657.rs:18:1 + | +LL | fn call(x: Self) -> Self::Output; + | --------------------------------- `call` from trait +... +LL | impl<T: PlusOne> Callable for T { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `call` in implementation + +error: unconstrained opaque type + --> $DIR/location-insensitive-scopes-issue-116657.rs:22:19 + | +LL | type Output = impl PlusOne; + | ^^^^^^^^^^^^ + | + = note: `Output` must be used in combination with a concrete type within the same impl + +error[E0700]: hidden type for `impl PlusOne` captures lifetime that does not appear in bounds + --> $DIR/location-insensitive-scopes-issue-116657.rs:28:5 + | +LL | fn test<'a>(y: &'a mut i32) -> impl PlusOne { + | -- ------------ opaque type defined here + | | + | hidden type `<&'a mut i32 as Callable>::Output` captures the lifetime `'a` as defined here +LL | <&mut i32 as Callable>::call(y) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: to declare that `impl PlusOne` captures `'a`, you can add an explicit `'a` lifetime bound + | +LL | fn test<'a>(y: &'a mut i32) -> impl PlusOne + 'a { + | ++++ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0046, E0700. +For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/nll/polonius/location-insensitive-scopes-issue-116657.polonius.stderr b/tests/ui/nll/polonius/location-insensitive-scopes-issue-116657.polonius.stderr new file mode 100644 index 00000000000..6f9b3303163 --- /dev/null +++ b/tests/ui/nll/polonius/location-insensitive-scopes-issue-116657.polonius.stderr @@ -0,0 +1,36 @@ +error[E0046]: not all trait items implemented, missing: `call` + --> $DIR/location-insensitive-scopes-issue-116657.rs:18:1 + | +LL | fn call(x: Self) -> Self::Output; + | --------------------------------- `call` from trait +... +LL | impl<T: PlusOne> Callable for T { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `call` in implementation + +error: unconstrained opaque type + --> $DIR/location-insensitive-scopes-issue-116657.rs:22:19 + | +LL | type Output = impl PlusOne; + | ^^^^^^^^^^^^ + | + = note: `Output` must be used in combination with a concrete type within the same impl + +error[E0700]: hidden type for `impl PlusOne` captures lifetime that does not appear in bounds + --> $DIR/location-insensitive-scopes-issue-116657.rs:28:5 + | +LL | fn test<'a>(y: &'a mut i32) -> impl PlusOne { + | -- ------------ opaque type defined here + | | + | hidden type `<&'a mut i32 as Callable>::Output` captures the lifetime `'a` as defined here +LL | <&mut i32 as Callable>::call(y) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: to declare that `impl PlusOne` captures `'a`, you can add an explicit `'a` lifetime bound + | +LL | fn test<'a>(y: &'a mut i32) -> impl PlusOne + 'a { + | ++++ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0046, E0700. +For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/nll/polonius/location-insensitive-scopes-issue-116657.rs b/tests/ui/nll/polonius/location-insensitive-scopes-issue-116657.rs new file mode 100644 index 00000000000..ec17e0b093b --- /dev/null +++ b/tests/ui/nll/polonius/location-insensitive-scopes-issue-116657.rs @@ -0,0 +1,33 @@ +// This is a non-regression test for issue #116657, where NLL and `-Zpolonius=next` computed +// different loan scopes when a member constraint was not ultimately applied. + +// revisions: nll polonius +// [polonius] compile-flags: -Zpolonius=next + +#![feature(impl_trait_in_assoc_type)] + +trait Callable { + type Output; + fn call(x: Self) -> Self::Output; +} + +trait PlusOne {} + +impl<'a> PlusOne for &'a mut i32 {} + +impl<T: PlusOne> Callable for T { + //[nll]~^ ERROR not all trait items implemented + //[polonius]~^^ ERROR not all trait items implemented + + type Output = impl PlusOne; + //[nll]~^ ERROR unconstrained opaque type + //[polonius]~^^ ERROR unconstrained opaque type +} + +fn test<'a>(y: &'a mut i32) -> impl PlusOne { + <&mut i32 as Callable>::call(y) + //[nll]~^ ERROR hidden type for `impl PlusOne` captures lifetime + //[polonius]~^^ ERROR hidden type for `impl PlusOne` captures lifetime +} + +fn main() {} diff --git a/tests/ui/parser/recover-missing-semi-before-item.fixed b/tests/ui/parser/recover-missing-semi-before-item.fixed index 0be17e69e8f..acb846373cb 100644 --- a/tests/ui/parser/recover-missing-semi-before-item.fixed +++ b/tests/ui/parser/recover-missing-semi-before-item.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused_variables, dead_code)] +#![allow(unused_variables, dead_code, unused_imports)] fn for_struct() { let foo = 3; //~ ERROR expected `;`, found keyword `struct` diff --git a/tests/ui/parser/recover-missing-semi-before-item.rs b/tests/ui/parser/recover-missing-semi-before-item.rs index 867b7b749bb..ef6cfe3c4ed 100644 --- a/tests/ui/parser/recover-missing-semi-before-item.rs +++ b/tests/ui/parser/recover-missing-semi-before-item.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused_variables, dead_code)] +#![allow(unused_variables, dead_code, unused_imports)] fn for_struct() { let foo = 3 //~ ERROR expected `;`, found keyword `struct` diff --git a/tests/ui/parser/variadic-ffi-nested-syntactic-fail.rs b/tests/ui/parser/variadic-ffi-nested-syntactic-fail.rs index 9eeee195e56..f1238ec240f 100644 --- a/tests/ui/parser/variadic-ffi-nested-syntactic-fail.rs +++ b/tests/ui/parser/variadic-ffi-nested-syntactic-fail.rs @@ -5,5 +5,9 @@ fn f2<'a>(x: u8, y: Vec<&'a ...>) {} //~^ ERROR C-variadic type `...` may not be nested inside another type fn main() { - let _recovery_witness: () = 0; //~ ERROR mismatched types + // While this is an error, wf-checks happen before typeck, and if any wf-checks + // encountered errors, we do not continue to typeck, even if the items are + // unrelated. + // FIXME(oli-obk): make this report a type mismatch again. + let _recovery_witness: () = 0; } diff --git a/tests/ui/parser/variadic-ffi-nested-syntactic-fail.stderr b/tests/ui/parser/variadic-ffi-nested-syntactic-fail.stderr index 8b9d676a45d..7ca6a6d1bbf 100644 --- a/tests/ui/parser/variadic-ffi-nested-syntactic-fail.stderr +++ b/tests/ui/parser/variadic-ffi-nested-syntactic-fail.stderr @@ -10,15 +10,6 @@ error[E0743]: C-variadic type `...` may not be nested inside another type LL | fn f2<'a>(x: u8, y: Vec<&'a ...>) {} | ^^^ -error[E0308]: mismatched types - --> $DIR/variadic-ffi-nested-syntactic-fail.rs:8:33 - | -LL | let _recovery_witness: () = 0; - | -- ^ expected `()`, found integer - | | - | expected due to this - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0308, E0743. -For more information about an error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0743`. diff --git a/tests/ui/privacy/issue-46209-private-enum-variant-reexport.rs b/tests/ui/privacy/issue-46209-private-enum-variant-reexport.rs index 6f115e78e14..653dcab5771 100644 --- a/tests/ui/privacy/issue-46209-private-enum-variant-reexport.rs +++ b/tests/ui/privacy/issue-46209-private-enum-variant-reexport.rs @@ -2,13 +2,17 @@ mod rank { pub use self::Professor::*; //~^ ERROR glob import doesn't reexport anything + //~| ERROR unused import: `self::Professor::*` pub use self::Lieutenant::{JuniorGrade, Full}; //~^ ERROR `JuniorGrade` is private, and cannot be re-exported //~| ERROR `Full` is private, and cannot be re-exported + //~| ERROR unused imports: `Full`, `JuniorGrade` pub use self::PettyOfficer::*; //~^ ERROR glob import doesn't reexport anything + //~| ERROR unused import: `self::PettyOfficer::*` pub use self::Crewman::*; //~^ ERROR glob import doesn't reexport anything + //~| ERROR unused import: `self::Crewman::*` enum Professor { Adjunct, diff --git a/tests/ui/privacy/issue-46209-private-enum-variant-reexport.stderr b/tests/ui/privacy/issue-46209-private-enum-variant-reexport.stderr index 59b181fab40..df5968ba323 100644 --- a/tests/ui/privacy/issue-46209-private-enum-variant-reexport.stderr +++ b/tests/ui/privacy/issue-46209-private-enum-variant-reexport.stderr @@ -1,23 +1,23 @@ error[E0364]: `JuniorGrade` is private, and cannot be re-exported - --> $DIR/issue-46209-private-enum-variant-reexport.rs:5:32 + --> $DIR/issue-46209-private-enum-variant-reexport.rs:6:32 | LL | pub use self::Lieutenant::{JuniorGrade, Full}; | ^^^^^^^^^^^ | note: consider marking `JuniorGrade` as `pub` in the imported module - --> $DIR/issue-46209-private-enum-variant-reexport.rs:5:32 + --> $DIR/issue-46209-private-enum-variant-reexport.rs:6:32 | LL | pub use self::Lieutenant::{JuniorGrade, Full}; | ^^^^^^^^^^^ error[E0364]: `Full` is private, and cannot be re-exported - --> $DIR/issue-46209-private-enum-variant-reexport.rs:5:45 + --> $DIR/issue-46209-private-enum-variant-reexport.rs:6:45 | LL | pub use self::Lieutenant::{JuniorGrade, Full}; | ^^^^ | note: consider marking `Full` as `pub` in the imported module - --> $DIR/issue-46209-private-enum-variant-reexport.rs:5:45 + --> $DIR/issue-46209-private-enum-variant-reexport.rs:6:45 | LL | pub use self::Lieutenant::{JuniorGrade, Full}; | ^^^^ @@ -34,18 +34,42 @@ note: the lint level is defined here LL | #[deny(unused_imports)] | ^^^^^^^^^^^^^^ +error: unused import: `self::Professor::*` + --> $DIR/issue-46209-private-enum-variant-reexport.rs:3:13 + | +LL | pub use self::Professor::*; + | ^^^^^^^^^^^^^^^^^^ + +error: unused imports: `Full`, `JuniorGrade` + --> $DIR/issue-46209-private-enum-variant-reexport.rs:6:32 + | +LL | pub use self::Lieutenant::{JuniorGrade, Full}; + | ^^^^^^^^^^^ ^^^^ + error: glob import doesn't reexport anything because no candidate is public enough - --> $DIR/issue-46209-private-enum-variant-reexport.rs:8:13 + --> $DIR/issue-46209-private-enum-variant-reexport.rs:10:13 | LL | pub use self::PettyOfficer::*; | ^^^^^^^^^^^^^^^^^^^^^ -error: glob import doesn't reexport anything because no candidate is public enough +error: unused import: `self::PettyOfficer::*` --> $DIR/issue-46209-private-enum-variant-reexport.rs:10:13 | +LL | pub use self::PettyOfficer::*; + | ^^^^^^^^^^^^^^^^^^^^^ + +error: glob import doesn't reexport anything because no candidate is public enough + --> $DIR/issue-46209-private-enum-variant-reexport.rs:13:13 + | +LL | pub use self::Crewman::*; + | ^^^^^^^^^^^^^^^^ + +error: unused import: `self::Crewman::*` + --> $DIR/issue-46209-private-enum-variant-reexport.rs:13:13 + | LL | pub use self::Crewman::*; | ^^^^^^^^^^^^^^^^ -error: aborting due to 5 previous errors +error: aborting due to 9 previous errors For more information about this error, try `rustc --explain E0364`. diff --git a/tests/ui/privacy/private-variant-reexport.rs b/tests/ui/privacy/private-variant-reexport.rs index 68828446022..b59243af620 100644 --- a/tests/ui/privacy/private-variant-reexport.rs +++ b/tests/ui/privacy/private-variant-reexport.rs @@ -12,7 +12,9 @@ mod m3 { #[deny(unused_imports)] mod m4 { - pub use ::E::*; //~ ERROR glob import doesn't reexport anything + pub use ::E::*; + //~^ ERROR glob import doesn't reexport anything + //~| ERROR unused import: `::E::*` } enum E { V } diff --git a/tests/ui/privacy/private-variant-reexport.stderr b/tests/ui/privacy/private-variant-reexport.stderr index 78771ee30d3..2f041934a81 100644 --- a/tests/ui/privacy/private-variant-reexport.stderr +++ b/tests/ui/privacy/private-variant-reexport.stderr @@ -42,7 +42,13 @@ note: the lint level is defined here LL | #[deny(unused_imports)] | ^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: unused import: `::E::*` + --> $DIR/private-variant-reexport.rs:15:13 + | +LL | pub use ::E::*; + | ^^^^^^ + +error: aborting due to 5 previous errors Some errors have detailed explanations: E0364, E0365. For more information about an error, try `rustc --explain E0364`. diff --git a/tests/ui/self/self_type_keyword.rs b/tests/ui/self/self_type_keyword.rs index b42bf8eea1a..96d24715edb 100644 --- a/tests/ui/self/self_type_keyword.rs +++ b/tests/ui/self/self_type_keyword.rs @@ -22,6 +22,8 @@ pub fn main() { //~^ ERROR cannot find macro `Self` in this scope Foo { Self } => (), //~^ ERROR expected identifier, found keyword `Self` + //~| ERROR mismatched types + //~| ERROR `Foo` does not have a field named `Self` } } diff --git a/tests/ui/self/self_type_keyword.stderr b/tests/ui/self/self_type_keyword.stderr index aca08d81163..6e65fae808d 100644 --- a/tests/ui/self/self_type_keyword.stderr +++ b/tests/ui/self/self_type_keyword.stderr @@ -31,19 +31,19 @@ LL | Foo { Self } => (), | ^^^^ expected identifier, found keyword error: expected identifier, found keyword `Self` - --> $DIR/self_type_keyword.rs:29:26 + --> $DIR/self_type_keyword.rs:31:26 | LL | extern crate core as Self; | ^^^^ expected identifier, found keyword error: expected identifier, found keyword `Self` - --> $DIR/self_type_keyword.rs:34:32 + --> $DIR/self_type_keyword.rs:36:32 | LL | use std::option::Option as Self; | ^^^^ expected identifier, found keyword error: expected identifier, found keyword `Self` - --> $DIR/self_type_keyword.rs:39:11 + --> $DIR/self_type_keyword.rs:41:11 | LL | trait Self {} | ^^^^ expected identifier, found keyword @@ -80,7 +80,22 @@ LL | struct Bar<'Self>; | = help: consider removing `'Self`, referring to it in a field, or using a marker such as `PhantomData` -error: aborting due to 12 previous errors +error[E0308]: mismatched types + --> $DIR/self_type_keyword.rs:23:9 + | +LL | match 15 { + | -- this expression has type `{integer}` +... +LL | Foo { Self } => (), + | ^^^^^^^^^^^^ expected integer, found `Foo` + +error[E0026]: struct `Foo` does not have a field named `Self` + --> $DIR/self_type_keyword.rs:23:15 + | +LL | Foo { Self } => (), + | ^^^^ struct `Foo` does not have this field + +error: aborting due to 14 previous errors -Some errors have detailed explanations: E0392, E0531. -For more information about an error, try `rustc --explain E0392`. +Some errors have detailed explanations: E0026, E0308, E0392, E0531. +For more information about an error, try `rustc --explain E0026`. diff --git a/tests/ui/suggestions/dont-suggest-ref/simple.stderr b/tests/ui/suggestions/dont-suggest-ref/simple.stderr index 52632652423..7d902dbccc4 100644 --- a/tests/ui/suggestions/dont-suggest-ref/simple.stderr +++ b/tests/ui/suggestions/dont-suggest-ref/simple.stderr @@ -43,7 +43,7 @@ LL - while let Either::One(_t) = *r { } LL + while let Either::One(_t) = r { } | -error[E0507]: cannot move out of `r` as enum variant `Two` which is behind a shared reference +error[E0507]: cannot move out of `r` as enum variant `One` which is behind a shared reference --> $DIR/simple.rs:47:11 | LL | match *r { @@ -124,7 +124,7 @@ LL - while let Either::One(_t) = *rm { } LL + while let Either::One(_t) = rm { } | -error[E0507]: cannot move out of `rm` as enum variant `Two` which is behind a mutable reference +error[E0507]: cannot move out of `rm` as enum variant `One` which is behind a mutable reference --> $DIR/simple.rs:70:11 | LL | match *rm { @@ -392,7 +392,7 @@ LL - while let &Either::One(_t) = r { } LL + while let Either::One(_t) = r { } | -error[E0507]: cannot move out of `r` as enum variant `Two` which is behind a shared reference +error[E0507]: cannot move out of `r` as enum variant `One` which is behind a shared reference --> $DIR/simple.rs:155:11 | LL | match r { @@ -491,7 +491,7 @@ LL - while let &mut Either::One(_t) = rm { } LL + while let Either::One(_t) = rm { } | -error[E0507]: cannot move out of `rm` as enum variant `Two` which is behind a mutable reference +error[E0507]: cannot move out of `rm` as enum variant `One` which is behind a mutable reference --> $DIR/simple.rs:187:11 | LL | match rm { diff --git a/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.rs b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.rs index a1b9a7eba4d..193544ebd3f 100644 --- a/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.rs +++ b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.rs @@ -5,7 +5,7 @@ trait Trait { - fn func<const N: u32>() -> [ (); N ]; + fn func<const N: u32>() -> [ (); N ]; //~ ERROR mismatched types } struct S {} diff --git a/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.stderr b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.stderr index 9843651b1e6..16aaf0615ed 100644 --- a/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.stderr +++ b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.stderr @@ -4,6 +4,12 @@ error[E0308]: mismatched types LL | fn func<const N: u32>() -> [ (); { () }] { | ^^ expected `usize`, found `()` -error: aborting due to previous error +error[E0308]: mismatched types + --> $DIR/const-in-impl-fn-return-type.rs:8:38 + | +LL | fn func<const N: u32>() -> [ (); N ]; + | ^ expected `usize`, found `u32` + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/triagebot.toml b/triagebot.toml index e3c4233c843..dc378d52e92 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -554,13 +554,9 @@ cc = ["@davidtwco", "@compiler-errors", "@JohnTitor", "@TaKO8Ki"] message = "`rustc_macros::diagnostics` was changed" cc = ["@davidtwco", "@compiler-errors", "@JohnTitor", "@TaKO8Ki"] -[mentions."compiler/rustc_smir"] -message = "This PR changes Stable MIR" -cc = ["@oli-obk", "@celinval", "@spastorino", "@ouz-a"] - [mentions."compiler/stable_mir"] message = "This PR changes Stable MIR" -cc = ["@oli-obk", "@celinval", "@spastorino"] +cc = ["@oli-obk", "@celinval", "@spastorino", "@ouz-a"] [mentions."compiler/rustc_target/src/spec"] message = """ diff --git a/x.ps1 b/x.ps1 index 55f99901645..eae1a2cb399 100755 --- a/x.ps1 +++ b/x.ps1 @@ -17,11 +17,6 @@ foreach ($arg in $args) { function Get-Application($app) { $cmd = Get-Command $app -ErrorAction SilentlyContinue -CommandType Application | Select-Object -First 1 - if ($cmd.source -match '.*AppData\\Local\\Microsoft\\WindowsApps\\.*exe') { - # Windows for some reason puts a `python3.exe` executable in PATH that just opens the windows store. - # Ignore it. - return $false - } return $cmd } |
